From dc40ddb55a7416e0f1ed652c6befdee05b996c90 Mon Sep 17 00:00:00 2001 From: Nick Santos Date: Wed, 18 Nov 2020 21:38:23 -0500 Subject: [PATCH] cluster: applying a new cluster should make it the current. deleting a cluster should remove it from kubeconfig (#60) --- pkg/cluster/admin_docker_desktop.go | 2 +- pkg/cluster/cluster.go | 27 +++++++++++++++++++++- pkg/cluster/cluster_test.go | 35 +++++++++++++++++++++++++++++ pkg/cluster/config.go | 30 +++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 pkg/cluster/config.go diff --git a/pkg/cluster/admin_docker_desktop.go b/pkg/cluster/admin_docker_desktop.go index 5eee00e..7314005 100644 --- a/pkg/cluster/admin_docker_desktop.go +++ b/pkg/cluster/admin_docker_desktop.go @@ -37,7 +37,7 @@ func (a *dockerDesktopAdmin) LocalRegistryHosting(ctx context.Context, desired * } func (a *dockerDesktopAdmin) Delete(ctx context.Context, config *api.Cluster) error { - if runtime.GOOS != "darwin" && runtime.GOOS != "windows" { + if a.os != "darwin" && a.os != "windows" { return fmt.Errorf("docker-desktop delete not implemented on: %s", runtime.GOOS) } diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 1d3ef93..98a2156 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -56,6 +56,7 @@ type Controller struct { dockerClient dockerClient dmachine *dockerMachine configLoader configLoader + configWriter configWriter registryCtl registryController mu sync.Mutex clientLoader clientLoader @@ -71,6 +72,8 @@ func DefaultController(iostreams genericclioptions.IOStreams) (*Controller, erro return loader.RawConfig() }) + configWriter := kubeconfigWriter{iostreams: iostreams} + clientLoader := clientLoader(func(restConfig *rest.Config) (kubernetes.Interface, error) { return kubernetes.NewForConfig(restConfig) }) @@ -83,6 +86,7 @@ func DefaultController(iostreams genericclioptions.IOStreams) (*Controller, erro return &Controller{ iostreams: iostreams, config: config, + configWriter: configWriter, clients: make(map[string]kubernetes.Interface), admins: make(map[Product]Admin), configLoader: configLoader, @@ -512,6 +516,12 @@ func (c *Controller) Apply(ctx context.Context, desired *api.Cluster) (*api.Clus } } + // Update the kubectl context to match this cluster. + err = c.configWriter.SetContext(desired.Name) + if err != nil { + return nil, err + } + err = c.reloadConfigs() if err != nil { return nil, err @@ -579,7 +589,22 @@ func (c *Controller) Delete(ctx context.Context, name string) error { return err } - return admin.Delete(ctx, existing) + err = admin.Delete(ctx, existing) + if err != nil { + return err + } + + err = c.reloadConfigs() + if err != nil { + return err + } + + // If the context is still in the configs, delete it. + _, ok := c.configCopy().Contexts[existing.Name] + if ok { + return c.configWriter.DeleteContext(existing.Name) + } + return nil } func (c *Controller) reloadConfigs() error { diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index a5d3348..f311e0b 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -43,6 +43,21 @@ func TestClusterCurrent(t *testing.T) { assert.Equal(t, cluster.Product, "microk8s") } +func TestDeleteClusterContext(t *testing.T) { + f := newFixture(t) + + admin := f.newFakeAdmin("docker-desktop") + + _, exists := f.config.Contexts["docker-desktop"] + assert.True(t, exists) + err := f.controller.Delete(context.Background(), "docker-desktop") + assert.NoError(t, err) + assert.Equal(t, "docker-desktop", admin.deleted.Name) + + _, exists = f.config.Contexts["docker-desktop"] + assert.False(t, exists) +} + func TestClusterList(t *testing.T) { c := newFakeController(t) clusters, err := c.List(context.Background(), ListOptions{}) @@ -192,6 +207,7 @@ func TestClusterApplyMinikubeVersion(t *testing.T) { assert.Equal(t, true, f.d4m.started) assert.Equal(t, "minikube", minikubeAdmin.created.Name) assert.Equal(t, "minikube", result.Name) + assert.Equal(t, "minikube", f.config.CurrentContext) minikubeAdmin.created = nil @@ -259,6 +275,7 @@ func newFixture(t *testing.T) *fixture { configLoader := configLoader(func() (clientcmdapi.Config, error) { return *config, nil }) + configWriter := fakeConfigWriter{config: config} iostreams := genericclioptions.IOStreams{ In: os.Stdin, Out: os.Stdout, @@ -280,6 +297,7 @@ func newFixture(t *testing.T) *fixture { iostreams: iostreams, admins: make(map[Product]Admin), config: *config, + configWriter: configWriter, dmachine: dmachine, configLoader: configLoader, clientLoader: clientLoader, @@ -455,3 +473,20 @@ func (c *fakeRegistryController) Apply(ctx context.Context, r *api.Registry) (*a } return newR, nil } + +type fakeConfigWriter struct { + config *clientcmdapi.Config +} + +func (w fakeConfigWriter) SetContext(name string) error { + w.config.CurrentContext = name + return nil +} + +func (w fakeConfigWriter) DeleteContext(name string) error { + if w.config.CurrentContext == name { + w.config.CurrentContext = "" + } + delete(w.config.Contexts, name) + return nil +} diff --git a/pkg/cluster/config.go b/pkg/cluster/config.go new file mode 100644 index 0000000..db3a296 --- /dev/null +++ b/pkg/cluster/config.go @@ -0,0 +1,30 @@ +package cluster + +import ( + "os/exec" + + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +type configWriter interface { + SetContext(name string) error + DeleteContext(name string) error +} + +type kubeconfigWriter struct { + iostreams genericclioptions.IOStreams +} + +func (w kubeconfigWriter) SetContext(name string) error { + cmd := exec.Command("kubectl", "config", "set-context", name) + cmd.Stdout = w.iostreams.Out + cmd.Stderr = w.iostreams.ErrOut + return cmd.Run() +} + +func (w kubeconfigWriter) DeleteContext(name string) error { + cmd := exec.Command("kubectl", "config", "delete-context", name) + cmd.Stdout = w.iostreams.Out + cmd.Stderr = w.iostreams.ErrOut + return cmd.Run() +}