From 7ec7147ce18fb5cd0656b1a9c77a661fcfcc636c Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Thu, 18 Jun 2020 14:36:22 +0200 Subject: [PATCH 1/9] use homedir --- cmd/get/getKubeconfig.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cmd/get/getKubeconfig.go b/cmd/get/getKubeconfig.go index 8865c217..6fde615d 100644 --- a/cmd/get/getKubeconfig.go +++ b/cmd/get/getKubeconfig.go @@ -31,6 +31,7 @@ import ( k3d "github.com/rancher/k3d/v3/pkg/types" "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" + homedir "github.com/mitchellh/go-homedir" log "github.com/sirupsen/logrus" ) @@ -38,6 +39,7 @@ import ( type getKubeconfigFlags struct { all bool output string + targetDefault bool } // NewCmdGetKubeconfig returns a new cobra command @@ -63,6 +65,10 @@ func NewCmdGetKubeconfig() *cobra.Command { var clusters []*k3d.Cluster var err error + if getKubeconfigFlags.targetDefault && getKubeconfigFlags.output != "" { + log.Fatalln("Cannot use both '--output' and '--default' at the same time") + } + // generate list of clusters if getKubeconfigFlags.all { clusters, err = cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) @@ -83,6 +89,13 @@ func NewCmdGetKubeconfig() *cobra.Command { errorGettingKubeconfig := false for _, c := range clusters { log.Debugf("Getting kubeconfig for cluster '%s'", c.Name) + output := getKubeconfigFlags.output + if output == "" && !getKubeconfigFlags.targetDefault { + output, err = util.GetConfigDirOrCreate() + if err != nil { + log.Fatalln(err) + } + } if getKubeconfigFlags.output, err = cluster.GetAndWriteKubeConfig(cmd.Context(), runtimes.SelectedRuntime, c, getKubeconfigFlags.output, &writeKubeConfigOptions); err != nil { log.Errorln(err) errorGettingKubeconfig = true @@ -106,8 +119,9 @@ func NewCmdGetKubeconfig() *cobra.Command { if err := cmd.MarkFlagFilename("output"); err != nil { log.Fatalln("Failed to mark flag --output as filename") } + cmd.Flags()BoolVarP(&getgetKubeconfigFlags.targetDefault, "default-kubeconfig", "d", false, fmt.Sprintf("Update the default kubeconfig ($KUBECONFIG or %s", clientcmd.RecommendedHomeFile) cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateExisting, "update", "u", true, "Update conflicting fields in existing KubeConfig") - cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateCurrentContext, "switch", "s", false, "Switch to new context") + cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateCurrentContext, "switch", "s", true, "Switch to new context") cmd.Flags().BoolVar(&writeKubeConfigOptions.OverwriteExisting, "overwrite", false, "[Careful!] Overwrite existing file, ignoring its contents") cmd.Flags().BoolVarP(&getKubeconfigFlags.all, "all", "a", false, "Get kubeconfigs from all existing clusters") From c00f8952ba60ada5d934b5ba8f1718c065ee0f61 Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Thu, 18 Jun 2020 17:15:45 +0200 Subject: [PATCH 2/9] getKubeconfig: write to new file by default --- cmd/create/createCluster.go | 14 ++++++------- cmd/delete/deleteCluster.go | 17 +++++++++++++++- cmd/get/getKubeconfig.go | 28 +++++++++++++++++--------- docs/index.md | 2 +- docs/internals/defaults.md | 20 +++++++++--------- docs/usage/commands.md | 5 +++-- docs/usage/guides/exposing_services.md | 2 +- docs/usage/kubeconfig.md | 22 ++++++++++++-------- tests/common.sh | 4 ++-- 9 files changed, 72 insertions(+), 42 deletions(-) diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index 46b48d6d..a2ae9bb7 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -51,7 +51,7 @@ Every cluster will consist of at least 2 containers: func NewCmdCreateCluster() *cobra.Command { createClusterOpts := &k3d.CreateClusterOpts{} - var updateKubeconfig, updateCurrentContext bool + var updateDefaultKubeconfig, updateCurrentContext bool // create new command cmd := &cobra.Command{ @@ -69,8 +69,8 @@ func NewCmdCreateCluster() *cobra.Command { } // create cluster - if updateKubeconfig || updateCurrentContext { - log.Debugln("'--update-kubeconfig set: enabling wait-for-master") + if updateDefaultKubeconfig || updateCurrentContext { + log.Debugln("'--update-default-kubeconfig set: enabling wait-for-master") cluster.CreateClusterOpts.WaitForMaster = true } if err := k3dCluster.CreateCluster(cmd.Context(), runtimes.SelectedRuntime, cluster); err != nil { @@ -85,7 +85,7 @@ func NewCmdCreateCluster() *cobra.Command { } log.Infof("Cluster '%s' created successfully!", cluster.Name) - if updateKubeconfig || updateCurrentContext { + if updateDefaultKubeconfig || updateCurrentContext { log.Debugf("Updating default kubeconfig with a new context for cluster %s", cluster.Name) if _, err := k3dCluster.GetAndWriteKubeConfig(cmd.Context(), runtimes.SelectedRuntime, cluster, "", &k3dCluster.WriteKubeConfigOptions{UpdateExisting: true, OverwriteExisting: false, UpdateCurrentContext: updateCurrentContext}); err != nil { log.Fatalln(err) @@ -94,7 +94,7 @@ func NewCmdCreateCluster() *cobra.Command { // print information on how to use the cluster with kubectl log.Infoln("You can now use it like this:") - if updateKubeconfig && !updateCurrentContext { + if updateDefaultKubeconfig && !updateCurrentContext { fmt.Printf("kubectl config use-context %s\n", fmt.Sprintf("%s-%s", k3d.DefaultObjectNamePrefix, cluster.Name)) } else if !updateCurrentContext { if runtime.GOOS == "windows" { @@ -120,8 +120,8 @@ func NewCmdCreateCluster() *cobra.Command { cmd.Flags().StringArrayP("port", "p", nil, "Map ports from the node containers to the host (Format: `[HOST:][HOSTPORT:]CONTAINERPORT[/PROTOCOL][@NODEFILTER]`)\n - Example: `k3d create -w 2 -p 8080:80@worker[0] -p 8081@worker[1]`") cmd.Flags().BoolVar(&createClusterOpts.WaitForMaster, "wait", true, "Wait for the master(s) to be ready before returning. Use '--timeout DURATION' to not wait forever.") cmd.Flags().DurationVar(&createClusterOpts.Timeout, "timeout", 0*time.Second, "Rollback changes if cluster couldn't be created in specified duration.") - cmd.Flags().BoolVar(&updateKubeconfig, "update-kubeconfig", false, "Directly update the default kubeconfig with the new cluster's context") - cmd.Flags().BoolVar(&updateCurrentContext, "switch", false, "Directly switch the default kubeconfig's current-context to the new cluster's context (implies --update-kubeconfig)") + cmd.Flags().BoolVar(&updateDefaultKubeconfig, "update-default-kubeconfig", false, "Directly update the default kubeconfig with the new cluster's context") + cmd.Flags().BoolVar(&updateCurrentContext, "switch", false, "Directly switch the default kubeconfig's current-context to the new cluster's context (implies --update-default-kubeconfig)") cmd.Flags().BoolVar(&createClusterOpts.DisableLoadBalancer, "no-lb", false, "Disable the creation of a LoadBalancer in front of the master nodes") /* Image Importing */ diff --git a/cmd/delete/deleteCluster.go b/cmd/delete/deleteCluster.go index ed7bb73f..167ce2fd 100644 --- a/cmd/delete/deleteCluster.go +++ b/cmd/delete/deleteCluster.go @@ -22,10 +22,15 @@ THE SOFTWARE. package delete import ( + "fmt" + "os" + "path" + "github.com/rancher/k3d/v3/cmd/util" "github.com/rancher/k3d/v3/pkg/cluster" "github.com/rancher/k3d/v3/pkg/runtimes" k3d "github.com/rancher/k3d/v3/pkg/types" + k3dutil "github.com/rancher/k3d/v3/pkg/util" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -51,11 +56,21 @@ func NewCmdDeleteCluster() *cobra.Command { if err := cluster.DeleteCluster(cmd.Context(), runtimes.SelectedRuntime, c); err != nil { log.Fatalln(err) } - log.Infoln("Removing cluster details from default kubeconfig") + log.Infoln("Removing cluster details from default kubeconfig...") if err := cluster.RemoveClusterFromDefaultKubeConfig(cmd.Context(), c); err != nil { log.Warnln("Failed to remove cluster details from default kubeconfig") log.Warnln(err) } + log.Infoln("Removing standalone kubeconfig file (if there is one)...") + configDir, err := k3dutil.GetConfigDirOrCreate() + if err != nil { + log.Warnf("Failed to delete kubeconfig file: %+v", err) + } else { + kubeconfigfile := path.Join(configDir, fmt.Sprintf("kubeconfig-%s.yaml", c.Name)) + if err := os.Remove(kubeconfigfile); err != nil { + log.Warnf("Failed to delete kubeconfig file '%s'", kubeconfigfile) + } + } log.Infof("Successfully deleted cluster %s!", c.Name) } diff --git a/cmd/get/getKubeconfig.go b/cmd/get/getKubeconfig.go index 6fde615d..8d96f86f 100644 --- a/cmd/get/getKubeconfig.go +++ b/cmd/get/getKubeconfig.go @@ -24,21 +24,23 @@ package get import ( "fmt" "os" + "path" + "strings" "github.com/rancher/k3d/v3/cmd/util" "github.com/rancher/k3d/v3/pkg/cluster" "github.com/rancher/k3d/v3/pkg/runtimes" k3d "github.com/rancher/k3d/v3/pkg/types" + k3dutil "github.com/rancher/k3d/v3/pkg/util" "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" - homedir "github.com/mitchellh/go-homedir" log "github.com/sirupsen/logrus" ) type getKubeconfigFlags struct { - all bool - output string + all bool + output string targetDefault bool } @@ -87,24 +89,30 @@ func NewCmdGetKubeconfig() *cobra.Command { // get kubeconfigs from all clusters errorGettingKubeconfig := false + var outputs []string + outputDir, err := k3dutil.GetConfigDirOrCreate() + if err != nil { + log.Errorln(err) + log.Fatalln("Failed to save kubeconfig to local directory") + } for _, c := range clusters { log.Debugf("Getting kubeconfig for cluster '%s'", c.Name) output := getKubeconfigFlags.output if output == "" && !getKubeconfigFlags.targetDefault { - output, err = util.GetConfigDirOrCreate() - if err != nil { - log.Fatalln(err) - } + output = path.Join(outputDir, fmt.Sprintf("kubeconfig-%s.yaml", c.Name)) } - if getKubeconfigFlags.output, err = cluster.GetAndWriteKubeConfig(cmd.Context(), runtimes.SelectedRuntime, c, getKubeconfigFlags.output, &writeKubeConfigOptions); err != nil { + output, err = cluster.GetAndWriteKubeConfig(cmd.Context(), runtimes.SelectedRuntime, c, output, &writeKubeConfigOptions) + if err != nil { log.Errorln(err) errorGettingKubeconfig = true + } else { + outputs = append(outputs, output) } } // only print kubeconfig file path if output is not stdout ("-") if getKubeconfigFlags.output != "-" { - fmt.Println(getKubeconfigFlags.output) + fmt.Println(strings.Join(outputs, ":")) } // return with non-zero exit code, if there was an error for one of the clusters @@ -119,7 +127,7 @@ func NewCmdGetKubeconfig() *cobra.Command { if err := cmd.MarkFlagFilename("output"); err != nil { log.Fatalln("Failed to mark flag --output as filename") } - cmd.Flags()BoolVarP(&getgetKubeconfigFlags.targetDefault, "default-kubeconfig", "d", false, fmt.Sprintf("Update the default kubeconfig ($KUBECONFIG or %s", clientcmd.RecommendedHomeFile) + cmd.Flags().BoolVarP(&getKubeconfigFlags.targetDefault, "default-kubeconfig", "d", false, fmt.Sprintf("Update the default kubeconfig ($KUBECONFIG or %s)", clientcmd.RecommendedHomeFile)) cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateExisting, "update", "u", true, "Update conflicting fields in existing KubeConfig") cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateCurrentContext, "switch", "s", true, "Switch to new context") cmd.Flags().BoolVar(&writeKubeConfigOptions.OverwriteExisting, "overwrite", false, "[Careful!] Overwrite existing file, ignoring its contents") diff --git a/docs/index.md b/docs/index.md index 257a1107..12957750 100644 --- a/docs/index.md +++ b/docs/index.md @@ -67,4 +67,4 @@ kubectl get nodes ## Related Projects -* [k3x](https://github.com/inercia/k3x): a graphics interface (for Linux) to k3d. +- [k3x](https://github.com/inercia/k3x): a graphics interface (for Linux) to k3d. diff --git a/docs/internals/defaults.md b/docs/internals/defaults.md index 482b18ca..eb72e124 100644 --- a/docs/internals/defaults.md +++ b/docs/internals/defaults.md @@ -1,12 +1,12 @@ # Defaults -* multiple master nodes - * by default, when `--master` > 1 and no `--datastore-x` option is set, the first master node (master-0) will be the initializing master node - * the initializing master node will have the `--cluster-init` flag appended - * all other master nodes will refer to the initializing master node via `--server https://:6443` -* API-Ports - * by default, we don't expose any API-Port (no host port mapping) -* kubeconfig - * if no output is set explicitly (via the `--output` flag), we use the default loading rules to get the default kubeconfig: - * First: kubeconfig specified via the KUBECONFIG environment variable (error out if multiple are specified) - * Second: default kubeconfig in home directory (e.g. `$HOME/.kube/config`) +- multiple master nodes + - by default, when `--master` > 1 and no `--datastore-x` option is set, the first master node (master-0) will be the initializing master node + - the initializing master node will have the `--cluster-init` flag appended + - all other master nodes will refer to the initializing master node via `--server https://:6443` +- API-Ports + - by default, we don't expose any API-Port (no host port mapping) +- kubeconfig + - if `--default` is set, we use the default loading rules to get the default kubeconfig: + - First: kubeconfig specified via the KUBECONFIG environment variable (error out if multiple are specified) + - Second: default kubeconfig in home directory (e.g. `$HOME/.kube/config`) diff --git a/docs/usage/commands.md b/docs/usage/commands.md index 495092c0..7f791b50 100644 --- a/docs/usage/commands.md +++ b/docs/usage/commands.md @@ -16,8 +16,8 @@ k3d -p, --port # add some more port mappings --token # specify a cluster token (default: auto-generated) --timeout # specify a timeout, after which the cluster creation will be interrupted and changes rolled back - --update-kubeconfig # enable the automated update of the default kubeconfig with the details of the newly created cluster (also sets '--wait=true') - --switch # (implies --update-kubeconfig) automatically sets the current-context of your default kubeconfig to the new cluster's context + --update-default-kubeconfig # enable the automated update of the default kubeconfig with the details of the newly created cluster (also sets '--wait=true') + --switch # (implies --update-default-kubeconfig) automatically sets the current-context of your default kubeconfig to the new cluster's context -v, --volume # specify additional bind-mounts --wait # enable waiting for all master nodes to be ready before returning -w, --workers # specify how many worker nodes you want to create @@ -55,6 +55,7 @@ k3d --overwrite # [Careful!] forcefully overwrite the output file, ignoring existing contents -s, --switch # switch current-context in kubeconfig to the new context -u, --update # update conflicting fields in existing kubeconfig (default: true) + -d, --default # update the default kubeconfig (usually $KUBECONFIG or $HOME/.kube/config) load image [IMAGE | ARCHIVE [IMAGE | ARCHIVE ...]] # Load one or more images from the local runtime environment or tar-archives into k3d clusters -c, --cluster # clusters to load the image into diff --git a/docs/usage/guides/exposing_services.md b/docs/usage/guides/exposing_services.md index 88366f2e..88993d2b 100644 --- a/docs/usage/guides/exposing_services.md +++ b/docs/usage/guides/exposing_services.md @@ -18,7 +18,7 @@ Therefore, we have to create the cluster in a way, that the internal port 80 (wh 2. Get the kubeconfig file - `#!bash export KUBECONFIG="$(k3d get-kubeconfig --name='k3s-default')"` + `#!bash export KUBECONFIG="$(k3d get kubeconfig k3s-default)"` 3. Create a nginx deployment diff --git a/docs/usage/kubeconfig.md b/docs/usage/kubeconfig.md index 76b4ee3b..932af3b8 100644 --- a/docs/usage/kubeconfig.md +++ b/docs/usage/kubeconfig.md @@ -11,28 +11,34 @@ To get a kubeconfig set up for you to connect to a k3d cluster, you can go diffe ## Getting the kubeconfig for a newly created cluster -1. Update your default kubeconfig **upon** cluster creation - - `#!bash k3d create cluster mycluster --update-kubeconfig` - - *Note:* this won't switch the current-context -2. Update your default kubeconfig **after** cluster creation +1. Create a new kubeconfig file **after** cluster creation - `#!bash k3d get kubeconfig mycluster` - - *Note:* this won't switch the current-context -3. Update a different kubeconfig **after** cluster creation + - *Note:* this will create (or update) the file `$HOME/.k3d/kubeconfig-mycluster.yaml` + - *Tip:* pipe this: `export KUBECONFIG?$(k3d get kubeconfig mycluster)` +2. Update your default kubeconfig **upon** cluster creation + - `#!bash k3d create cluster mycluster --update-kubeconfig` + - *Note:* this won't switch the current-context (append `--switch` to do so) +3. Update your default kubeconfig **after** cluster creation + - `#!bash k3d get kubeconfig mycluster --default` + - *Note:* this won't switch the current-context (append `--switch` to do so) +4. Update a different kubeconfig **after** cluster creation - `#!bash k3d get kubeconfig mycluster --output some/other/file.yaml` - *Note:* this won't switch the current-context - The file will be created if it doesn't exist !!! info "Switching the current context" - None of the above options switch the current-context. + None of the above options switch the current-context by default. This is intended to be least intrusive, since the current-context has a global effect. You can switch the current-context directly with the `get kubeconfig` command by adding the `--switch` flag. ## Removing cluster details from the kubeconfig `#!bash k3d delete cluster mycluster` will always remove the details for `mycluster` from the default kubeconfig. +It will also delete the respective kubeconfig file in `$HOME/.k3d/` if it exists. ## Handling multiple clusters `k3d get kubeconfig` let's you specify one or more clusters via arguments _or_ all via `--all`. -All kubeconfigs will then be merged into a single file, which is either the default kubeconfig or the kubeconfig specified via `--output FILE`. +All kubeconfigs will then be merged into a single file if `--default` or `--output` is specified. +If none of those two flags was specified, a new file will be created per cluster and the merged path (e.g. `$HOME/.k3d/kubeconfig-cluster1.yaml:$HOME/.k3d/cluster2.yaml`) will be returned. Note, that with multiple cluster specified, the `--switch` flag will change the current context to the cluster which was last in the list. diff --git a/tests/common.sh b/tests/common.sh index 42b180f4..372fbc23 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -61,7 +61,7 @@ check_url() { check_clusters() { [ -n "$EXE" ] || abort "EXE is not defined" for c in "$@" ; do - $EXE get kubeconfig "$c" --switch + $EXE get kubeconfig "$c" --default-kubeconfig --switch if kubectl cluster-info ; then passed "cluster $c is reachable" else @@ -87,7 +87,7 @@ check_cluster_count() { check_multi_node() { cluster=$1 expectedNodeCount=$2 - $EXE get kubeconfig "$cluster" --switch + $EXE get kubeconfig "$cluster" --default-kubeconfig --switch nodeCount=$(kubectl get nodes -o=custom-columns=NAME:.metadata.name --no-headers | wc -l) if [[ $nodeCount == $expectedNodeCount ]]; then passed "cluster $cluster has $expectedNodeCount nodes, as expected" From 846aefeb40d0924f19ec8fc9d9f52b323d546780 Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Mon, 22 Jun 2020 08:01:39 +0200 Subject: [PATCH 3/9] kubeconfig: improved flag names and bug fixes --- cmd/create/createCluster.go | 4 ++-- cmd/get/getKubeconfig.go | 8 ++++---- docs/internals/defaults.md | 2 +- docs/usage/commands.md | 6 +++--- docs/usage/kubeconfig.md | 14 +++++++------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index a2ae9bb7..266bb641 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -88,7 +88,7 @@ func NewCmdCreateCluster() *cobra.Command { if updateDefaultKubeconfig || updateCurrentContext { log.Debugf("Updating default kubeconfig with a new context for cluster %s", cluster.Name) if _, err := k3dCluster.GetAndWriteKubeConfig(cmd.Context(), runtimes.SelectedRuntime, cluster, "", &k3dCluster.WriteKubeConfigOptions{UpdateExisting: true, OverwriteExisting: false, UpdateCurrentContext: updateCurrentContext}); err != nil { - log.Fatalln(err) + log.Warningln(err) } } @@ -121,7 +121,7 @@ func NewCmdCreateCluster() *cobra.Command { cmd.Flags().BoolVar(&createClusterOpts.WaitForMaster, "wait", true, "Wait for the master(s) to be ready before returning. Use '--timeout DURATION' to not wait forever.") cmd.Flags().DurationVar(&createClusterOpts.Timeout, "timeout", 0*time.Second, "Rollback changes if cluster couldn't be created in specified duration.") cmd.Flags().BoolVar(&updateDefaultKubeconfig, "update-default-kubeconfig", false, "Directly update the default kubeconfig with the new cluster's context") - cmd.Flags().BoolVar(&updateCurrentContext, "switch", false, "Directly switch the default kubeconfig's current-context to the new cluster's context (implies --update-default-kubeconfig)") + cmd.Flags().BoolVar(&updateCurrentContext, "switch-context", false, "Directly switch the default kubeconfig's current-context to the new cluster's context (implies --update-default-kubeconfig)") cmd.Flags().BoolVar(&createClusterOpts.DisableLoadBalancer, "no-lb", false, "Disable the creation of a LoadBalancer in front of the master nodes") /* Image Importing */ diff --git a/cmd/get/getKubeconfig.go b/cmd/get/getKubeconfig.go index 8d96f86f..a1f88599 100644 --- a/cmd/get/getKubeconfig.go +++ b/cmd/get/getKubeconfig.go @@ -68,7 +68,7 @@ func NewCmdGetKubeconfig() *cobra.Command { var err error if getKubeconfigFlags.targetDefault && getKubeconfigFlags.output != "" { - log.Fatalln("Cannot use both '--output' and '--default' at the same time") + log.Fatalln("Cannot use both '--output' and '--merge-default-kubeconfig' at the same time") } // generate list of clusters @@ -127,9 +127,9 @@ func NewCmdGetKubeconfig() *cobra.Command { if err := cmd.MarkFlagFilename("output"); err != nil { log.Fatalln("Failed to mark flag --output as filename") } - cmd.Flags().BoolVarP(&getKubeconfigFlags.targetDefault, "default-kubeconfig", "d", false, fmt.Sprintf("Update the default kubeconfig ($KUBECONFIG or %s)", clientcmd.RecommendedHomeFile)) - cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateExisting, "update", "u", true, "Update conflicting fields in existing KubeConfig") - cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateCurrentContext, "switch", "s", true, "Switch to new context") + cmd.Flags().BoolVarP(&getKubeconfigFlags.targetDefault, "merge-default-kubeconfig", "d", false, fmt.Sprintf("Merge into the default kubeconfig ($KUBECONFIG or %s)", clientcmd.RecommendedHomeFile)) + cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateExisting, "update", "u", true, "Update conflicting fields in existing kubeconfig") + cmd.Flags().BoolVarP(&writeKubeConfigOptions.UpdateCurrentContext, "switch-context", "s", true, "Switch to new context") cmd.Flags().BoolVar(&writeKubeConfigOptions.OverwriteExisting, "overwrite", false, "[Careful!] Overwrite existing file, ignoring its contents") cmd.Flags().BoolVarP(&getKubeconfigFlags.all, "all", "a", false, "Get kubeconfigs from all existing clusters") diff --git a/docs/internals/defaults.md b/docs/internals/defaults.md index eb72e124..ca6221bc 100644 --- a/docs/internals/defaults.md +++ b/docs/internals/defaults.md @@ -7,6 +7,6 @@ - API-Ports - by default, we don't expose any API-Port (no host port mapping) - kubeconfig - - if `--default` is set, we use the default loading rules to get the default kubeconfig: + - if `--[update|merge]-default-kubeconfig` is set, we use the default loading rules to get the default kubeconfig: - First: kubeconfig specified via the KUBECONFIG environment variable (error out if multiple are specified) - Second: default kubeconfig in home directory (e.g. `$HOME/.kube/config`) diff --git a/docs/usage/commands.md b/docs/usage/commands.md index 7f791b50..a5a49ce4 100644 --- a/docs/usage/commands.md +++ b/docs/usage/commands.md @@ -17,7 +17,7 @@ k3d --token # specify a cluster token (default: auto-generated) --timeout # specify a timeout, after which the cluster creation will be interrupted and changes rolled back --update-default-kubeconfig # enable the automated update of the default kubeconfig with the details of the newly created cluster (also sets '--wait=true') - --switch # (implies --update-default-kubeconfig) automatically sets the current-context of your default kubeconfig to the new cluster's context + --switch-context # (implies --update-default-kubeconfig) automatically sets the current-context of your default kubeconfig to the new cluster's context -v, --volume # specify additional bind-mounts --wait # enable waiting for all master nodes to be ready before returning -w, --workers # specify how many worker nodes you want to create @@ -53,9 +53,9 @@ k3d -a, --all # get kubeconfigs from all clusters --output # specify the output file where the kubeconfig should be written to --overwrite # [Careful!] forcefully overwrite the output file, ignoring existing contents - -s, --switch # switch current-context in kubeconfig to the new context + -s, --switch-context # switch current-context in kubeconfig to the new context -u, --update # update conflicting fields in existing kubeconfig (default: true) - -d, --default # update the default kubeconfig (usually $KUBECONFIG or $HOME/.kube/config) + -d, --merge-default-kubeconfig # update the default kubeconfig (usually $KUBECONFIG or $HOME/.kube/config) load image [IMAGE | ARCHIVE [IMAGE | ARCHIVE ...]] # Load one or more images from the local runtime environment or tar-archives into k3d clusters -c, --cluster # clusters to load the image into diff --git a/docs/usage/kubeconfig.md b/docs/usage/kubeconfig.md index 932af3b8..f45670c8 100644 --- a/docs/usage/kubeconfig.md +++ b/docs/usage/kubeconfig.md @@ -14,13 +14,13 @@ To get a kubeconfig set up for you to connect to a k3d cluster, you can go diffe 1. Create a new kubeconfig file **after** cluster creation - `#!bash k3d get kubeconfig mycluster` - *Note:* this will create (or update) the file `$HOME/.k3d/kubeconfig-mycluster.yaml` - - *Tip:* pipe this: `export KUBECONFIG?$(k3d get kubeconfig mycluster)` + - *Tip:* Use it: `#!bash export KUBECONFIG=$(k3d get kubeconfig mycluster)` 2. Update your default kubeconfig **upon** cluster creation - `#!bash k3d create cluster mycluster --update-kubeconfig` - - *Note:* this won't switch the current-context (append `--switch` to do so) + - *Note:* this won't switch the current-context (append `--switch-context` to do so) 3. Update your default kubeconfig **after** cluster creation - - `#!bash k3d get kubeconfig mycluster --default` - - *Note:* this won't switch the current-context (append `--switch` to do so) + - `#!bash k3d get kubeconfig mycluster --merge-default-kubeconfig` + - *Note:* this won't switch the current-context (append `--switch-context` to do so) 4. Update a different kubeconfig **after** cluster creation - `#!bash k3d get kubeconfig mycluster --output some/other/file.yaml` - *Note:* this won't switch the current-context @@ -29,7 +29,7 @@ To get a kubeconfig set up for you to connect to a k3d cluster, you can go diffe !!! info "Switching the current context" None of the above options switch the current-context by default. This is intended to be least intrusive, since the current-context has a global effect. - You can switch the current-context directly with the `get kubeconfig` command by adding the `--switch` flag. + You can switch the current-context directly with the `get kubeconfig` command by adding the `--switch-context` flag. ## Removing cluster details from the kubeconfig @@ -39,6 +39,6 @@ It will also delete the respective kubeconfig file in `$HOME/.k3d/` if it exists ## Handling multiple clusters `k3d get kubeconfig` let's you specify one or more clusters via arguments _or_ all via `--all`. -All kubeconfigs will then be merged into a single file if `--default` or `--output` is specified. +All kubeconfigs will then be merged into a single file if `--merge-default-kubeconfig` or `--output` is specified. If none of those two flags was specified, a new file will be created per cluster and the merged path (e.g. `$HOME/.k3d/kubeconfig-cluster1.yaml:$HOME/.k3d/cluster2.yaml`) will be returned. -Note, that with multiple cluster specified, the `--switch` flag will change the current context to the cluster which was last in the list. +Note, that with multiple cluster specified, the `--switch-context` flag will change the current context to the cluster which was last in the list. From 7325cc41c42caefac5e0ceb13cff2d100f7200cd Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Mon, 22 Jun 2020 08:15:35 +0200 Subject: [PATCH 4/9] tests/e2e: update flags for 'get kubeconfig' --- tests/common.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common.sh b/tests/common.sh index 372fbc23..8b363a58 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -61,7 +61,7 @@ check_url() { check_clusters() { [ -n "$EXE" ] || abort "EXE is not defined" for c in "$@" ; do - $EXE get kubeconfig "$c" --default-kubeconfig --switch + $EXE get kubeconfig "$c" --merge-default-kubeconfig --switch-context if kubectl cluster-info ; then passed "cluster $c is reachable" else @@ -87,7 +87,7 @@ check_cluster_count() { check_multi_node() { cluster=$1 expectedNodeCount=$2 - $EXE get kubeconfig "$cluster" --default-kubeconfig --switch + $EXE get kubeconfig "$cluster" --merge-default-kubeconfig --switch-context nodeCount=$(kubectl get nodes -o=custom-columns=NAME:.metadata.name --no-headers | wc -l) if [[ $nodeCount == $expectedNodeCount ]]; then passed "cluster $cluster has $expectedNodeCount nodes, as expected" From 40a2d032ab77d9892a81b44475907ed6c7825ce6 Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Mon, 22 Jun 2020 08:21:35 +0200 Subject: [PATCH 5/9] deleteCluster: only warn about failed kubeconfig deletion if file exists and couldn't be deleted --- cmd/delete/deleteCluster.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/delete/deleteCluster.go b/cmd/delete/deleteCluster.go index 167ce2fd..fb97e50e 100644 --- a/cmd/delete/deleteCluster.go +++ b/cmd/delete/deleteCluster.go @@ -68,7 +68,9 @@ func NewCmdDeleteCluster() *cobra.Command { } else { kubeconfigfile := path.Join(configDir, fmt.Sprintf("kubeconfig-%s.yaml", c.Name)) if err := os.Remove(kubeconfigfile); err != nil { - log.Warnf("Failed to delete kubeconfig file '%s'", kubeconfigfile) + if !os.IsNotExist(err) { + log.Warnf("Failed to delete kubeconfig file '%s'", kubeconfigfile) + } } } From 18bc8b89db05cedff5133f0cd4a5d2e91cca6ce4 Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Wed, 8 Jul 2020 15:17:58 +0200 Subject: [PATCH 6/9] createCluster: update default kubeconfig and switch context to new cluster by default upon cluster creation --- cmd/create/createCluster.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index 266bb641..2dbd5177 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -120,8 +120,8 @@ func NewCmdCreateCluster() *cobra.Command { cmd.Flags().StringArrayP("port", "p", nil, "Map ports from the node containers to the host (Format: `[HOST:][HOSTPORT:]CONTAINERPORT[/PROTOCOL][@NODEFILTER]`)\n - Example: `k3d create -w 2 -p 8080:80@worker[0] -p 8081@worker[1]`") cmd.Flags().BoolVar(&createClusterOpts.WaitForMaster, "wait", true, "Wait for the master(s) to be ready before returning. Use '--timeout DURATION' to not wait forever.") cmd.Flags().DurationVar(&createClusterOpts.Timeout, "timeout", 0*time.Second, "Rollback changes if cluster couldn't be created in specified duration.") - cmd.Flags().BoolVar(&updateDefaultKubeconfig, "update-default-kubeconfig", false, "Directly update the default kubeconfig with the new cluster's context") - cmd.Flags().BoolVar(&updateCurrentContext, "switch-context", false, "Directly switch the default kubeconfig's current-context to the new cluster's context (implies --update-default-kubeconfig)") + cmd.Flags().BoolVar(&updateDefaultKubeconfig, "update-default-kubeconfig", true, "Directly update the default kubeconfig with the new cluster's context") + cmd.Flags().BoolVar(&updateCurrentContext, "switch-context", true, "Directly switch the default kubeconfig's current-context to the new cluster's context (implies --update-default-kubeconfig)") cmd.Flags().BoolVar(&createClusterOpts.DisableLoadBalancer, "no-lb", false, "Disable the creation of a LoadBalancer in front of the master nodes") /* Image Importing */ From 81ba7200f49b1f8cfb6b85e412be80ac6805641e Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Wed, 8 Jul 2020 15:33:47 +0200 Subject: [PATCH 7/9] Sane usage of the default cluster name - use by default whereever clustername is required as an arg (if nArgs is 0) - use by default whereever clustername is expected as flag --- cmd/create/createNode.go | 3 --- cmd/delete/deleteCluster.go | 15 ++++++++------- cmd/get/getKubeconfig.go | 15 ++++++++------- cmd/start/startCluster.go | 13 +++++++------ cmd/stop/stopCluster.go | 13 +++++++------ 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/cmd/create/createNode.go b/cmd/create/createNode.go index 9bdb8e47..1aa2ba8e 100644 --- a/cmd/create/createNode.go +++ b/cmd/create/createNode.go @@ -62,9 +62,6 @@ func NewCmdCreateNode() *cobra.Command { log.Fatalln("Failed to register flag completion for '--role'", err) } cmd.Flags().StringP("cluster", "c", k3d.DefaultClusterName, "Select the cluster that the node shall connect to.") - if err := cmd.MarkFlagRequired("cluster"); err != nil { - log.Fatalln("Failed to mark required flag '--cluster'") - } if err := cmd.RegisterFlagCompletionFunc("cluster", util.ValidArgsAvailableClusters); err != nil { log.Fatalln("Failed to register flag completion for '--cluster'", err) } diff --git a/cmd/delete/deleteCluster.go b/cmd/delete/deleteCluster.go index fb97e50e..97040bd1 100644 --- a/cmd/delete/deleteCluster.go +++ b/cmd/delete/deleteCluster.go @@ -41,10 +41,10 @@ func NewCmdDeleteCluster() *cobra.Command { // create new cobra command cmd := &cobra.Command{ - Use: "cluster (NAME | --all)", - Short: "Delete a cluster.", - Long: `Delete a cluster.`, - Args: cobra.MinimumNArgs(0), // 0 or n arguments; 0 only if --all is set + Use: "cluster [NAME [NAME ...] | --all]", + Short: "Delete cluster(s).", + Long: `Delete cluster(s).`, + Args: cobra.MinimumNArgs(0), // 0 or n arguments; 0 = default cluster name ValidArgsFunction: util.ValidArgsAvailableClusters, Run: func(cmd *cobra.Command, args []string) { clusters := parseDeleteClusterCmd(cmd, args) @@ -106,11 +106,12 @@ func parseDeleteClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { return clusters } - if len(args) < 1 { - log.Fatalln("Expecting at least one cluster name if `--all` is not set") + clusternames := []string{k3d.DefaultClusterName} + if len(args) != 0 { + clusternames = args } - for _, name := range args { + for _, name := range clusternames { cluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: name}) if err != nil { log.Fatalln(err) diff --git a/cmd/get/getKubeconfig.go b/cmd/get/getKubeconfig.go index a1f88599..0c0c2878 100644 --- a/cmd/get/getKubeconfig.go +++ b/cmd/get/getKubeconfig.go @@ -57,12 +57,7 @@ func NewCmdGetKubeconfig() *cobra.Command { Short: "Get kubeconfig", Long: `Get kubeconfig.`, ValidArgsFunction: util.ValidArgsAvailableClusters, - Args: func(cmd *cobra.Command, args []string) error { - if (len(args) < 1 && !getKubeconfigFlags.all) || (len(args) > 0 && getKubeconfigFlags.all) { - return fmt.Errorf("Need to specify one or more cluster names *or* set `--all` flag") - } - return nil - }, + Args: cobra.MinimumNArgs(0), Run: func(cmd *cobra.Command, args []string) { var clusters []*k3d.Cluster var err error @@ -78,7 +73,13 @@ func NewCmdGetKubeconfig() *cobra.Command { log.Fatalln(err) } } else { - for _, clusterName := range args { + + clusternames := []string{k3d.DefaultClusterName} + if len(args) != 0 { + clusternames = args + } + + for _, clusterName := range clusternames { retrievedCluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: clusterName}) if err != nil { log.Fatalln(err) diff --git a/cmd/start/startCluster.go b/cmd/start/startCluster.go index 6c50faba..d5df5f1c 100644 --- a/cmd/start/startCluster.go +++ b/cmd/start/startCluster.go @@ -42,9 +42,9 @@ func NewCmdStartCluster() *cobra.Command { // create new command cmd := &cobra.Command{ - Use: "cluster (NAME [NAME...] | --all)", - Short: "Start an existing k3d cluster", - Long: `Start an existing k3d cluster`, + Use: "cluster [NAME [NAME...] | --all]", + Long: `Start existing k3d cluster(s)`, + Short: "Start existing k3d cluster(s)", ValidArgsFunction: util.ValidArgsAvailableClusters, Run: func(cmd *cobra.Command, args []string) { clusters := parseStartClusterCmd(cmd, args) @@ -86,11 +86,12 @@ func parseStartClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { return clusters } - if len(args) < 1 { - log.Fatalln("Expecting at least one cluster name if `--all` is not set") + clusternames := []string{k3d.DefaultClusterName} + if len(args) != 0 { + clusternames = args } - for _, name := range args { + for _, name := range clusternames { cluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: name}) if err != nil { log.Fatalln(err) diff --git a/cmd/stop/stopCluster.go b/cmd/stop/stopCluster.go index 5a91fb9f..bfa0db90 100644 --- a/cmd/stop/stopCluster.go +++ b/cmd/stop/stopCluster.go @@ -37,9 +37,9 @@ func NewCmdStopCluster() *cobra.Command { // create new command cmd := &cobra.Command{ - Use: "cluster (NAME [NAME...] | --all)", - Short: "Stop an existing k3d cluster", - Long: `Stop an existing k3d cluster.`, + Use: "cluster [NAME [NAME...] | --all]", + Short: "Stop existing k3d cluster(s)", + Long: `Stop existing k3d cluster(s).`, ValidArgsFunction: util.ValidArgsAvailableClusters, Run: func(cmd *cobra.Command, args []string) { clusters := parseStopClusterCmd(cmd, args) @@ -79,11 +79,12 @@ func parseStopClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { return clusters } - if len(args) < 1 { - log.Fatalln("Expecting at least one cluster name if `--all` is not set") + clusternames := []string{k3d.DefaultClusterName} + if len(args) != 0 { + clusternames = args } - for _, name := range args { + for _, name := range clusternames { cluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: name}) if err != nil { log.Fatalln(err) From 846d42b56825eca2b8010d5d81bab19605659725 Mon Sep 17 00:00:00 2001 From: lvuch Date: Fri, 10 Jul 2020 12:08:42 -0700 Subject: [PATCH 8/9] header fix --- docs/static/css/extra.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/static/css/extra.css b/docs/static/css/extra.css index 14676c1e..7720fcf3 100644 --- a/docs/static/css/extra.css +++ b/docs/static/css/extra.css @@ -20,10 +20,14 @@ display: inline; } +.md-header-nav__topic { + position: relative; +} + [data-md-color-primary=black] .md-tabs { background-color: #0DCEFF; } .md-tabs { color: black; -} \ No newline at end of file +} From d7199d7ba57313dba42a11ea64a21e5d3d88b15a Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Tue, 14 Jul 2020 12:37:01 +0200 Subject: [PATCH 9/9] createCluster: fix description --- cmd/create/createCluster.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index 2dbd5177..61d8a6b8 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -42,9 +42,10 @@ import ( const createClusterDescription = ` Create a new k3s cluster with containerized nodes (k3s in docker). -Every cluster will consist of at least 2 containers: - - 1 master node container (k3s) - - 1 loadbalancer container as the entrypoint to the cluster (nginx) +Every cluster will consist of one or more containers: + - 1 (or more) master node container (k3s) + - (optionally) 1 loadbalancer container as the entrypoint to the cluster (nginx) + - (optionally) 1 (or more) worker node containers (k3s) ` // NewCmdCreateCluster returns a new cobra command