diff --git a/pkg/kubectl/cmd/testing/BUILD b/pkg/kubectl/cmd/testing/BUILD index 62f5708a2d0..e6b0ab3045c 100644 --- a/pkg/kubectl/cmd/testing/BUILD +++ b/pkg/kubectl/cmd/testing/BUILD @@ -35,6 +35,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library", + "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library", ], diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 23c27a45653..8413823798b 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -34,6 +34,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" @@ -307,6 +308,10 @@ func (f *FakeFactory) RESTClient() (*restclient.RESTClient, error) { return nil, nil } +func (f *FakeFactory) KubernetesClientSet() (*kubernetes.Clientset, error) { + return nil, nil +} + func (f *FakeFactory) ClientSet() (internalclientset.Interface, error) { return nil, nil } @@ -582,6 +587,33 @@ func (f *fakeAPIFactory) JSONEncoder() runtime.Encoder { return testapi.Default.Codec() } +func (f *fakeAPIFactory) KubernetesClientSet() (*kubernetes.Clientset, error) { + fakeClient := f.tf.Client.(*fake.RESTClient) + clientset := kubernetes.NewForConfigOrDie(f.tf.ClientConfig) + + clientset.CoreV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AutoscalingV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AutoscalingV2alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.BatchV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.BatchV2alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.CertificatesV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.ExtensionsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.RbacV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.RbacV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.StorageV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.StorageV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AppsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.AppsV1beta2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.PolicyV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + clientset.DiscoveryClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client + + return clientset, f.tf.Err +} + func (f *fakeAPIFactory) ClientSet() (internalclientset.Interface, error) { // Swap the HTTP client out of the REST client with the fake // version. diff --git a/pkg/kubectl/cmd/util/BUILD b/pkg/kubectl/cmd/util/BUILD index 99473b51dc0..68eee0a23bc 100644 --- a/pkg/kubectl/cmd/util/BUILD +++ b/pkg/kubectl/cmd/util/BUILD @@ -66,6 +66,7 @@ go_library( "//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", "//vendor/k8s.io/client-go/util/homedir:go_default_library", diff --git a/pkg/kubectl/cmd/util/clientcache.go b/pkg/kubectl/cmd/util/clientcache.go index da7c45cc6e7..9de532ecc98 100644 --- a/pkg/kubectl/cmd/util/clientcache.go +++ b/pkg/kubectl/cmd/util/clientcache.go @@ -21,6 +21,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" @@ -58,6 +59,34 @@ type ClientCache struct { // argument evaluation discoveryClientFactory DiscoveryClientFactory discoveryClient discovery.DiscoveryInterface + + kubernetesClientLoader KubernetesClientCache +} + +// kubernetesClientLoader provides a new kubernetes.Clientset +type KubernetesClientCache struct { + // once makes sure the client is only initialized once + once sync.Once + // client is the cached client value + client *kubernetes.Clientset + // err is the cached error value + err error +} + +// KubernetesClientSet returns a new kubernetes.Clientset. It will cache the value +// the first time it is called and return the cached value on subsequent calls. +// If an error is encountered the first time KubernetesClientSet is called, +// the error will be cached. +func (c *ClientCache) KubernetesClientSet(requiredVersion *schema.GroupVersion) (*kubernetes.Clientset, error) { + c.kubernetesClientLoader.once.Do(func() { + config, err := c.ClientConfigForVersion(requiredVersion) + if err != nil { + c.kubernetesClientLoader.err = err + return + } + c.kubernetesClientLoader.client, c.kubernetesClientLoader.err = kubernetes.NewForConfig(config) + }) + return c.kubernetesClientLoader.client, c.kubernetesClientLoader.err } // also looks up the discovery client. We can't do this during init because the flags won't have been set diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 13419716ce5..40a879bd884 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -43,6 +43,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" @@ -91,6 +92,10 @@ type ClientAccessFactory interface { // ClientSet gives you back an internal, generated clientset ClientSet() (internalclientset.Interface, error) + + // KubernetesClientSet gives you back an external clientset + KubernetesClientSet() (*kubernetes.Clientset, error) + // Returns a RESTClient for accessing Kubernetes resources or an error. RESTClient() (*restclient.RESTClient, error) // Returns a client.Config for accessing the Kubernetes server. diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index 9aaa4680c81..2cf9c7a39f2 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -37,6 +37,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" utilflag "k8s.io/apiserver/pkg/util/flag" "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" @@ -167,6 +168,10 @@ func (f *ring0Factory) DiscoveryClient() (discovery.CachedDiscoveryInterface, er return f.discoveryFactory.DiscoveryClient() } +func (f *ring0Factory) KubernetesClientSet() (*kubernetes.Clientset, error) { + return f.clientCache.KubernetesClientSet(nil) +} + func (f *ring0Factory) ClientSet() (internalclientset.Interface, error) { return f.clientCache.ClientSetForVersion(nil) }