diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index a283c8aba..64212dcc7 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -28,18 +28,18 @@ import ( "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/restclient" - "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/helm/cmd/helm/helmpath" "k8s.io/helm/pkg/kube" + "k8s.io/helm/pkg/tiller/environment" ) const ( localRepoIndexFilePath = "index.yaml" homeEnvVar = "HELM_HOME" hostEnvVar = "HELM_HOST" - tillerNamespace = "kube-system" ) var ( @@ -145,7 +145,7 @@ func main() { func setupConnection(c *cobra.Command, args []string) error { if tillerHost == "" { - tunnel, err := newTillerPortForwarder(tillerNamespace, kubeContext) + tunnel, err := newTillerPortForwarder(environment.TillerNamespace, kubeContext) if err != nil { return err } @@ -199,12 +199,12 @@ func homePath() string { // getKubeClient is a convenience method for creating kubernetes config and client // for a given kubeconfig context -func getKubeClient(context string) (*restclient.Config, *unversioned.Client, error) { +func getKubeClient(context string) (*restclient.Config, *internalclientset.Clientset, 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) } - client, err := unversioned.New(config) + client, err := internalclientset.NewForConfig(config) if err != nil { return nil, nil, fmt.Errorf("could not get kubernetes client: %s", err) } diff --git a/cmd/helm/init.go b/cmd/helm/init.go index d3e60f629..c7a9b24c8 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -24,11 +24,12 @@ import ( "github.com/spf13/cobra" kerrors "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/client/unversioned" + extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" "k8s.io/helm/cmd/helm/helmpath" "k8s.io/helm/cmd/helm/installer" "k8s.io/helm/pkg/repo" + "k8s.io/helm/pkg/tiller/environment" ) const initDesc = ` @@ -65,15 +66,17 @@ type initCmd struct { image string clientOnly bool canary bool + namespace string dryRun bool out io.Writer home helmpath.Home - kubeClient unversioned.DeploymentsNamespacer + kubeClient extensionsclient.DeploymentsGetter } func newInitCmd(out io.Writer) *cobra.Command { i := &initCmd{ - out: out, + out: out, + namespace: environment.TillerNamespace, } cmd := &cobra.Command{ @@ -102,7 +105,7 @@ func newInitCmd(out io.Writer) *cobra.Command { func (i *initCmd) run() error { if flagDebug { - m, err := installer.DeploymentManifest(i.image, i.canary) + m, err := installer.DeploymentManifest(i.namespace, i.image, i.canary) if err != nil { return err } @@ -124,7 +127,7 @@ func (i *initCmd) run() error { } i.kubeClient = c } - if err := installer.Install(i.kubeClient, tillerNamespace, i.image, i.canary, flagDebug); err != nil { + if err := installer.Install(i.kubeClient, i.namespace, i.image, i.canary, flagDebug); err != nil { if !kerrors.IsAlreadyExists(err) { return fmt.Errorf("error installing: %s", err) } diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index f341d8745..7ad617570 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -27,7 +27,9 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" + testcore "k8s.io/kubernetes/pkg/client/testing/core" "k8s.io/kubernetes/pkg/runtime" "k8s.io/helm/cmd/helm/helmpath" @@ -41,14 +43,19 @@ func TestInitCmd(t *testing.T) { defer os.Remove(home) var buf bytes.Buffer - fake := testclient.Fake{} - cmd := &initCmd{out: &buf, home: helmpath.Home(home), kubeClient: fake.Extensions()} + fc := fake.NewSimpleClientset() + cmd := &initCmd{ + out: &buf, + home: helmpath.Home(home), + kubeClient: fc.Extensions(), + namespace: api.NamespaceDefault, + } if err := cmd.run(); err != nil { t.Errorf("expected error: %v", err) } - actions := fake.Actions() - if action, ok := actions[0].(testclient.CreateAction); !ok || action.GetResource() != "deployments" { - t.Errorf("unexpected action: %v, expected create deployment", actions[0]) + action := fc.Actions()[0] + if !action.Matches("create", "deployments") { + t.Errorf("unexpected action: %v, expected create deployment", action) } expected := "Tiller (the helm server side component) has been installed into your Kubernetes Cluster." if !strings.Contains(buf.String(), expected) { @@ -64,11 +71,21 @@ func TestInitCmd_exsits(t *testing.T) { defer os.Remove(home) var buf bytes.Buffer - fake := testclient.Fake{} - fake.AddReactor("*", "*", func(action testclient.Action) (bool, runtime.Object, error) { + fc := fake.NewSimpleClientset(&extensions.Deployment{ + ObjectMeta: api.ObjectMeta{ + Namespace: api.NamespaceDefault, + Name: "tiller-deploy", + }, + }) + fc.AddReactor("*", "*", func(action testcore.Action) (bool, runtime.Object, error) { return true, nil, errors.NewAlreadyExists(api.Resource("deployments"), "1") }) - cmd := &initCmd{out: &buf, home: helmpath.Home(home), kubeClient: fake.Extensions()} + cmd := &initCmd{ + out: &buf, + home: helmpath.Home(home), + kubeClient: fc.Extensions(), + namespace: api.NamespaceDefault, + } if err := cmd.run(); err != nil { t.Errorf("expected error: %v", err) } @@ -86,12 +103,18 @@ func TestInitCmd_clientOnly(t *testing.T) { defer os.Remove(home) var buf bytes.Buffer - fake := testclient.Fake{} - cmd := &initCmd{out: &buf, home: helmpath.Home(home), kubeClient: fake.Extensions(), clientOnly: true} + fc := fake.NewSimpleClientset() + cmd := &initCmd{ + out: &buf, + home: helmpath.Home(home), + kubeClient: fc.Extensions(), + clientOnly: true, + namespace: api.NamespaceDefault, + } if err := cmd.run(); err != nil { t.Errorf("unexpected error: %v", err) } - if len(fake.Actions()) != 0 { + if len(fc.Actions()) != 0 { t.Error("expected client call") } expected := "Not installing tiller due to 'client-only' flag having been set" @@ -114,18 +137,19 @@ func TestInitCmd_dryRun(t *testing.T) { }() var buf bytes.Buffer - fake := testclient.Fake{} + fc := fake.NewSimpleClientset() cmd := &initCmd{ out: &buf, home: helmpath.Home(home), - kubeClient: fake.Extensions(), + kubeClient: fc.Extensions(), clientOnly: true, dryRun: true, + namespace: api.NamespaceDefault, } if err := cmd.run(); err != nil { t.Fatal(err) } - if len(fake.Actions()) != 0 { + if len(fc.Actions()) != 0 { t.Error("expected no server calls") } diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go index e84aece71..f82a9cb7d 100644 --- a/cmd/helm/installer/install.go +++ b/cmd/helm/installer/install.go @@ -23,7 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/unversioned" + extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" "k8s.io/kubernetes/pkg/util/intstr" "k8s.io/helm/pkg/version" @@ -37,27 +37,27 @@ const defaultImage = "gcr.io/kubernetes-helm/tiller" // command failed. // // If verbose is true, this will print the manifest to stdout. -func Install(client unversioned.DeploymentsNamespacer, namespace, image string, canary, verbose bool) error { - obj := deployment(image, canary) - _, err := client.Deployments(namespace).Create(obj) +func Install(client extensionsclient.DeploymentsGetter, namespace, image string, canary, verbose bool) error { + obj := deployment(namespace, image, canary) + _, err := client.Deployments(obj.Namespace).Create(obj) return err } // deployment gets the deployment object that installs Tiller. -func deployment(image string, canary bool) *extensions.Deployment { +func deployment(namespace, image string, canary bool) *extensions.Deployment { switch { case canary: image = defaultImage + ":canary" case image == "": image = fmt.Sprintf("%s:%s", defaultImage, version.Version) } - return generateDeployment(image) + return generateDeployment(namespace, image) } // DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment // resource. -func DeploymentManifest(image string, canary bool) (string, error) { - obj := deployment(image, canary) +func DeploymentManifest(namespace, image string, canary bool) (string, error) { + obj := deployment(namespace, image, canary) buf, err := yaml.Marshal(obj) return string(buf), err @@ -68,12 +68,13 @@ func generateLabels(labels map[string]string) map[string]string { return labels } -func generateDeployment(image string) *extensions.Deployment { +func generateDeployment(namespace, image string) *extensions.Deployment { labels := generateLabels(map[string]string{"name": "tiller"}) d := &extensions.Deployment{ ObjectMeta: api.ObjectMeta{ - Name: "tiller-deploy", - Labels: labels, + Namespace: namespace, + Name: "tiller-deploy", + Labels: labels, }, Spec: extensions.DeploymentSpec{ Replicas: 1, diff --git a/cmd/helm/installer/install_test.go b/cmd/helm/installer/install_test.go index 00a02eb64..7a43660ac 100644 --- a/cmd/helm/installer/install_test.go +++ b/cmd/helm/installer/install_test.go @@ -21,12 +21,13 @@ import ( "testing" "github.com/ghodss/yaml" - - "k8s.io/helm/pkg/version" - + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" + testcore "k8s.io/kubernetes/pkg/client/testing/core" "k8s.io/kubernetes/pkg/runtime" + + "k8s.io/helm/pkg/version" ) func TestDeploymentManifest(t *testing.T) { @@ -44,7 +45,7 @@ func TestDeploymentManifest(t *testing.T) { for _, tt := range tests { - o, err := DeploymentManifest(tt.image, tt.canary) + o, err := DeploymentManifest(api.NamespaceDefault, tt.image, tt.canary) if err != nil { t.Fatalf("%s: error %q", tt.name, err) } @@ -62,9 +63,9 @@ func TestDeploymentManifest(t *testing.T) { func TestInstall(t *testing.T) { image := "gcr.io/kubernetes-helm/tiller:v2.0.0" - fake := testclient.Fake{} - fake.AddReactor("create", "deployments", func(action testclient.Action) (bool, runtime.Object, error) { - obj := action.(testclient.CreateAction).GetObject().(*extensions.Deployment) + fake := fake.NewSimpleClientset() + fake.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { + obj := action.(testcore.CreateAction).GetObject().(*extensions.Deployment) l := obj.GetLabels() if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { t.Errorf("expected labels = '', got '%s'", l) @@ -76,16 +77,16 @@ func TestInstall(t *testing.T) { return true, obj, nil }) - err := Install(fake.Extensions(), "default", image, false, false) + err := Install(fake.Extensions(), api.NamespaceDefault, image, false, false) if err != nil { t.Errorf("unexpected error: %#+v", err) } } func TestInstall_canary(t *testing.T) { - fake := testclient.Fake{} - fake.AddReactor("create", "deployments", func(action testclient.Action) (bool, runtime.Object, error) { - obj := action.(testclient.CreateAction).GetObject().(*extensions.Deployment) + fake := fake.NewSimpleClientset() + fake.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { + obj := action.(testcore.CreateAction).GetObject().(*extensions.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) @@ -93,7 +94,7 @@ func TestInstall_canary(t *testing.T) { return true, obj, nil }) - err := Install(fake.Extensions(), "default", "", true, false) + err := Install(fake.Extensions(), api.NamespaceDefault, "", true, false) if err != nil { t.Errorf("unexpected error: %#+v", err) } diff --git a/cmd/helm/tunnel.go b/cmd/helm/tunnel.go index 6b5142397..67dad1007 100644 --- a/cmd/helm/tunnel.go +++ b/cmd/helm/tunnel.go @@ -20,7 +20,7 @@ import ( "fmt" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/kubernetes/pkg/labels" "k8s.io/helm/pkg/kube" @@ -35,16 +35,16 @@ func newTillerPortForwarder(namespace, context string) (*kube.Tunnel, error) { return nil, err } - podName, err := getTillerPodName(client, namespace) + podName, err := getTillerPodName(client.Core(), namespace) if err != nil { return nil, err } const tillerPort = 44134 - t := kube.NewTunnel(client.RESTClient, config, namespace, podName, tillerPort) + t := kube.NewTunnel(client.Core().RESTClient(), config, namespace, podName, tillerPort) return t, t.ForwardPort() } -func getTillerPodName(client unversioned.PodsNamespacer, namespace string) (string, error) { +func getTillerPodName(client internalversion.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) @@ -54,7 +54,7 @@ func getTillerPodName(client unversioned.PodsNamespacer, namespace string) (stri return pod.ObjectMeta.GetName(), nil } -func getFirstRunningPod(client unversioned.PodsNamespacer, namespace string, selector labels.Selector) (*api.Pod, error) { +func getFirstRunningPod(client internalversion.PodsGetter, namespace string, selector labels.Selector) (*api.Pod, error) { options := api.ListOptions{LabelSelector: selector} pods, err := client.Pods(namespace).List(options) if err != nil { diff --git a/cmd/helm/tunnel_test.go b/cmd/helm/tunnel_test.go index 511a67a74..e7e862b51 100644 --- a/cmd/helm/tunnel_test.go +++ b/cmd/helm/tunnel_test.go @@ -20,7 +20,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" ) func mockTillerPod() api.Pod { @@ -74,8 +74,8 @@ func TestGetFirstPod(t *testing.T) { } for _, tt := range tests { - client := testclient.NewSimpleFake(&api.PodList{Items: tt.pods}) - name, err := getTillerPodName(client, api.NamespaceDefault) + client := fake.NewSimpleClientset(&api.PodList{Items: tt.pods}) + name, err := getTillerPodName(client.Core(), api.NamespaceDefault) if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) } diff --git a/cmd/tiller/tiller.go b/cmd/tiller/tiller.go index 188eddfec..4009c2a24 100644 --- a/cmd/tiller/tiller.go +++ b/cmd/tiller/tiller.go @@ -25,6 +25,7 @@ import ( "github.com/spf13/cobra" + "k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/proto/hapi/services" "k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage/driver" @@ -80,15 +81,16 @@ func main() { } func start(c *cobra.Command, args []string) { + clientset, err := kube.New(nil).ClientSet() + if err != nil { + fmt.Fprintf(os.Stderr, "Cannot initialize Kubernetes connection: %s", err) + } + switch store { case storageMemory: env.Releases = storage.Init(driver.NewMemory()) case storageConfigMap: - c, err := env.KubeClient.APIClient() - if err != nil { - fmt.Fprintf(os.Stderr, "Cannot initialize Kubernetes connection: %s", err) - } - env.Releases = storage.Init(driver.NewConfigMaps(c.ConfigMaps(environment.TillerNamespace))) + env.Releases = storage.Init(driver.NewConfigMaps(clientset.Core().ConfigMaps(environment.TillerNamespace))) } lstn, err := net.Listen("tcp", grpcAddr) @@ -108,7 +110,7 @@ func start(c *cobra.Command, args []string) { srvErrCh := make(chan error) probeErrCh := make(chan error) go func() { - svc := tiller.NewReleaseServer(env) + svc := tiller.NewReleaseServer(env, clientset) services.RegisterReleaseServiceServer(rootServer, svc) if err := rootServer.Serve(lstn); err != nil { srvErrCh <- err diff --git a/glide.lock b/glide.lock index 0f309e1c6..9df10d3b2 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 8ae84a3225f6cc31f91cc42dc8cf816792a989838254964cdcaaa57c75c37cdc -updated: 2016-12-01T17:35:05.940550036-07:00 +hash: 93ecfadd94d604412b346a3b988000c40e4e4414788d653cec36139481e4d157 +updated: 2016-12-13T00:31:11.783543393-08:00 imports: - name: cloud.google.com/go version: 3b1ae45394a234c385be014e9a488f2bb6eef821 @@ -17,22 +17,13 @@ imports: - name: github.com/blang/semver version: 31b736133b98f26d5e078ec9eb591666edfd091f - name: github.com/coreos/go-oidc - version: 5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b + version: 5644a2f50e2d2d5ba0b474bc5bc55fea1925936d subpackages: - http - jose - key - oauth2 - oidc -- name: github.com/coreos/go-systemd - version: 4484981625c1a6a2ecb40a390fcb6a9bcfee76e3 - subpackages: - - activation - - daemon - - dbus - - journal - - unit - - util - name: github.com/coreos/pkg version: fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8 subpackages: @@ -49,6 +40,8 @@ imports: version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d subpackages: - spew +- name: github.com/dgrijalva/jwt-go + version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 - name: github.com/docker/distribution version: cd27f179f2c10c5d300e6d09025b538c475b0d51 subpackages: @@ -101,10 +94,20 @@ imports: - swagger - name: github.com/evanphx/json-patch version: 465937c80b3c07a7c7ad20cc934898646a91c1de +- name: github.com/exponent-io/jsonpath + version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5 - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee +- name: github.com/go-openapi/jsonpointer + version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 +- name: github.com/go-openapi/jsonreference + version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 +- name: github.com/go-openapi/spec + version: 6aced65f8501fe1217321abf0749d354824ba2ff +- name: github.com/go-openapi/swag + version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 - name: github.com/gobwas/glob - version: 0354991b92587e2742549d3036f3b5bae5ab03f2 + version: bea32b9cd2d6f55753d94a28e959b13f0244797a subpackages: - compiler - match @@ -153,50 +156,6 @@ imports: - proto - ptypes/any - ptypes/timestamp -- name: github.com/google/cadvisor - version: a726d13de8cb32860e73d72a78dc8e0124267709 - subpackages: - - api - - cache/memory - - client/v2 - - collector - - container - - container/common - - container/docker - - container/libcontainer - - container/raw - - container/rkt - - container/systemd - - devicemapper - - events - - fs - - healthz - - http - - http/mux - - info/v1 - - info/v1/test - - info/v2 - - machine - - manager - - manager/watcher - - manager/watcher/raw - - manager/watcher/rkt - - metrics - - pages - - pages/static - - storage - - summary - - utils - - utils/cloudinfo - - utils/cpuload - - utils/cpuload/netlink - - utils/docker - - utils/oomparser - - utils/sysfs - - utils/sysinfo - - utils/tail - - validate - - version - name: github.com/google/gofuzz version: bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5 - name: github.com/gosuri/uitable @@ -204,6 +163,8 @@ imports: subpackages: - util/strutil - util/wordwrap +- name: github.com/howeyc/gopass + version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d - name: github.com/imdario/mergo version: 6633656539c1639d9d78127b7d47c622b5d7b6dc - name: github.com/inconshreveable/mousetrap @@ -212,14 +173,26 @@ imports: version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982 - name: github.com/juju/ratelimit version: 77ed1c8a01217656d2080ad51981f6e99adaa177 +- name: github.com/mailru/easyjson + version: d5b7844b561a7bc640052f1b935f7b800330d7e0 + subpackages: + - buffer + - jlexer + - jwriter - name: github.com/Masterminds/semver version: 52edfc04e184ecf0962489d167b511b27aeebd61 - name: github.com/Masterminds/sprig version: 1e60e4ce482a1e2c7b9c9be667535ef152e04300 - name: github.com/mattn/go-runewidth version: d6bea18f789704b5f83375793155289da36a3c7f +- name: github.com/mitchellh/go-wordwrap + version: ad45545899c7b13c020ea92b2072220eefad42b8 - name: github.com/pborman/uuid version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 +- name: github.com/PuerkitoBio/purell + version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 +- name: github.com/PuerkitoBio/urlesc + version: 5bd2802263f21d8788851d5305584c82a5c75d7e - name: github.com/russross/blackfriday version: 300106c228d52c8941d4b3de6054a6062a86dda3 - name: github.com/satori/go.uuid @@ -229,11 +202,11 @@ imports: - name: github.com/Sirupsen/logrus version: 51fe59aca108dc5680109e7b2051cbdcfa5a253c - name: github.com/spf13/cobra - version: 6a8bd97bdb1fc0d08a83459940498ea49d3e8c93 + version: f62e98d28ab7ad31d707ba837a966378465c7b57 subpackages: - doc - name: github.com/spf13/pflag - version: 367864438f1b1a3c7db4da06a2f55b144e6784e0 + version: 5ccb023bc27df288a957c5e994cd44fd19619465 - name: github.com/technosophos/moniker version: 9f956786b91d9786ca11aa5be6104542fa911546 - name: github.com/ugorji/go @@ -254,13 +227,15 @@ imports: - openpgp/s2k - ssh/terminal - name: golang.org/x/net - version: fb93926129b8ec0056f2f458b1f519654814edf0 + version: e90d6d0afc4c315a0d87a568ae68577cc15149a0 subpackages: - context - context/ctxhttp - http2 - http2/hpack + - idna - internal/timeseries + - lex/httplex - trace - websocket - name: golang.org/x/oauth2 @@ -270,6 +245,23 @@ imports: - internal - jws - jwt +- name: golang.org/x/sys + version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9 + subpackages: + - unix +- name: golang.org/x/text + version: 2910a502d2bf9e43193af9d68ca516529614eed3 + subpackages: + - cases + - internal/tag + - language + - runes + - secure/bidirule + - secure/precis + - transform + - unicode/bidi + - unicode/norm + - width - name: google.golang.org/appengine version: 4f7eeb5305a4ba1966344836ba4af9996b7b4e05 subpackages: @@ -297,88 +289,24 @@ imports: version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/yaml.v2 version: a83829b6f1293c91addabc89d0571c246397bbf4 -- name: k8s.io/client-go - version: 0b62e254fe853d89b1d8d3445bbdab11bcc11bc3 - subpackages: - - 1.4/pkg/api - - 1.4/pkg/api/endpoints - - 1.4/pkg/api/errors - - 1.4/pkg/api/meta - - 1.4/pkg/api/meta/metatypes - - 1.4/pkg/api/pod - - 1.4/pkg/api/resource - - 1.4/pkg/api/service - - 1.4/pkg/api/unversioned - - 1.4/pkg/api/unversioned/validation - - 1.4/pkg/api/util - - 1.4/pkg/api/v1 - - 1.4/pkg/api/validation - - 1.4/pkg/apimachinery - - 1.4/pkg/apimachinery/registered - - 1.4/pkg/apis/autoscaling - - 1.4/pkg/apis/batch - - 1.4/pkg/apis/extensions - - 1.4/pkg/auth/user - - 1.4/pkg/capabilities - - 1.4/pkg/conversion - - 1.4/pkg/conversion/queryparams - - 1.4/pkg/fields - - 1.4/pkg/labels - - 1.4/pkg/runtime - - 1.4/pkg/runtime/serializer - - 1.4/pkg/runtime/serializer/json - - 1.4/pkg/runtime/serializer/protobuf - - 1.4/pkg/runtime/serializer/recognizer - - 1.4/pkg/runtime/serializer/streaming - - 1.4/pkg/runtime/serializer/versioning - - 1.4/pkg/security/apparmor - - 1.4/pkg/selection - - 1.4/pkg/third_party/forked/golang/reflect - - 1.4/pkg/types - - 1.4/pkg/util - - 1.4/pkg/util/clock - - 1.4/pkg/util/config - - 1.4/pkg/util/crypto - - 1.4/pkg/util/errors - - 1.4/pkg/util/flowcontrol - - 1.4/pkg/util/framer - - 1.4/pkg/util/hash - - 1.4/pkg/util/integer - - 1.4/pkg/util/intstr - - 1.4/pkg/util/json - - 1.4/pkg/util/labels - - 1.4/pkg/util/net - - 1.4/pkg/util/net/sets - - 1.4/pkg/util/parsers - - 1.4/pkg/util/rand - - 1.4/pkg/util/runtime - - 1.4/pkg/util/sets - - 1.4/pkg/util/uuid - - 1.4/pkg/util/validation - - 1.4/pkg/util/validation/field - - 1.4/pkg/util/wait - - 1.4/pkg/util/yaml - - 1.4/pkg/version - - 1.4/pkg/watch - - 1.4/pkg/watch/versioned - - 1.4/rest - - 1.4/tools/clientcmd/api - - 1.4/tools/metrics - - 1.4/transport - name: k8s.io/kubernetes - version: fd8fac83034df346529c6e11aabceea2db48d663 + version: d47846323632bf59c729460fc7344d2df347bf46 subpackages: + - cmd/kubeadm/app/apis/kubeadm + - cmd/kubeadm/app/apis/kubeadm/install + - cmd/kubeadm/app/apis/kubeadm/v1alpha1 - federation/apis/federation - federation/apis/federation/install - federation/apis/federation/v1beta1 - federation/client/clientset_generated/federation_internalclientset - - federation/client/clientset_generated/federation_internalclientset/typed/core/unversioned - - federation/client/clientset_generated/federation_internalclientset/typed/extensions/unversioned - - federation/client/clientset_generated/federation_internalclientset/typed/federation/unversioned + - federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion + - federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion + - federation/client/clientset_generated/federation_internalclientset/typed/federation/internalversion - pkg/api - pkg/api/annotations - pkg/api/endpoints - pkg/api/errors + - pkg/api/events - pkg/api/install - pkg/api/meta - pkg/api/meta/metatypes @@ -392,11 +320,13 @@ imports: - pkg/api/util - pkg/api/v1 - pkg/api/validation + - pkg/api/validation/path - pkg/apimachinery + - pkg/apimachinery/announced - pkg/apimachinery/registered - pkg/apis/apps - pkg/apis/apps/install - - pkg/apis/apps/v1alpha1 + - pkg/apis/apps/v1beta1 - pkg/apis/authentication - pkg/apis/authentication/install - pkg/apis/authentication/v1beta1 @@ -425,43 +355,60 @@ imports: - pkg/apis/imagepolicy/v1alpha1 - pkg/apis/policy - pkg/apis/policy/install - - pkg/apis/policy/v1alpha1 + - pkg/apis/policy/v1beta1 - pkg/apis/rbac - pkg/apis/rbac/install - pkg/apis/rbac/v1alpha1 - pkg/apis/storage - pkg/apis/storage/install + - pkg/apis/storage/util - pkg/apis/storage/v1beta1 + - pkg/auth/authenticator - pkg/auth/user - pkg/capabilities - pkg/client/cache - pkg/client/clientset_generated/internalclientset - - pkg/client/clientset_generated/internalclientset/typed/authentication/unversioned - - pkg/client/clientset_generated/internalclientset/typed/authorization/unversioned - - pkg/client/clientset_generated/internalclientset/typed/autoscaling/unversioned - - pkg/client/clientset_generated/internalclientset/typed/batch/unversioned - - pkg/client/clientset_generated/internalclientset/typed/certificates/unversioned - - pkg/client/clientset_generated/internalclientset/typed/core/unversioned - - pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned - - pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned - - pkg/client/clientset_generated/internalclientset/typed/storage/unversioned + - pkg/client/clientset_generated/internalclientset/fake + - pkg/client/clientset_generated/internalclientset/typed/apps/internalversion + - pkg/client/clientset_generated/internalclientset/typed/apps/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion + - pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion + - pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion + - pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/batch/internalversion + - pkg/client/clientset_generated/internalclientset/typed/batch/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion + - pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/core/internalversion + - pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion + - pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/policy/internalversion + - pkg/client/clientset_generated/internalclientset/typed/policy/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion + - pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/fake + - pkg/client/clientset_generated/internalclientset/typed/storage/internalversion + - pkg/client/clientset_generated/internalclientset/typed/storage/internalversion/fake - pkg/client/metrics - pkg/client/record - pkg/client/restclient + - pkg/client/restclient/fake + - pkg/client/retry + - pkg/client/testing/core - pkg/client/transport - pkg/client/typed/discovery + - pkg/client/typed/discovery/fake - pkg/client/typed/dynamic - pkg/client/unversioned - - pkg/client/unversioned/adapters/internalclientset - pkg/client/unversioned/auth - pkg/client/unversioned/clientcmd - pkg/client/unversioned/clientcmd/api - pkg/client/unversioned/clientcmd/api/latest - pkg/client/unversioned/clientcmd/api/v1 - - pkg/client/unversioned/fake - pkg/client/unversioned/portforward - pkg/client/unversioned/remotecommand - - pkg/client/unversioned/testclient - pkg/controller - pkg/controller/deployment/util - pkg/conversion @@ -469,8 +416,10 @@ imports: - pkg/credentialprovider - pkg/fieldpath - pkg/fields + - pkg/genericapiserver/openapi/common - pkg/httplog - pkg/kubectl + - pkg/kubectl/cmd/testing - pkg/kubectl/cmd/util - pkg/kubectl/resource - pkg/kubelet/qos @@ -479,7 +428,7 @@ imports: - pkg/kubelet/types - pkg/labels - pkg/master/ports - - pkg/registry/thirdpartyresourcedata + - pkg/registry/extensions/thirdpartyresourcedata - pkg/runtime - pkg/runtime/serializer - pkg/runtime/serializer/json @@ -488,15 +437,17 @@ imports: - pkg/runtime/serializer/streaming - pkg/runtime/serializer/versioning - pkg/security/apparmor + - pkg/security/podsecuritypolicy/seccomp - pkg/security/podsecuritypolicy/util - pkg/selection + - pkg/serviceaccount - pkg/storage - pkg/types - pkg/util - - pkg/util/certificates + - pkg/util/cert - pkg/util/clock - pkg/util/config - - pkg/util/crypto + - pkg/util/diff - pkg/util/errors - pkg/util/exec - pkg/util/flag @@ -514,10 +465,10 @@ imports: - pkg/util/labels - pkg/util/net - pkg/util/net/sets + - pkg/util/node - pkg/util/parsers - pkg/util/pod - pkg/util/rand - - pkg/util/replicaset - pkg/util/runtime - pkg/util/sets - pkg/util/slice diff --git a/glide.yaml b/glide.yaml index f3e3a4b3e..be7ec30be 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,10 +1,13 @@ package: k8s.io/helm import: - package: golang.org/x/net - version: fb93926129b8ec0056f2f458b1f519654814edf0 + version: e90d6d0afc4c315a0d87a568ae68577cc15149a0 subpackages: - context - package: github.com/spf13/cobra + version: f62e98d28ab7ad31d707ba837a966378465c7b57 +- package: github.com/spf13/pflag + version: 5ccb023bc27df288a957c5e994cd44fd19619465 - package: github.com/Masterminds/sprig version: ^2.7 - package: github.com/ghodss/yaml @@ -20,7 +23,7 @@ import: - package: google.golang.org/grpc version: 1.0.3 - package: k8s.io/kubernetes - version: ~1.4.1 + version: ~1.5.0 subpackages: - pkg/api - pkg/api/errors @@ -28,12 +31,14 @@ import: - pkg/apimachinery/registered - pkg/apis/batch - pkg/apis/extensions + - pkg/client/clientset_generated/internalclientset + - pkg/client/clientset_generated/internalclientset/typed/core/internalversion + - pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion - pkg/client/restclient - - pkg/client/unversioned + - pkg/client/typed/discovery - pkg/client/unversioned/clientcmd - pkg/client/unversioned/portforward - pkg/client/unversioned/remotecommand - - pkg/client/unversioned/testclient - pkg/kubectl - pkg/kubectl/cmd/util - pkg/kubectl/resource @@ -41,7 +46,6 @@ import: - pkg/runtime - pkg/util/intstr - pkg/util/strategicpatch - - pkg/util/yaml - pkg/watch - package: github.com/gosuri/uitable - package: github.com/asaskevich/govalidator diff --git a/pkg/kube/client.go b/pkg/kube/client.go index e76ed5180..e721b69b9 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -22,7 +22,6 @@ import ( "fmt" "io" "log" - "reflect" "strings" "time" @@ -30,14 +29,12 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/batch" - "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/strategicpatch" - "k8s.io/kubernetes/pkg/util/yaml" "k8s.io/kubernetes/pkg/watch" ) @@ -46,14 +43,7 @@ var ErrNoObjectsVisited = goerrors.New("no objects visited") // Client represents a client capable of communicating with the Kubernetes API. type Client struct { - *cmdutil.Factory - // IncludeThirdPartyAPIs indicates whether to load "dynamic" APIs. - // - // This requires additional calls to the Kubernetes API server, and these calls - // are not supported by all versions. Additionally, during testing, initializing - // a client will still attempt to contact a live server. In these situations, - // this flag may need to be disabled. - IncludeThirdPartyAPIs bool + cmdutil.Factory // Validate idicates whether to load a schema for validation. Validate bool // SchemaCacheDir is the path for loading cached schema. @@ -63,10 +53,9 @@ type Client struct { // New create a new Client func New(config clientcmd.ClientConfig) *Client { return &Client{ - Factory: cmdutil.NewFactory(config), - IncludeThirdPartyAPIs: true, - Validate: true, - SchemaCacheDir: clientcmd.RecommendedSchemaFile, + Factory: cmdutil.NewFactory(config), + Validate: true, + SchemaCacheDir: clientcmd.RecommendedSchemaFile, } } @@ -82,21 +71,15 @@ func (e ErrAlreadyExists) Error() string { return fmt.Sprintf("Looks like there are no changes for %s", e.errorMsg) } -// APIClient returns a Kubernetes API client. -// -// This is necessary because cmdutil.Client is a field, not a method, which -// means it can't satisfy an interface's method requirement. In order to ensure -// that an implementation of environment.KubeClient can access the raw API client, -// it is necessary to add this method. -func (c *Client) APIClient() (unversioned.Interface, error) { - return c.Client() -} - // Create creates kubernetes resources from an io.reader // // Namespace will set the namespace func (c *Client) Create(namespace string, reader io.Reader) error { - if err := c.ensureNamespace(namespace); err != nil { + client, err := c.ClientSet() + if err != nil { + return err + } + if err := ensureNamespace(client, namespace); err != nil { return err } return perform(c, namespace, reader, createResource) @@ -107,7 +90,7 @@ func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Builde if err != nil { log.Printf("warning: failed to load schema: %s", err) } - return c.NewBuilder(c.IncludeThirdPartyAPIs). + return c.NewBuilder(). ContinueOnError(). Schema(schema). NamespaceParam(namespace). @@ -313,33 +296,22 @@ func deleteResource(info *resource.Info) error { } func updateResource(target *resource.Info, currentObj runtime.Object) error { - encoder := api.Codecs.LegacyCodec(registered.EnabledVersions()...) - originalSerialization, err := runtime.Encode(encoder, currentObj) - if err != nil { - return err - } - - editedSerialization, err := runtime.Encode(encoder, target.Object) - if err != nil { - return err - } - - originalJS, err := yaml.ToJSON(originalSerialization) + original, err := runtime.Encode(encoder, currentObj) if err != nil { return err } - editedJS, err := yaml.ToJSON(editedSerialization) + modified, err := runtime.Encode(encoder, target.Object) if err != nil { return err } - if reflect.DeepEqual(originalJS, editedJS) { + if api.Semantic.DeepEqual(original, modified) { return ErrAlreadyExists{target.Name} } - patch, err := strategicpatch.CreateStrategicMergePatch(originalJS, editedJS, currentObj) + patch, err := strategicpatch.CreateTwoWayMergePatch(original, modified, currentObj) if err != nil { return err } @@ -413,21 +385,6 @@ func waitForJob(e watch.Event, name string) (bool, error) { return false, nil } -func (c *Client) ensureNamespace(namespace string) error { - client, err := c.Client() - if err != nil { - return err - } - - ns := &api.Namespace{} - ns.Name = namespace - _, err = client.Namespaces().Create(ns) - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - return nil -} - func deleteUnwantedResources(currentInfos, targetInfos []*resource.Info) { for _, cInfo := range currentInfos { if _, ok := findMatchingInfo(cInfo, targetInfos); !ok { diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index afd95a5a0..cd7c9850b 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -18,51 +18,122 @@ package kube import ( "bytes" - "encoding/json" "io" "io/ioutil" "net/http" "strings" "testing" - "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - api "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/client/unversioned/fake" + "k8s.io/kubernetes/pkg/client/restclient/fake" + cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" ) -func TestUpdateResource(t *testing.T) { +func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { + return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) +} - tests := []struct { - name string - namespace string - modified *resource.Info - currentObj runtime.Object - err bool - errMessage string - }{ - { - name: "no changes when updating resources", - modified: createFakeInfo("nginx", nil), - currentObj: createFakePod("nginx", nil), - err: true, - errMessage: "Looks like there are no changes for nginx", +func newPod(name string) api.Pod { + return api.Pod{ + ObjectMeta: api.ObjectMeta{Name: name}, + Spec: api.PodSpec{ + Containers: []api.Container{{ + Name: "app:v4", + Image: "abc/app:v4", + Ports: []api.ContainerPort{{Name: "http", ContainerPort: 80}}, + }}, }, - //{ - //name: "valid update input", - //modified: createFakeInfo("nginx", map[string]string{"app": "nginx"}), - //currentObj: createFakePod("nginx", nil), - //}, } +} + +func newPodList(names ...string) api.PodList { + var list api.PodList + for _, name := range names { + list.Items = append(list.Items, newPod(name)) + } + return list +} - for _, tt := range tests { - err := updateResource(tt.modified, tt.currentObj) - if err != nil && err.Error() != tt.errMessage { - t.Errorf("%q. expected error message: %v, got %v", tt.name, tt.errMessage, err) +func notFoundBody() *unversioned.Status { + return &unversioned.Status{ + Code: http.StatusNotFound, + Status: unversioned.StatusFailure, + Reason: unversioned.StatusReasonNotFound, + Message: " \"\" not found", + Details: &unversioned.StatusDetails{}, + } +} + +func newResponse(code int, obj runtime.Object) (*http.Response, error) { + header := http.Header{} + header.Set("Content-Type", runtime.ContentTypeJSON) + body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), obj)))) + return &http.Response{StatusCode: code, Header: header, Body: body}, nil +} + +func TestUpdate(t *testing.T) { + listA := newPodList("starfish", "otter", "squid") + listB := newPodList("starfish", "otter", "dolphin") + listB.Items[0].Spec.Containers[0].Ports = []api.ContainerPort{{Name: "https", ContainerPort: 443}} + + actions := make(map[string]string) + + f, tf, codec, ns := cmdtesting.NewAPIFactory() + tf.Client = &fake.RESTClient{ + NegotiatedSerializer: ns, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + p, m := req.URL.Path, req.Method + actions[p] = m + switch { + case p == "/namespaces/test/pods/starfish" && m == "GET": + return newResponse(200, &listA.Items[0]) + case p == "/namespaces/test/pods/otter" && m == "GET": + return newResponse(200, &listA.Items[1]) + case p == "/namespaces/test/pods/dolphin" && m == "GET": + return newResponse(404, notFoundBody()) + case p == "/namespaces/test/pods/starfish" && m == "PATCH": + data, err := ioutil.ReadAll(req.Body) + if err != nil { + t.Fatalf("could not dump request: %s", err) + } + req.Body.Close() + expected := `{"spec":{"containers":[{"name":"app:v4","ports":[{"containerPort":443,"name":"https","protocol":"TCP"},{"$patch":"delete","containerPort":80}]}]}}` + if string(data) != expected { + t.Errorf("expected patch %s, got %s", expected, string(data)) + } + return newResponse(200, &listB.Items[0]) + case p == "/namespaces/test/pods" && m == "POST": + return newResponse(200, &listB.Items[1]) + case p == "/namespaces/test/pods/squid" && m == "DELETE": + return newResponse(200, &listB.Items[1]) + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + + c := &Client{Factory: f} + if err := c.Update("test", objBody(codec, &listA), objBody(codec, &listB)); err != nil { + t.Fatal(err) + } + + expectedActions := map[string]string{ + "/namespaces/test/pods/dolphin": "GET", + "/namespaces/test/pods/otter": "GET", + "/namespaces/test/pods/starfish": "PATCH", + "/namespaces/test/pods": "POST", + "/namespaces/test/pods/squid": "DELETE", + } + + for k, v := range expectedActions { + if m, ok := actions[k]; !ok || m != v { + t.Errorf("expected a %s request to %s", k, v) } } } @@ -110,20 +181,18 @@ func TestPerform(t *testing.T) { return nil } - c := New(nil) - c.IncludeThirdPartyAPIs = false - c.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return &fake.RESTClient{}, nil - } - c.Validator = func(validate bool, cacheDir string) (validation.Schema, error) { - if tt.swaggerFile == "" { - return validation.NullSchema{}, nil - } + f, tf, _, _ := cmdtesting.NewAPIFactory() + c := &Client{Factory: f} + if tt.swaggerFile != "" { data, err := ioutil.ReadFile(tt.swaggerFile) + if err != nil { + t.Fatalf("could not read swagger spec: %s", err) + } + validator, err := validation.NewSwaggerSchemaFromBytes(data, nil) if err != nil { t.Fatalf("could not load swagger spec: %s", err) } - return validation.NewSwaggerSchemaFromBytes(data, nil) + tf.Validator = validator } err := perform(c, tt.namespace, tt.reader, fn) @@ -143,14 +212,12 @@ func TestPerform(t *testing.T) { func TestReal(t *testing.T) { t.Skip("This is a live test, comment this line to run") c := New(nil) - c.IncludeThirdPartyAPIs = false if err := c.Create("test", strings.NewReader(guestbookManifest)); err != nil { t.Fatal(err) } testSvcEndpointManifest := testServiceManifest + "\n---\n" + testEndpointManifest c = New(nil) - c.IncludeThirdPartyAPIs = false if err := c.Create("test-delete", strings.NewReader(testSvcEndpointManifest)); err != nil { t.Fatal(err) } @@ -322,52 +389,3 @@ spec: ports: - containerPort: 80 ` - -func createFakePod(name string, labels map[string]string) runtime.Object { - objectMeta := createObjectMeta(name, labels) - - object := &api.Pod{ - ObjectMeta: objectMeta, - } - - return object -} - -func createFakeInfo(name string, labels map[string]string) *resource.Info { - pod := createFakePod(name, labels) - marshaledObj, _ := json.Marshal(pod) - - mapping := &meta.RESTMapping{ - Resource: name, - Scope: meta.RESTScopeNamespace, - GroupVersionKind: unversioned.GroupVersionKind{ - Kind: "Pod", - Version: "v1", - }} - - client := &fake.RESTClient{ - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - header := http.Header{} - header.Set("Content-Type", runtime.ContentTypeJSON) - return &http.Response{ - StatusCode: 200, - Header: header, - Body: ioutil.NopCloser(bytes.NewReader(marshaledObj)), - }, nil - })} - info := resource.NewInfo(client, mapping, "default", "nginx", false) - - info.Object = pod - - return info -} - -func createObjectMeta(name string, labels map[string]string) api.ObjectMeta { - objectMeta := api.ObjectMeta{Name: name, Namespace: "default"} - - if labels != nil { - objectMeta.Labels = labels - } - - return objectMeta -} diff --git a/pkg/kube/namespace.go b/pkg/kube/namespace.go new file mode 100644 index 000000000..81784c95c --- /dev/null +++ b/pkg/kube/namespace.go @@ -0,0 +1,41 @@ +/* +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 kube // import "k8s.io/helm/pkg/kube" + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" +) + +func createNamespace(client internalclientset.Interface, namespace string) error { + ns := &api.Namespace{ + ObjectMeta: api.ObjectMeta{ + Name: namespace, + }, + } + _, err := client.Core().Namespaces().Create(ns) + return err +} + +func ensureNamespace(client internalclientset.Interface, namespace string) error { + err := createNamespace(client, namespace) + if err != nil && !errors.IsAlreadyExists(err) { + return err + } + return nil +} diff --git a/pkg/kube/namespace_test.go b/pkg/kube/namespace_test.go new file mode 100644 index 000000000..869b65bb7 --- /dev/null +++ b/pkg/kube/namespace_test.go @@ -0,0 +1,36 @@ +/* +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 kube // import "k8s.io/helm/pkg/kube" + +import ( + "testing" + + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" +) + +func TestEnsureNamespace(t *testing.T) { + client := fake.NewSimpleClientset() + if err := ensureNamespace(client, "foo"); err != nil { + t.Fatalf("unexpected error: %s", err) + } + if err := ensureNamespace(client, "foo"); err != nil { + t.Fatalf("unexpected error: %s", err) + } + if _, err := client.Core().Namespaces().Get("foo"); err != nil { + t.Fatalf("unexpected error: %s", err) + } +} diff --git a/pkg/kube/tunnel.go b/pkg/kube/tunnel.go index a93c0384b..5a5b68235 100644 --- a/pkg/kube/tunnel.go +++ b/pkg/kube/tunnel.go @@ -38,11 +38,11 @@ type Tunnel struct { stopChan chan struct{} readyChan chan struct{} config *restclient.Config - client *restclient.RESTClient + client restclient.Interface } // NewTunnel creates a new tunnel -func NewTunnel(client *restclient.RESTClient, config *restclient.Config, namespace, podName string, remote int) *Tunnel { +func NewTunnel(client restclient.Interface, config *restclient.Config, namespace, podName string, remote int) *Tunnel { return &Tunnel{ config: config, client: client, diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index 722e2b144..79a562b17 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -27,13 +27,12 @@ import ( "time" "github.com/golang/protobuf/proto" - - rspb "k8s.io/helm/pkg/proto/hapi/release" - "k8s.io/kubernetes/pkg/api" kberrs "k8s.io/kubernetes/pkg/api/errors" - client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" kblabels "k8s.io/kubernetes/pkg/labels" + + rspb "k8s.io/helm/pkg/proto/hapi/release" ) var _ Driver = (*ConfigMaps)(nil) @@ -48,12 +47,12 @@ var magicGzip = []byte{0x1f, 0x8b, 0x08} // ConfigMaps is a wrapper around an implementation of a kubernetes // ConfigMapsInterface. type ConfigMaps struct { - impl client.ConfigMapsInterface + impl internalversion.ConfigMapInterface } // NewConfigMaps initializes a new ConfigMaps wrapping an implmenetation of // the kubernetes ConfigMapsInterface. -func NewConfigMaps(impl client.ConfigMapsInterface) *ConfigMaps { +func NewConfigMaps(impl internalversion.ConfigMapInterface) *ConfigMaps { return &ConfigMaps{impl: impl} } @@ -210,7 +209,7 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { return nil, err } // delete the release - if err = cfgmaps.impl.Delete(key); err != nil { + if err = cfgmaps.impl.Delete(key, &api.DeleteOptions{}); err != nil { return rls, err } return rls, nil diff --git a/pkg/storage/driver/cfgmaps_test.go b/pkg/storage/driver/cfgmaps_test.go index 9df12877b..13cc09b01 100644 --- a/pkg/storage/driver/cfgmaps_test.go +++ b/pkg/storage/driver/cfgmaps_test.go @@ -19,9 +19,9 @@ import ( "testing" "github.com/gogo/protobuf/proto" + "k8s.io/kubernetes/pkg/api" rspb "k8s.io/helm/pkg/proto/hapi/release" - "k8s.io/kubernetes/pkg/api" ) func TestConfigMapName(t *testing.T) { diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index d21cd0f2f..3c546a45b 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -20,10 +20,11 @@ import ( "fmt" "testing" - rspb "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/kubernetes/pkg/api" kberrs "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" + + rspb "k8s.io/helm/pkg/proto/hapi/release" ) func releaseStub(name string, vers int32, code rspb.Status_Code) *rspb.Release { @@ -73,7 +74,7 @@ func newTestFixtureCfgMaps(t *testing.T, releases ...*rspb.Release) *ConfigMaps // MockConfigMapsInterface mocks a kubernetes ConfigMapsInterface type MockConfigMapsInterface struct { - unversioned.ConfigMapsInterface + internalversion.ConfigMapInterface objects map[string]*api.ConfigMap } @@ -132,7 +133,7 @@ func (mock *MockConfigMapsInterface) Update(cfgmap *api.ConfigMap) (*api.ConfigM } // Delete deletes a ConfigMap by name. -func (mock *MockConfigMapsInterface) Delete(name string) error { +func (mock *MockConfigMapsInterface) Delete(name string, opts *api.DeleteOptions) error { if _, ok := mock.objects[name]; !ok { return kberrs.NewNotFound(api.Resource("tests"), name) } diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index 72fd510be..1b2aa4e6d 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -31,8 +31,6 @@ import ( "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage/driver" - "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" ) // TillerNamespace is the namespace tiller is running in. @@ -134,9 +132,6 @@ type KubeClient interface { // reader must contain a YAML stream (one or more YAML documents separated // by "\n---\n"). Update(namespace string, originalReader, modifiedReader io.Reader) error - - // APIClient gets a raw API client for Kubernetes. - APIClient() (unversioned.Interface, error) } // PrintingKubeClient implements KubeClient, but simply prints the reader to @@ -145,14 +140,6 @@ type PrintingKubeClient struct { Out io.Writer } -// APIClient always returns an error. -// -// The printing client does not have access to a Kubernetes client at all. So it -// will always return an error if the client is accessed. -func (p *PrintingKubeClient) APIClient() (unversioned.Interface, error) { - return testclient.NewSimpleFake(), nil -} - // Create prints the values of what would be created with a real KubeClient. func (p *PrintingKubeClient) Create(ns string, r io.Reader) error { _, err := io.Copy(p.Out, r) diff --git a/pkg/tiller/environment/environment_test.go b/pkg/tiller/environment/environment_test.go index 68cc5d63c..f547c75a9 100644 --- a/pkg/tiller/environment/environment_test.go +++ b/pkg/tiller/environment/environment_test.go @@ -23,8 +23,6 @@ import ( "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/proto/hapi/chart" - unversionedclient "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" ) type mockEngine struct { @@ -35,12 +33,7 @@ func (e *mockEngine) Render(chrt *chart.Chart, v chartutil.Values) (map[string]s return e.out, nil } -type mockKubeClient struct { -} - -func (k *mockKubeClient) APIClient() (unversionedclient.Interface, error) { - return testclient.NewSimpleFake(), nil -} +type mockKubeClient struct{} func (k *mockKubeClient) Create(ns string, r io.Reader) error { return nil diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 281a234ba..c03107cbe 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -23,6 +23,7 @@ import ( "strings" "github.com/ghodss/yaml" + "k8s.io/helm/pkg/proto/hapi/release" ) diff --git a/pkg/tiller/release_history.go b/pkg/tiller/release_history.go index 003bf8c03..341079575 100644 --- a/pkg/tiller/release_history.go +++ b/pkg/tiller/release_history.go @@ -18,6 +18,7 @@ package tiller import ( "golang.org/x/net/context" + tpb "k8s.io/helm/pkg/proto/hapi/services" relutil "k8s.io/helm/pkg/releaseutil" ) diff --git a/pkg/tiller/release_history_test.go b/pkg/tiller/release_history_test.go index 75139c51f..5df98410f 100644 --- a/pkg/tiller/release_history_test.go +++ b/pkg/tiller/release_history_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/helm/pkg/helm" - rpb "k8s.io/helm/pkg/proto/hapi/release" tpb "k8s.io/helm/pkg/proto/hapi/services" ) diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index f3ad0252f..8df3a8991 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -25,12 +25,13 @@ import ( "regexp" "strings" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" - "github.com/technosophos/moniker" ctx "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/client/typed/discovery" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/kube" @@ -95,13 +96,15 @@ func NewServer() *grpc.Server { // ReleaseServer implements the server-side gRPC endpoint for the HAPI services. type ReleaseServer struct { - env *environment.Environment + env *environment.Environment + clientset internalclientset.Interface } // NewReleaseServer creates a new release server. -func NewReleaseServer(env *environment.Environment) *ReleaseServer { +func NewReleaseServer(env *environment.Environment, clientset internalclientset.Interface) *ReleaseServer { return &ReleaseServer{ - env: env, + env: env, + clientset: clientset, } } @@ -693,15 +696,10 @@ func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re return rel, nil } -func (s *ReleaseServer) getVersionSet() (versionSet, error) { +func getVersionSet(client discovery.ServerGroupsInterface) (versionSet, error) { defVersions := newVersionSet("v1") - cli, err := s.env.KubeClient.APIClient() - if err != nil { - log.Printf("API Client for Kubernetes is missing: %s.", err) - return defVersions, err - } - groups, err := cli.Discovery().ServerGroups() + groups, err := client.ServerGroups() if err != nil { return defVersions, err } @@ -745,7 +743,7 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values // Sort hooks, manifests, and partials. Only hooks and manifests are returned, // as partials are not used after renderer.Render. Empty manifests are also // removed here. - vs, err := s.getVersionSet() + vs, err := getVersionSet(s.clientset.Discovery()) if err != nil { return nil, nil, "", fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err) } @@ -958,7 +956,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR } } - vs, err := s.getVersionSet() + vs, err := getVersionSet(s.clientset.Discovery()) if err != nil { return nil, fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err) } diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index 6c5d8909d..95caac4e4 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -28,6 +28,7 @@ import ( "github.com/golang/protobuf/ptypes/timestamp" "golang.org/x/net/context" "google.golang.org/grpc/metadata" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/proto/hapi/chart" @@ -82,7 +83,8 @@ data: func rsFixture() *ReleaseServer { return &ReleaseServer{ - env: mockEnvironment(), + env: mockEnvironment(), + clientset: fake.NewSimpleClientset(), } } @@ -178,7 +180,7 @@ func TestValidName(t *testing.T) { func TestGetVersionSet(t *testing.T) { rs := rsFixture() - vs, err := rs.getVersionSet() + vs, err := getVersionSet(rs.clientset.Discovery()) if err != nil { t.Error(err) }