Browse Source

Merge pull request #1175 from aiordache/kube_context_create

Refactor kubernetes context create
Guillaume Tardif 4 years ago
parent
commit
0d778fbf48

+ 2 - 1
api/context/store/contextmetadata.go

@@ -57,7 +57,8 @@ type EcsContext struct {
 
 // KubeContext is the context for a kube backend
 type KubeContext struct {
-	Endpoint        string `json:",omitempty"`
+	ContextName     string `json:",omitempty"`
+	KubeconfigPath  string `json:",omitempty"`
 	FromEnvironment bool
 }
 

+ 1 - 1
api/context/store/store.go

@@ -57,7 +57,7 @@ const (
 	LocalContextType = "local"
 	// KubeContextType is the endpoint key in the context endpoints for a new
 	// kube backend
-	KubeContextType = "kubernetes"
+	KubeContextType = "kube"
 )
 
 const (

+ 8 - 17
cli/cmd/context/create_kube.go

@@ -33,8 +33,8 @@ func init() {
 	extraCommands = append(extraCommands, createKubeCommand)
 	extraHelp = append(extraHelp, `
 Create a Kubernetes context:
-$ docker context create kubernetes CONTEXT [flags]
-(see docker context create kubernetes --help)
+$ docker context create k8s CONTEXT [flags]
+(see docker context create k8s --help)
 `)
 }
 
@@ -45,18 +45,13 @@ func createKubeCommand() *cobra.Command {
 		Short: "Create context for a Kubernetes Cluster",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			opts.Name = args[0]
-
-			if opts.Endpoint != "" {
-				opts.FromEnvironment = false
-			}
 			return runCreateKube(cmd.Context(), args[0], opts)
 		},
 	}
 
 	addDescriptionFlag(cmd, &opts.Description)
-	cmd.Flags().StringVar(&opts.Endpoint, "endpoint", "", "The endpoint of the Kubernetes manager")
-	cmd.Flags().BoolVar(&opts.FromEnvironment, "from-env", true, "Get endpoint and creds from env vars")
+	cmd.Flags().StringVar(&opts.KubeconfigPath, "kubeconfig", "", "The endpoint of the Kubernetes manager")
+	cmd.Flags().BoolVar(&opts.FromEnvironment, "from-env", false, "Get endpoint and creds from env vars")
 	return cmd
 }
 
@@ -65,13 +60,9 @@ func runCreateKube(ctx context.Context, contextName string, opts kube.ContextPar
 		return errors.Wrapf(errdefs.ErrAlreadyExists, "context %q", contextName)
 	}
 
-	contextData, description := createContextData(opts)
+	contextData, description, err := opts.CreateContextData()
+	if err != nil {
+		return err
+	}
 	return createDockerContext(ctx, contextName, store.KubeContextType, description, contextData)
 }
-
-func createContextData(opts kube.ContextParams) (interface{}, string) {
-	return store.KubeContext{
-		Endpoint:        opts.Endpoint,
-		FromEnvironment: opts.FromEnvironment,
-	}, opts.Description
-}

+ 1 - 0
go.mod

@@ -64,6 +64,7 @@ require (
 	helm.sh/helm/v3 v3.5.0
 	k8s.io/api v0.20.1
 	k8s.io/apimachinery v0.20.1
+	k8s.io/client-go v0.20.1
 	sigs.k8s.io/kustomize/kyaml v0.10.5
 )
 

+ 50 - 0
kube/charts/kubernetes/context.go

@@ -0,0 +1,50 @@
+// +build kube
+
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   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 kubernetes
+
+import (
+	"fmt"
+	"os"
+
+	"k8s.io/client-go/tools/clientcmd"
+)
+
+func ListAvailableKubeConfigContexts(kubeconfig string) ([]string, error) {
+	config, err := clientcmd.NewDefaultPathOptions().GetStartingConfig()
+	if err != nil {
+		return nil, err
+	}
+	if kubeconfig != "" {
+		f, err := os.Stat(kubeconfig)
+		if os.IsNotExist(err) {
+			return nil, err
+		}
+		if f.IsDir() {
+			return nil, fmt.Errorf("%s not a config file", kubeconfig)
+		}
+
+		config = clientcmd.GetConfigFromFileOrDie(kubeconfig)
+	}
+
+	contexts := []string{}
+	for k := range config.Contexts {
+		contexts = append(contexts, k)
+	}
+	return contexts, nil
+}

+ 76 - 2
kube/context.go

@@ -18,10 +18,84 @@
 
 package kube
 
+import (
+	"github.com/AlecAivazis/survey/v2/terminal"
+	"github.com/docker/compose-cli/api/context/store"
+	"github.com/docker/compose-cli/api/errdefs"
+	"github.com/docker/compose-cli/utils/prompt"
+
+	"github.com/docker/compose-cli/kube/charts/kubernetes"
+)
+
 // ContextParams options for creating a Kubernetes context
 type ContextParams struct {
-	Name            string
+	ContextName     string
 	Description     string
-	Endpoint        string
+	KubeconfigPath  string
 	FromEnvironment bool
 }
+
+func (cp ContextParams) CreateContextData() (interface{}, string, error) {
+	if cp.FromEnvironment {
+		// we use the current kubectl context from a $KUBECONFIG path
+		return store.KubeContext{
+			FromEnvironment: cp.FromEnvironment,
+		}, cp.Description, nil
+	}
+	user := prompt.User{}
+	selectContext := func() error {
+		contexts, err := kubernetes.ListAvailableKubeConfigContexts(cp.KubeconfigPath)
+		if err != nil {
+			return err
+		}
+
+		selected, err := user.Select("Select kubeconfig context", contexts)
+		if err != nil {
+			if err == terminal.InterruptErr {
+				return errdefs.ErrCanceled
+			}
+			return err
+		}
+		cp.ContextName = contexts[selected]
+		return nil
+	}
+
+	if cp.KubeconfigPath != "" {
+		err := selectContext()
+		if err != nil {
+			return nil, "", err
+		}
+	} else {
+
+		// interactive
+		var options []string
+		var actions []func() error
+
+		options = append(options, "Context from kubeconfig file")
+		actions = append(actions, selectContext)
+
+		options = append(options, "Kubernetes environment variables")
+		actions = append(actions, func() error {
+			cp.FromEnvironment = true
+			return nil
+		})
+
+		selected, err := user.Select("Create a Docker context using:", options)
+		if err != nil {
+			if err == terminal.InterruptErr {
+				return nil, "", errdefs.ErrCanceled
+			}
+			return nil, "", err
+		}
+
+		err = actions[selected]()
+		if err != nil {
+			return nil, "", err
+		}
+	}
+	return store.KubeContext{
+		ContextName:     cp.ContextName,
+		KubeconfigPath:  cp.KubeconfigPath,
+		FromEnvironment: cp.FromEnvironment,
+	}, cp.Description, nil
+}