From 849707016b189a2298f0000205d6148e3c84281b Mon Sep 17 00:00:00 2001 From: aiordache Date: Fri, 22 Jan 2021 13:41:57 +0100 Subject: [PATCH 1/2] Implement kubernetes context create Signed-off-by: aiordache --- api/context/store/contextmetadata.go | 3 +- api/context/store/store.go | 2 +- cli/cmd/context/create_kube.go | 27 ++++------ go.mod | 1 + kube/charts/kubernetes/context.go | 50 ++++++++++++++++++ kube/context.go | 78 +++++++++++++++++++++++++++- 6 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 kube/charts/kubernetes/context.go diff --git a/api/context/store/contextmetadata.go b/api/context/store/contextmetadata.go index 5ec187320..2ecba7855 100644 --- a/api/context/store/contextmetadata.go +++ b/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 } diff --git a/api/context/store/store.go b/api/context/store/store.go index 9b943a583..ba0135958 100644 --- a/api/context/store/store.go +++ b/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 = "k8s" ) const ( diff --git a/cli/cmd/context/create_kube.go b/cli/cmd/context/create_kube.go index a963002fe..d10bdd82c 100644 --- a/cli/cmd/context/create_kube.go +++ b/cli/cmd/context/create_kube.go @@ -33,30 +33,25 @@ 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) `) } func createKubeCommand() *cobra.Command { var opts kube.ContextParams cmd := &cobra.Command{ - Use: "kubernetes CONTEXT [flags]", + Use: "k8s CONTEXT [flags]", 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 -} diff --git a/go.mod b/go.mod index 50aeb9e05..5369dd4f6 100644 --- a/go.mod +++ b/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 ) diff --git a/kube/charts/kubernetes/context.go b/kube/charts/kubernetes/context.go new file mode 100644 index 000000000..57c6ce059 --- /dev/null +++ b/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 +} diff --git a/kube/context.go b/kube/context.go index a9ce58a74..d094d335b 100644 --- a/kube/context.go +++ b/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 +} From 61a3f6faa58fc822a0fe3fafd3dde9f711855ae0 Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Tue, 26 Jan 2021 16:40:18 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Keep=20=E2=80=9Ckubernetes=E2=80=9D=20in=20?= =?UTF-8?q?`docker=20context=20create=20kubernetes=20mycontext`=20UX.=20We?= =?UTF-8?q?=20cannot=20use=20=E2=80=9Ckubernetes=E2=80=9D=20as=20an=20endp?= =?UTF-8?q?oint=20name,=20because=20the=20context=20Marshall/unmarshall=20?= =?UTF-8?q?has=20a=20specific=20logic=20for=20the=20=E2=80=9Ckubernetes?= =?UTF-8?q?=E2=80=9D=20endpoint,=20so=20storing=20=E2=80=9Ckube=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guillaume Tardif --- api/context/store/store.go | 2 +- cli/cmd/context/create_kube.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/context/store/store.go b/api/context/store/store.go index ba0135958..5427f7529 100644 --- a/api/context/store/store.go +++ b/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 = "k8s" + KubeContextType = "kube" ) const ( diff --git a/cli/cmd/context/create_kube.go b/cli/cmd/context/create_kube.go index d10bdd82c..b42370e9a 100644 --- a/cli/cmd/context/create_kube.go +++ b/cli/cmd/context/create_kube.go @@ -41,7 +41,7 @@ $ docker context create k8s CONTEXT [flags] func createKubeCommand() *cobra.Command { var opts kube.ContextParams cmd := &cobra.Command{ - Use: "k8s CONTEXT [flags]", + Use: "kubernetes CONTEXT [flags]", Short: "Create context for a Kubernetes Cluster", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error {