From 12ac36525a6f6c4467137c4f0e2247e37a9685fd Mon Sep 17 00:00:00 2001 From: "Steven E. Harris" Date: Tue, 30 May 2017 15:12:14 -0400 Subject: [PATCH] Use versioned API types from the client-go library Wherever possible, use the k8s.io/client-go/kubernetes.Interface type in favor of the client-related types from package k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset The latter are still required by the kubectl "reaper" types used in the "installer" and "kube" packages. --- cmd/helm/helm.go | 34 ++++++- cmd/helm/init.go | 4 +- cmd/helm/init_test.go | 20 ++-- cmd/helm/installer/install.go | 83 ++++++++------- cmd/helm/installer/install_test.go | 100 +++++++++---------- cmd/helm/installer/options.go | 8 +- cmd/helm/installer/uninstall.go | 6 +- cmd/helm/reset.go | 2 +- pkg/helm/portforwarder/pod.go | 60 +++++++++++ pkg/helm/portforwarder/portforwarder.go | 16 +-- pkg/helm/portforwarder/portforwarder_test.go | 36 +++---- 11 files changed, 226 insertions(+), 143 deletions(-) create mode 100644 pkg/helm/portforwarder/pod.go diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 48f87d5e5..ff5d3f130 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -28,6 +28,7 @@ import ( "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -225,16 +226,39 @@ func prettyError(err error) error { return errors.New(grpc.ErrorDesc(err)) } -// getKubeClient is a convenience method for creating kubernetes config and client -// for a given kubeconfig context -func getKubeClient(context string) (*rest.Config, *internalclientset.Clientset, error) { +// configForContext creates a Kubernetes REST client configuration for a given kubeconfig context. +func configForContext(context string) (*rest.Config, error) { config, err := kube.GetConfig(context).ClientConfig() if err != nil { - return nil, nil, fmt.Errorf("could not get kubernetes config for context '%s': %s", context, err) + return nil, fmt.Errorf("could not get Kubernetes config for context %q: %s", context, err) + } + return config, nil +} + +// getKubeClient creates a Kubernetes config and client for a given kubeconfig context. +func getKubeClient(context string) (*rest.Config, kubernetes.Interface, error) { + config, err := configForContext(context) + if err != nil { + return nil, nil, err + } + client, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err) + } + return config, client, nil +} + +// getInternalKubeClient creates a Kubernetes config and an "internal" client for a given kubeconfig context. +// +// Prefer the similar getKubeClient if you don't need to use such an internal client. +func getInternalKubeClient(context string) (*rest.Config, internalclientset.Interface, error) { + config, err := configForContext(context) + if err != nil { + return nil, nil, err } client, err := internalclientset.NewForConfig(config) if err != nil { - return nil, nil, fmt.Errorf("could not get kubernetes client: %s", err) + return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err) } return config, client, nil } diff --git a/cmd/helm/init.go b/cmd/helm/init.go index dc1298915..9f4237af2 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -24,7 +24,7 @@ import ( "github.com/spf13/cobra" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/client-go/kubernetes" "k8s.io/helm/cmd/helm/installer" "k8s.io/helm/pkg/getter" @@ -76,7 +76,7 @@ type initCmd struct { out io.Writer home helmpath.Home opts installer.Options - kubeClient internalclientset.Interface + kubeClient kubernetes.Interface serviceAccount string } diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 4d82c297b..2d6c07320 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -30,10 +30,10 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/pkg/apis/extensions/v1beta1" testcore "k8s.io/client-go/testing" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/helm/cmd/helm/installer" "k8s.io/helm/pkg/helm/helmpath" @@ -52,7 +52,7 @@ func TestInitCmd(t *testing.T) { out: &buf, home: helmpath.Home(home), kubeClient: fc, - namespace: api.NamespaceDefault, + namespace: v1.NamespaceDefault, } if err := cmd.run(); err != nil { t.Errorf("expected error: %v", err) @@ -81,20 +81,20 @@ func TestInitCmd_exists(t *testing.T) { defer os.Remove(home) var buf bytes.Buffer - fc := fake.NewSimpleClientset(&extensions.Deployment{ + fc := fake.NewSimpleClientset(&v1beta1.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Namespace: api.NamespaceDefault, + Namespace: v1.NamespaceDefault, Name: "tiller-deploy", }, }) fc.PrependReactor("*", "*", func(action testcore.Action) (bool, runtime.Object, error) { - return true, nil, apierrors.NewAlreadyExists(api.Resource("deployments"), "1") + return true, nil, apierrors.NewAlreadyExists(v1.Resource("deployments"), "1") }) cmd := &initCmd{ out: &buf, home: helmpath.Home(home), kubeClient: fc, - namespace: api.NamespaceDefault, + namespace: v1.NamespaceDefault, } if err := cmd.run(); err != nil { t.Errorf("expected error: %v", err) @@ -120,7 +120,7 @@ func TestInitCmd_clientOnly(t *testing.T) { home: helmpath.Home(home), kubeClient: fc, clientOnly: true, - namespace: api.NamespaceDefault, + namespace: v1.NamespaceDefault, } if err := cmd.run(); err != nil { t.Errorf("unexpected error: %v", err) @@ -155,7 +155,7 @@ func TestInitCmd_dryRun(t *testing.T) { kubeClient: fc, clientOnly: true, dryRun: true, - namespace: api.NamespaceDefault, + namespace: v1.NamespaceDefault, } if err := cmd.run(); err != nil { t.Fatal(err) diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go index a6d4626c8..0f858f951 100644 --- a/cmd/helm/installer/install.go +++ b/cmd/helm/installer/install.go @@ -23,17 +23,18 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" - extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" + "k8s.io/client-go/kubernetes" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/pkg/apis/extensions/v1beta1" + "k8s.io/client-go/pkg/util" ) // Install uses kubernetes client to install tiller. // // Returns an error if the command failed. -func Install(client internalclientset.Interface, opts *Options) error { +func Install(client kubernetes.Interface, opts *Options) error { if err := createDeployment(client.Extensions(), opts); err != nil { return err } @@ -51,7 +52,7 @@ func Install(client internalclientset.Interface, opts *Options) error { // Upgrade uses kubernetes client to upgrade tiller to current version. // // Returns an error if the command failed. -func Upgrade(client internalclientset.Interface, opts *Options) error { +func Upgrade(client kubernetes.Interface, opts *Options) error { obj, err := client.Extensions().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{}) if err != nil { return err @@ -79,19 +80,19 @@ func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) } // deployment gets the deployment object that installs Tiller. -func deployment(opts *Options) *extensions.Deployment { +func deployment(opts *Options) *v1beta1.Deployment { return generateDeployment(opts) } // createService creates the Tiller service resource -func createService(client internalversion.ServicesGetter, namespace string) error { +func createService(client corev1.ServicesGetter, namespace string) error { obj := service(namespace) _, err := client.Services(obj.Namespace).Create(obj) return err } // service gets the service object that installs Tiller. -func service(namespace string) *api.Service { +func service(namespace string) *v1.Service { return generateService(namespace) } @@ -117,36 +118,36 @@ func generateLabels(labels map[string]string) map[string]string { return labels } -func generateDeployment(opts *Options) *extensions.Deployment { +func generateDeployment(opts *Options) *v1beta1.Deployment { labels := generateLabels(map[string]string{"name": "tiller"}) - d := &extensions.Deployment{ + d := &v1beta1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Namespace: opts.Namespace, Name: deploymentName, Labels: labels, }, - Spec: extensions.DeploymentSpec{ - Replicas: 1, - Template: api.PodTemplateSpec{ + Spec: v1beta1.DeploymentSpec{ + Replicas: util.Int32Ptr(1), + Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, - Spec: api.PodSpec{ + Spec: v1.PodSpec{ ServiceAccountName: opts.ServiceAccount, - Containers: []api.Container{ + Containers: []v1.Container{ { Name: "tiller", Image: opts.selectImage(), ImagePullPolicy: opts.pullPolicy(), - Ports: []api.ContainerPort{ + Ports: []v1.ContainerPort{ {ContainerPort: 44134, Name: "tiller"}, }, - Env: []api.EnvVar{ + Env: []v1.EnvVar{ {Name: "TILLER_NAMESPACE", Value: opts.Namespace}, }, - LivenessProbe: &api.Probe{ - Handler: api.Handler{ - HTTPGet: &api.HTTPGetAction{ + LivenessProbe: &v1.Probe{ + Handler: v1.Handler{ + HTTPGet: &v1.HTTPGetAction{ Path: "/liveness", Port: intstr.FromInt(44135), }, @@ -154,9 +155,9 @@ func generateDeployment(opts *Options) *extensions.Deployment { InitialDelaySeconds: 1, TimeoutSeconds: 1, }, - ReadinessProbe: &api.Probe{ - Handler: api.Handler{ - HTTPGet: &api.HTTPGetAction{ + ReadinessProbe: &v1.Probe{ + Handler: v1.Handler{ + HTTPGet: &v1.HTTPGetAction{ Path: "/readiness", Port: intstr.FromInt(44135), }, @@ -166,9 +167,7 @@ func generateDeployment(opts *Options) *extensions.Deployment { }, }, }, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: opts.EnableHostNetwork, - }, + HostNetwork: opts.EnableHostNetwork, }, }, }, @@ -183,22 +182,22 @@ func generateDeployment(opts *Options) *extensions.Deployment { } // Mount secret to "/etc/certs" - d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, api.VolumeMount{ + d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{ Name: "tiller-certs", ReadOnly: true, MountPath: certsDir, }) // Add environment variable required for enabling TLS - d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []api.EnvVar{ + d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []v1.EnvVar{ {Name: "TILLER_TLS_VERIFY", Value: tlsVerify}, {Name: "TILLER_TLS_ENABLE", Value: tlsEnable}, {Name: "TILLER_TLS_CERTS", Value: certsDir}, }...) // Add secret volume to deployment - d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, api.Volume{ + d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v1.Volume{ Name: "tiller-certs", - VolumeSource: api.VolumeSource{ - Secret: &api.SecretVolumeSource{ + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ SecretName: "tiller-secret", }, }, @@ -207,17 +206,17 @@ func generateDeployment(opts *Options) *extensions.Deployment { return d } -func generateService(namespace string) *api.Service { +func generateService(namespace string) *v1.Service { labels := generateLabels(map[string]string{"name": "tiller"}) - s := &api.Service{ + s := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: serviceName, Labels: labels, }, - Spec: api.ServiceSpec{ - Type: api.ServiceTypeClusterIP, - Ports: []api.ServicePort{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + Ports: []v1.ServicePort{ { Name: "tiller", Port: 44134, @@ -241,7 +240,7 @@ func SecretManifest(opts *Options) (string, error) { } // createSecret creates the Tiller secret resource. -func createSecret(client internalversion.SecretsGetter, opts *Options) error { +func createSecret(client corev1.SecretsGetter, opts *Options) error { o, err := generateSecret(opts) if err != nil { return err @@ -251,12 +250,12 @@ func createSecret(client internalversion.SecretsGetter, opts *Options) error { } // generateSecret builds the secret object that hold Tiller secrets. -func generateSecret(opts *Options) (*api.Secret, error) { +func generateSecret(opts *Options) (*v1.Secret, error) { const secretName = "tiller-secret" labels := generateLabels(map[string]string{"name": "tiller"}) - secret := &api.Secret{ - Type: api.SecretTypeOpaque, + secret := &v1.Secret{ + Type: v1.SecretTypeOpaque, Data: make(map[string][]byte), ObjectMeta: metav1.ObjectMeta{ Name: secretName, diff --git a/cmd/helm/installer/install_test.go b/cmd/helm/installer/install_test.go index 22af4d61e..e1e94d7e5 100644 --- a/cmd/helm/installer/install_test.go +++ b/cmd/helm/installer/install_test.go @@ -25,10 +25,10 @@ import ( "github.com/ghodss/yaml" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/pkg/apis/extensions/v1beta1" testcore "k8s.io/client-go/testing" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/helm/pkg/version" ) @@ -39,7 +39,7 @@ func TestDeploymentManifest(t *testing.T) { image string canary bool expect string - imagePullPolicy api.PullPolicy + imagePullPolicy v1.PullPolicy }{ {"default", "", false, "gcr.io/kubernetes-helm/tiller:" + version.Version, "IfNotPresent"}, {"canary", "example.com/tiller", true, "gcr.io/kubernetes-helm/tiller:canary", "Always"}, @@ -47,11 +47,11 @@ func TestDeploymentManifest(t *testing.T) { } for _, tt := range tests { - o, err := DeploymentManifest(&Options{Namespace: api.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary}) + o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary}) if err != nil { t.Fatalf("%s: error %q", tt.name, err) } - var dep extensions.Deployment + var dep v1beta1.Deployment if err := yaml.Unmarshal([]byte(o), &dep); err != nil { t.Fatalf("%s: error %q", tt.name, err) } @@ -64,8 +64,8 @@ func TestDeploymentManifest(t *testing.T) { t.Errorf("%s: expected imagePullPolicy %q, got %q", tt.name, tt.imagePullPolicy, got) } - if got := dep.Spec.Template.Spec.Containers[0].Env[0].Value; got != api.NamespaceDefault { - t.Errorf("%s: expected namespace %q, got %q", tt.name, api.NamespaceDefault, got) + if got := dep.Spec.Template.Spec.Containers[0].Env[0].Value; got != v1.NamespaceDefault { + t.Errorf("%s: expected namespace %q, got %q", tt.name, v1.NamespaceDefault, got) } } } @@ -76,19 +76,19 @@ func TestDeploymentManifestForServiceAccount(t *testing.T) { image string canary bool expect string - imagePullPolicy api.PullPolicy + imagePullPolicy v1.PullPolicy serviceAccount string }{ {"withSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", "service-account"}, {"withoutSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", ""}, } for _, tt := range tests { - o, err := DeploymentManifest(&Options{Namespace: api.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary, ServiceAccount: tt.serviceAccount}) + o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary, ServiceAccount: tt.serviceAccount}) if err != nil { t.Fatalf("%s: error %q", tt.name, err) } - var d extensions.Deployment + var d v1beta1.Deployment if err := yaml.Unmarshal([]byte(o), &d); err != nil { t.Fatalf("%s: error %q", tt.name, err) } @@ -106,19 +106,19 @@ func TestDeploymentManifest_WithTLS(t *testing.T) { verify string }{ { - Options{Namespace: api.NamespaceDefault, EnableTLS: true, VerifyTLS: true}, + Options{Namespace: v1.NamespaceDefault, EnableTLS: true, VerifyTLS: true}, "tls enable (true), tls verify (true)", "1", "1", }, { - Options{Namespace: api.NamespaceDefault, EnableTLS: true, VerifyTLS: false}, + Options{Namespace: v1.NamespaceDefault, EnableTLS: true, VerifyTLS: false}, "tls enable (true), tls verify (false)", "1", "", }, { - Options{Namespace: api.NamespaceDefault, EnableTLS: false, VerifyTLS: true}, + Options{Namespace: v1.NamespaceDefault, EnableTLS: false, VerifyTLS: true}, "tls enable (false), tls verify (true)", "1", "1", @@ -130,7 +130,7 @@ func TestDeploymentManifest_WithTLS(t *testing.T) { t.Fatalf("%s: error %q", tt.name, err) } - var d extensions.Deployment + var d v1beta1.Deployment if err := yaml.Unmarshal([]byte(o), &d); err != nil { t.Fatalf("%s: error %q", tt.name, err) } @@ -145,17 +145,17 @@ func TestDeploymentManifest_WithTLS(t *testing.T) { } func TestServiceManifest(t *testing.T) { - o, err := ServiceManifest(api.NamespaceDefault) + o, err := ServiceManifest(v1.NamespaceDefault) if err != nil { t.Fatalf("error %q", err) } - var svc api.Service + var svc v1.Service if err := yaml.Unmarshal([]byte(o), &svc); err != nil { t.Fatalf("error %q", err) } - if got := svc.ObjectMeta.Namespace; got != api.NamespaceDefault { - t.Errorf("expected namespace %s, got %s", api.NamespaceDefault, got) + if got := svc.ObjectMeta.Namespace; got != v1.NamespaceDefault { + t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got) } } @@ -163,7 +163,7 @@ func TestSecretManifest(t *testing.T) { o, err := SecretManifest(&Options{ VerifyTLS: true, EnableTLS: true, - Namespace: api.NamespaceDefault, + Namespace: v1.NamespaceDefault, TLSKeyFile: tlsTestFile(t, "key.pem"), TLSCertFile: tlsTestFile(t, "crt.pem"), TLSCaCertFile: tlsTestFile(t, "ca.pem"), @@ -173,13 +173,13 @@ func TestSecretManifest(t *testing.T) { t.Fatalf("error %q", err) } - var obj api.Secret + var obj v1.Secret if err := yaml.Unmarshal([]byte(o), &obj); err != nil { t.Fatalf("error %q", err) } - if got := obj.ObjectMeta.Namespace; got != api.NamespaceDefault { - t.Errorf("expected namespace %s, got %s", api.NamespaceDefault, got) + if got := obj.ObjectMeta.Namespace; got != v1.NamespaceDefault { + t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got) } if _, ok := obj.Data["tls.key"]; !ok { t.Errorf("missing 'tls.key' in generated secret object") @@ -197,7 +197,7 @@ func TestInstall(t *testing.T) { fc := &fake.Clientset{} fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*extensions.Deployment) + obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) l := obj.GetLabels() if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { t.Errorf("expected labels = '', got '%s'", l) @@ -209,19 +209,19 @@ func TestInstall(t *testing.T) { return true, obj, nil }) fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*api.Service) + obj := action.(testcore.CreateAction).GetObject().(*v1.Service) l := obj.GetLabels() if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { t.Errorf("expected labels = '', got '%s'", l) } n := obj.ObjectMeta.Namespace - if n != api.NamespaceDefault { - t.Errorf("expected namespace = '%s', got '%s'", api.NamespaceDefault, n) + if n != v1.NamespaceDefault { + t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) } return true, obj, nil }) - opts := &Options{Namespace: api.NamespaceDefault, ImageSpec: image} + opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image} if err := Install(fc, opts); err != nil { t.Errorf("unexpected error: %#+v", err) } @@ -237,7 +237,7 @@ func TestInstall_WithTLS(t *testing.T) { fc := &fake.Clientset{} fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*extensions.Deployment) + obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) l := obj.GetLabels() if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { t.Errorf("expected labels = '', got '%s'", l) @@ -249,24 +249,24 @@ func TestInstall_WithTLS(t *testing.T) { return true, obj, nil }) fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*api.Service) + obj := action.(testcore.CreateAction).GetObject().(*v1.Service) l := obj.GetLabels() if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { t.Errorf("expected labels = '', got '%s'", l) } n := obj.ObjectMeta.Namespace - if n != api.NamespaceDefault { - t.Errorf("expected namespace = '%s', got '%s'", api.NamespaceDefault, n) + if n != v1.NamespaceDefault { + t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) } return true, obj, nil }) fc.AddReactor("create", "secrets", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*api.Secret) + obj := action.(testcore.CreateAction).GetObject().(*v1.Secret) if l := obj.GetLabels(); reflect.DeepEqual(l, map[string]string{"app": "helm"}) { t.Errorf("expected labels = '', got '%s'", l) } - if n := obj.ObjectMeta.Namespace; n != api.NamespaceDefault { - t.Errorf("expected namespace = '%s', got '%s'", api.NamespaceDefault, n) + if n := obj.ObjectMeta.Namespace; n != v1.NamespaceDefault { + t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) } if s := obj.ObjectMeta.Name; s != name { t.Errorf("expected name = '%s', got '%s'", name, s) @@ -284,7 +284,7 @@ func TestInstall_WithTLS(t *testing.T) { }) opts := &Options{ - Namespace: api.NamespaceDefault, + Namespace: v1.NamespaceDefault, ImageSpec: image, EnableTLS: true, VerifyTLS: true, @@ -305,7 +305,7 @@ func TestInstall_WithTLS(t *testing.T) { func TestInstall_canary(t *testing.T) { fc := &fake.Clientset{} fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*extensions.Deployment) + obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) i := obj.Spec.Template.Spec.Containers[0].Image if i != "gcr.io/kubernetes-helm/tiller:canary" { t.Errorf("expected canary image, got '%s'", i) @@ -313,11 +313,11 @@ func TestInstall_canary(t *testing.T) { return true, obj, nil }) fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*api.Service) + obj := action.(testcore.CreateAction).GetObject().(*v1.Service) return true, obj, nil }) - opts := &Options{Namespace: api.NamespaceDefault, UseCanary: true} + opts := &Options{Namespace: v1.NamespaceDefault, UseCanary: true} if err := Install(fc, opts); err != nil { t.Errorf("unexpected error: %#+v", err) } @@ -331,19 +331,19 @@ func TestUpgrade(t *testing.T) { image := "gcr.io/kubernetes-helm/tiller:v2.0.0" serviceAccount := "newServiceAccount" existingDeployment := deployment(&Options{ - Namespace: api.NamespaceDefault, + Namespace: v1.NamespaceDefault, ImageSpec: "imageToReplace", ServiceAccount: "serviceAccountToReplace", UseCanary: false, }) - existingService := service(api.NamespaceDefault) + existingService := service(v1.NamespaceDefault) fc := &fake.Clientset{} fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { return true, existingDeployment, nil }) fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.UpdateAction).GetObject().(*extensions.Deployment) + obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) i := obj.Spec.Template.Spec.Containers[0].Image if i != image { t.Errorf("expected image = '%s', got '%s'", image, i) @@ -358,7 +358,7 @@ func TestUpgrade(t *testing.T) { return true, existingService, nil }) - opts := &Options{Namespace: api.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount} + opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount} if err := Upgrade(fc, opts); err != nil { t.Errorf("unexpected error: %#+v", err) } @@ -372,7 +372,7 @@ func TestUpgrade_serviceNotFound(t *testing.T) { image := "gcr.io/kubernetes-helm/tiller:v2.0.0" existingDeployment := deployment(&Options{ - Namespace: api.NamespaceDefault, + Namespace: v1.NamespaceDefault, ImageSpec: "imageToReplace", UseCanary: false, }) @@ -382,7 +382,7 @@ func TestUpgrade_serviceNotFound(t *testing.T) { return true, existingDeployment, nil }) fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.UpdateAction).GetObject().(*extensions.Deployment) + obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) i := obj.Spec.Template.Spec.Containers[0].Image if i != image { t.Errorf("expected image = '%s', got '%s'", image, i) @@ -390,18 +390,18 @@ func TestUpgrade_serviceNotFound(t *testing.T) { return true, obj, nil }) fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) { - return true, nil, apierrors.NewNotFound(api.Resource("services"), "1") + return true, nil, apierrors.NewNotFound(v1.Resource("services"), "1") }) fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) { - obj := action.(testcore.CreateAction).GetObject().(*api.Service) + obj := action.(testcore.CreateAction).GetObject().(*v1.Service) n := obj.ObjectMeta.Namespace - if n != api.NamespaceDefault { - t.Errorf("expected namespace = '%s', got '%s'", api.NamespaceDefault, n) + if n != v1.NamespaceDefault { + t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n) } return true, obj, nil }) - opts := &Options{Namespace: api.NamespaceDefault, ImageSpec: image} + opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image} if err := Upgrade(fc, opts); err != nil { t.Errorf("unexpected error: %#+v", err) } diff --git a/cmd/helm/installer/options.go b/cmd/helm/installer/options.go index 6fb804a46..a76bb5d6f 100644 --- a/cmd/helm/installer/options.go +++ b/cmd/helm/installer/options.go @@ -19,8 +19,8 @@ package installer // import "k8s.io/helm/cmd/helm/installer" import ( "fmt" + "k8s.io/client-go/pkg/api/v1" "k8s.io/helm/pkg/version" - "k8s.io/kubernetes/pkg/api" ) const defaultImage = "gcr.io/kubernetes-helm/tiller" @@ -84,11 +84,11 @@ func (opts *Options) selectImage() string { } } -func (opts *Options) pullPolicy() api.PullPolicy { +func (opts *Options) pullPolicy() v1.PullPolicy { if opts.UseCanary { - return api.PullAlways + return v1.PullAlways } - return api.PullIfNotPresent + return v1.PullIfNotPresent } func (opts *Options) tls() bool { return opts.EnableTLS || opts.VerifyTLS } diff --git a/cmd/helm/installer/uninstall.go b/cmd/helm/installer/uninstall.go index 1c3e37a30..8c6c46bb4 100644 --- a/cmd/helm/installer/uninstall.go +++ b/cmd/helm/installer/uninstall.go @@ -19,9 +19,9 @@ package installer // import "k8s.io/helm/cmd/helm/installer" import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/client-go/pkg/apis/extensions" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/kubernetes/pkg/kubectl" ) @@ -39,7 +39,7 @@ func Uninstall(client internalclientset.Interface, opts *Options) error { } // deleteService deletes the Tiller Service resource -func deleteService(client internalversion.ServicesGetter, namespace string) error { +func deleteService(client coreclient.ServicesGetter, namespace string) error { err := client.Services(namespace).Delete(serviceName, &metav1.DeleteOptions{}) return ingoreNotFound(err) } diff --git a/cmd/helm/reset.go b/cmd/helm/reset.go index 56a216735..654765cd3 100644 --- a/cmd/helm/reset.go +++ b/cmd/helm/reset.go @@ -86,7 +86,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command { // runReset uninstalls tiller from Kubernetes Cluster and deletes local config func (d *resetCmd) run() error { if d.kubeClient == nil { - _, c, err := getKubeClient(kubeContext) + _, c, err := getInternalKubeClient(kubeContext) if err != nil { return fmt.Errorf("could not get kubernetes client: %s", err) } diff --git a/pkg/helm/portforwarder/pod.go b/pkg/helm/portforwarder/pod.go new file mode 100644 index 000000000..e20d644ea --- /dev/null +++ b/pkg/helm/portforwarder/pod.go @@ -0,0 +1,60 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package portforwarder + +import ( + "k8s.io/client-go/pkg/api/v1" +) + +// These functions are adapted from the "kubernetes" repository's file +// +// kubernetes/pkg/api/v1/pod/util.go +// +// where they rely upon the API types specific to that repository. Here we recast them to operate +// upon the type from the "client-go" repository instead. + +// isPodReady returns true if a pod is ready; false otherwise. +func isPodReady(pod *v1.Pod) bool { + return isPodReadyConditionTrue(pod.Status) +} + +// isPodReady retruns true if a pod is ready; false otherwise. +func isPodReadyConditionTrue(status v1.PodStatus) bool { + condition := getPodReadyCondition(status) + return condition != nil && condition.Status == v1.ConditionTrue +} + +// getPodReadyCondition extracts the pod ready condition from the given status and returns that. +// Returns nil if the condition is not present. +func getPodReadyCondition(status v1.PodStatus) *v1.PodCondition { + _, condition := getPodCondition(&status, v1.PodReady) + return condition +} + +// getPodCondition extracts the provided condition from the given status and returns that. +// Returns nil and -1 if the condition is not present, and the index of the located condition. +func getPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) { + if status == nil { + return -1, nil + } + for i := range status.Conditions { + if status.Conditions[i].Type == conditionType { + return i, &status.Conditions[i] + } + } + return -1, nil +} diff --git a/pkg/helm/portforwarder/portforwarder.go b/pkg/helm/portforwarder/portforwarder.go index 58ffe7367..77c9bc9aa 100644 --- a/pkg/helm/portforwarder/portforwarder.go +++ b/pkg/helm/portforwarder/portforwarder.go @@ -21,17 +21,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/pkg/api/v1" "k8s.io/client-go/rest" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/helm/pkg/kube" ) // New creates a new and initialized tunnel. -func New(namespace string, client *internalclientset.Clientset, config *rest.Config) (*kube.Tunnel, error) { - podName, err := getTillerPodName(client.Core(), namespace) +func New(namespace string, client kubernetes.Interface, config *rest.Config) (*kube.Tunnel, error) { + podName, err := getTillerPodName(client.CoreV1(), namespace) if err != nil { return nil, err } @@ -40,7 +40,7 @@ func New(namespace string, client *internalclientset.Clientset, config *rest.Con return t, t.ForwardPort() } -func getTillerPodName(client internalversion.PodsGetter, namespace string) (string, error) { +func getTillerPodName(client corev1.PodsGetter, namespace string) (string, error) { // TODO use a const for labels selector := labels.Set{"app": "helm", "name": "tiller"}.AsSelector() pod, err := getFirstRunningPod(client, namespace, selector) @@ -50,7 +50,7 @@ func getTillerPodName(client internalversion.PodsGetter, namespace string) (stri return pod.ObjectMeta.GetName(), nil } -func getFirstRunningPod(client internalversion.PodsGetter, namespace string, selector labels.Selector) (*api.Pod, error) { +func getFirstRunningPod(client corev1.PodsGetter, namespace string, selector labels.Selector) (*v1.Pod, error) { options := metav1.ListOptions{LabelSelector: selector.String()} pods, err := client.Pods(namespace).List(options) if err != nil { @@ -60,7 +60,7 @@ func getFirstRunningPod(client internalversion.PodsGetter, namespace string, sel return nil, fmt.Errorf("could not find tiller") } for _, p := range pods.Items { - if api.IsPodReady(&p) { + if isPodReady(&p) { return &p, nil } } diff --git a/pkg/helm/portforwarder/portforwarder_test.go b/pkg/helm/portforwarder/portforwarder_test.go index 54ceb586a..964554457 100644 --- a/pkg/helm/portforwarder/portforwarder_test.go +++ b/pkg/helm/portforwarder/portforwarder_test.go @@ -20,63 +20,63 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/pkg/api/v1" ) -func mockTillerPod() api.Pod { - return api.Pod{ +func mockTillerPod() v1.Pod { + return v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "orca", - Namespace: api.NamespaceDefault, + Namespace: v1.NamespaceDefault, Labels: map[string]string{"app": "helm", "name": "tiller"}, }, - Status: api.PodStatus{ - Phase: api.PodRunning, - Conditions: []api.PodCondition{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + Conditions: []v1.PodCondition{ { - Status: api.ConditionTrue, - Type: api.PodReady, + Status: v1.ConditionTrue, + Type: v1.PodReady, }, }, }, } } -func mockTillerPodPending() api.Pod { +func mockTillerPodPending() v1.Pod { p := mockTillerPod() p.Name = "blue" - p.Status.Conditions[0].Status = api.ConditionFalse + p.Status.Conditions[0].Status = v1.ConditionFalse return p } func TestGetFirstPod(t *testing.T) { tests := []struct { name string - pods []api.Pod + pods []v1.Pod expected string err bool }{ { name: "with a ready pod", - pods: []api.Pod{mockTillerPod()}, + pods: []v1.Pod{mockTillerPod()}, expected: "orca", }, { name: "without a ready pod", - pods: []api.Pod{mockTillerPodPending()}, + pods: []v1.Pod{mockTillerPodPending()}, err: true, }, { name: "without a pod", - pods: []api.Pod{}, + pods: []v1.Pod{}, err: true, }, } for _, tt := range tests { - client := fake.NewSimpleClientset(&api.PodList{Items: tt.pods}) - name, err := getTillerPodName(client.Core(), api.NamespaceDefault) + client := fake.NewSimpleClientset(&v1.PodList{Items: tt.pods}) + name, err := getTillerPodName(client.Core(), v1.NamespaceDefault) if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) }