From a6aa1ff2d2bfb51350a3f7aac6c688f70c006dce Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 1 Jun 2020 21:22:11 +0200 Subject: [PATCH 1/7] :wrench: Update Makefile and .gitignore Add go compiler flag to makefile Add makefile target with debug compiler flag Exclude Idea project file --- .gitignore | 4 +++- Makefile | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6f68fe36..ad35f9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,6 @@ site/ # Editors .vscode/ -.local/ \ No newline at end of file +.local/ +.idea/ +*.iml \ No newline at end of file diff --git a/Makefile b/Makefile index f108f7b9..8eea3767 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ TAGS := TESTS := . TESTFLAGS := LDFLAGS := -w -s -X github.com/rancher/k3d/version.Version=${GIT_TAG} -X github.com/rancher/k3d/version.K3sVersion=${K3S_TAG} +GCFLAGS := GOFLAGS := BINDIR := $(CURDIR)/bin BINARIES := k3d @@ -68,8 +69,11 @@ LINT_DIRS := $(DIRS) $(foreach dir,$(REC_DIRS),$(dir)/...) all: clean fmt check build +build-debug: GCFLAGS+="all=-N -l" +build-debug: build + build: - CGO_ENABLED=0 $(GO) build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)/$(BINARIES)' + CGO_ENABLED=0 $(GO) build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -gcflags '$(GCFLAGS)' -o '$(BINDIR)/$(BINARIES)' build-cross: LDFLAGS += -extldflags "-static" build-cross: From f6eaf25a2e114855731a9f3b4948ec3a5d7380d9 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 1 Jun 2020 21:33:56 +0200 Subject: [PATCH 2/7] :recycle: Some side effect refactoring Print usage When no k3d verb was specify Replace fake Cluster object create into getKubeConfig by realy complete object obtain thanks to cluster.GetCluster() function Use constant variable for node Label name use into populateClusterFieldsFromLabels() function, in forecast to rename label `k3d.cluster.secret` to `k3d.cluster.token` --- cmd/get/getKubeconfig.go | 6 +++++- cmd/root.go | 2 ++ pkg/cluster/cluster.go | 16 ++++++++-------- pkg/tools/tools.go | 2 +- pkg/types/types.go | 8 ++++++++ 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/cmd/get/getKubeconfig.go b/cmd/get/getKubeconfig.go index 2be6cc97..9ec20596 100644 --- a/cmd/get/getKubeconfig.go +++ b/cmd/get/getKubeconfig.go @@ -69,7 +69,11 @@ func NewCmdGetKubeconfig() *cobra.Command { } } else { for _, clusterName := range args { - clusters = append(clusters, &k3d.Cluster{Name: clusterName}) + retrievedCluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: clusterName}) + if err != nil { + log.Fatalln(err) + } + clusters = append(clusters, retrievedCluster) } } diff --git a/cmd/root.go b/cmd/root.go index 3190234c..1c5ce172 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -65,6 +65,8 @@ All Nodes of a k3d cluster are part of the same docker network.`, Run: func(cmd *cobra.Command, args []string) { if flags.version { printVersion() + } else { + cmd.Usage() } }, } diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 9eb74736..bc913614 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -79,11 +79,11 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus } cluster.Network.Name = networkID extraLabels := map[string]string{ - "k3d.cluster.network": networkID, - "k3d.cluster.network.external": strconv.FormatBool(cluster.Network.External), + k3d.NetworkLabelName: networkID, + k3d.NetworkExternalLabelName: strconv.FormatBool(cluster.Network.External), } if networkExists { - extraLabels["k3d.cluster.network.external"] = "true" // if the network wasn't created, we say that it's managed externally (important for cluster deletion) + extraLabels[k3d.NetworkExternalLabelName] = "true" // if the network wasn't created, we say that it's managed externally (important for cluster deletion) } /* @@ -105,7 +105,7 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus return err } - extraLabels["k3d.cluster.imageVolume"] = imageVolumeName + extraLabels[k3d.ImageVolumeLabelName] = imageVolumeName // attach volume to nodes for _, node := range cluster.Nodes { @@ -128,7 +128,7 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus } node.Labels["k3d.cluster"] = cluster.Name node.Env = append(node.Env, fmt.Sprintf("K3S_TOKEN=%s", cluster.Secret)) - node.Labels["k3d.cluster.secret"] = cluster.Secret + node.Labels[k3d.SecretLabelName] = cluster.Secret node.Labels["k3d.cluster.url"] = connectionURL // append extra labels @@ -419,7 +419,7 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { // get the name of the cluster network if cluster.Network.Name == "" { - if networkName, ok := node.Labels["k3d.cluster.network"]; ok { + if networkName, ok := node.Labels[k3d.NetworkLabelName]; ok { cluster.Network.Name = networkName } } @@ -427,7 +427,7 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { // check if the network is external // since the struct value is a bool, initialized as false, we cannot check if it's unset if !cluster.Network.External && !networkExternalSet { - if networkExternalString, ok := node.Labels["k3d.cluster.network.external"]; ok { + if networkExternalString, ok := node.Labels[k3d.NetworkExternalLabelName]; ok { if networkExternal, err := strconv.ParseBool(networkExternalString); err == nil { cluster.Network.External = networkExternal networkExternalSet = true @@ -437,7 +437,7 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { // get image volume // TODO: enable external image volumes the same way we do it with networks if cluster.ImageVolume == "" { - if imageVolumeName, ok := node.Labels["k3d.cluster.imageVolume"]; ok { + if imageVolumeName, ok := node.Labels[k3d.ImageVolumeLabelName]; ok { cluster.ImageVolume = imageVolumeName } } diff --git a/pkg/tools/tools.go b/pkg/tools/tools.go index ddb8fdc4..5704b550 100644 --- a/pkg/tools/tools.go +++ b/pkg/tools/tools.go @@ -50,7 +50,7 @@ func LoadImagesIntoCluster(ctx context.Context, runtime runtimes.Runtime, images var ok bool for _, node := range cluster.Nodes { if node.Role == k3d.MasterRole || node.Role == k3d.WorkerRole { - if imageVolume, ok = node.Labels["k3d.cluster.imageVolume"]; ok { + if imageVolume, ok = node.Labels[k3d.ImageVolumeLabelName]; ok { break } } diff --git a/pkg/types/types.go b/pkg/types/types.go index dd67d33e..469219e6 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -74,6 +74,14 @@ var DefaultObjectLabels = map[string]string{ "app": "k3d", } +// List of k3d technical label name +const ( + SecretLabelName string = "k3d.cluster.secret" + ImageVolumeLabelName string = "k3d.cluster.imageVolume" + NetworkExternalLabelName string = "k3d.cluster.network.external" + NetworkLabelName string = "k3d.cluster.network" +) + // DefaultRoleCmds maps the node roles to their respective default commands var DefaultRoleCmds = map[Role][]string{ MasterRole: {"server"}, From 6d9d5a0cc4e0a6d812a641f255fc1d3360598294 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 1 Jun 2020 21:43:49 +0200 Subject: [PATCH 3/7] :sparkles: Add get k3stoken feature Add new command verb `k3stoken` to `k3d get` Populate cluster secret field Add test for `k3d get k3stoken` command Externalize validable flag `--all`, in anticipation to reuse into `getKubeConfig.go` --- cmd/get/get.go | 1 + cmd/get/getClusterToken.go | 111 +++++++++++++++++++++++++++++++++++++ cmd/util/args.go | 44 +++++++++++++++ pkg/cluster/cluster.go | 6 ++ tests/common.sh | 5 ++ tests/test_basic.sh | 4 ++ 6 files changed, 171 insertions(+) create mode 100644 cmd/get/getClusterToken.go create mode 100644 cmd/util/args.go diff --git a/cmd/get/get.go b/cmd/get/get.go index e46f14f5..0ab7bd5d 100644 --- a/cmd/get/get.go +++ b/cmd/get/get.go @@ -47,6 +47,7 @@ func NewCmdGet() *cobra.Command { cmd.AddCommand(NewCmdGetCluster()) cmd.AddCommand(NewCmdGetNode()) cmd.AddCommand(NewCmdGetKubeconfig()) + cmd.AddCommand(NewCmdGetClusterToken()) // done return cmd diff --git a/cmd/get/getClusterToken.go b/cmd/get/getClusterToken.go new file mode 100644 index 00000000..006528c4 --- /dev/null +++ b/cmd/get/getClusterToken.go @@ -0,0 +1,111 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package get + +import ( + "fmt" + "os" + "sort" + "strings" + + "github.com/liggitt/tabwriter" + cliutil "github.com/rancher/k3d/cmd/util" + "github.com/rancher/k3d/pkg/cluster" + "github.com/rancher/k3d/pkg/runtimes" + k3d "github.com/rancher/k3d/pkg/types" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +type clusterTokenFlags struct { + validableFlags cliutil.ValidableFlags + noHeader bool +} + +// NewCmdGetClusterToken returns a new cobra command +func NewCmdGetClusterToken() *cobra.Command { + + getClusterTokenFlags := clusterTokenFlags{} + + // create new command + cmd := &cobra.Command{ + Use: "k3stoken [CLUSTER [CLUSTER [...]] | --all]", + Short: "Get cluster token", + Long: `Get k3s cluster token.`, + Args: func(cmd *cobra.Command, args []string) error { + return cliutil.ValidateClusterNameOrAllFlag(args, getClusterTokenFlags.validableFlags) + }, + Run: func(cmd *cobra.Command, args []string) { + var clusters []*k3d.Cluster + var err error + + // generate list of clusters + if getClusterTokenFlags.validableFlags.All { + clusters, err = cluster.GetClusters(runtimes.SelectedRuntime) + if err != nil { + log.Fatalln(err) + } + } else { + for _, clusterName := range args { + retrievedCluster, err := cluster.GetCluster(&k3d.Cluster{Name: clusterName}, runtimes.SelectedRuntime) + if err != nil { + log.Fatalln(err) + } + clusters = append(clusters, retrievedCluster) + } + } + + // pretty print secret + printSecret(clusters, getClusterTokenFlags.noHeader) + }, + } + + // add flags + cmd.Flags().BoolVarP(&getClusterTokenFlags.validableFlags.All, "all", "a", false, "Get k3s token from all existing clusters") + cmd.Flags().BoolVar(&getClusterTokenFlags.noHeader, "no-headers", false, "Disable headers") + + // done + return cmd +} + +func printSecret(clusters []*k3d.Cluster, headersOff bool) { + + tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) + defer tabwriter.Flush() + + if !headersOff { + headers := []string{"CLUSTER", "SECRET"} + _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) + if err != nil { + log.Fatalln("Failed to print headers") + } + } + + // alphabetical sort by cluster name + sort.Slice(clusters, func(i, j int) bool { + return clusters[i].Name < clusters[j].Name + }) + + for _, cluster := range clusters { + fmt.Fprintf(tabwriter, "%s\t%s\n", cluster.Name, string(cluster.Secret)) + } +} diff --git a/cmd/util/args.go b/cmd/util/args.go new file mode 100644 index 00000000..e2a7fa79 --- /dev/null +++ b/cmd/util/args.go @@ -0,0 +1,44 @@ +/* +Copyright © 2020 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package util + +import ( + "fmt" +) + +// struct describe flags which could be validate thanks to this file's functions +type ValidableFlags struct { + All bool +} + +// ValidateClusterNameOrAllFlag checks, if cluster name or --all arg is set +func ValidateClusterNameOrAllFlag(args []string, flagStruct ValidableFlags) error { + + clusterNamePresent := len(args) > 0 + getAllFlagPresent := flagStruct.All + + // xor : provide cluster name or all flags + if clusterNamePresent != getAllFlagPresent { + return nil + } + return fmt.Errorf("Need to specify one or more cluster names *or* set `--all` flag") +} diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index bc913614..43056c6c 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -442,6 +442,12 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { } } + // get k3s cluster's secret + if cluster.Secret == "" { + if secretToken, ok := node.Labels[k3d.SecretLabelName]; ok { + cluster.Secret = secretToken + } + } } return nil diff --git a/tests/common.sh b/tests/common.sh index f6464e4c..7b9eb5ca 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -107,3 +107,8 @@ check_registry() { check_volume_exists() { docker volume inspect "$1" >/dev/null 2>&1 } + +check_cluster_token_exist() { + [ -n "$EXE" ] || abort "EXE is not defined" + $EXE get k3stoken "$1" >/dev/null 2>&1 +} diff --git a/tests/test_basic.sh b/tests/test_basic.sh index 1ace7ba0..bdf9b930 100755 --- a/tests/test_basic.sh +++ b/tests/test_basic.sh @@ -16,6 +16,10 @@ check_cluster_count 2 info "Checking we have access to both clusters..." check_clusters "c1" "c2" || failed "error checking cluster" +info "Check k3s token retrieval" +check_cluster_token_exist "c1" || failed "could not find cluster token c1" +check_cluster_token_exist "c2" || failed "could not find cluster token c2" + info "Deleting clusters..." $EXE delete cluster c1 || failed "could not delete the cluster c1" $EXE delete cluster c2 || failed "could not delete the cluster c2" From 07be211bca40d08a2ccf41a9c38f0903cfa016c5 Mon Sep 17 00:00:00 2001 From: Antoine Date: Mon, 1 Jun 2020 22:19:22 +0200 Subject: [PATCH 4/7] :green_heart: fix ci review unchecked function error --- cmd/root.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index 1c5ce172..2646a0de 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -66,7 +66,10 @@ All Nodes of a k3d cluster are part of the same docker network.`, if flags.version { printVersion() } else { - cmd.Usage() + if err := cmd.Usage(); err != nil { + log.Errorln(err) + os.Exit(1) + } } }, } From e3bf18660e34b24d59f4e830da18c6ea1cb41c94 Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 3 Jun 2020 02:01:25 +0200 Subject: [PATCH 5/7] :ok_hand: PR review Use `log.Fatalln(err)` instead of `log.Errorln(err)` + `os.Exit(1)` Use Label prefix instead of LabelName suffix Rename all secret occurence with token --- cmd/create/createCluster.go | 8 ++++---- cmd/get/getClusterToken.go | 10 +++++----- cmd/root.go | 6 ++---- docs/usage/commands.md | 2 +- pkg/cluster/cluster.go | 36 ++++++++++++++++++------------------ pkg/tools/tools.go | 2 +- pkg/types/types.go | 10 +++++----- pkg/util/files.go | 2 +- pkg/util/randomString.go | 2 +- 9 files changed, 38 insertions(+), 40 deletions(-) diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index a0a62904..f05e2c57 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -115,7 +115,7 @@ func NewCmdCreateCluster() *cobra.Command { cmd.Flags().IntP("workers", "w", 0, "Specify how many workers you want to create") cmd.Flags().StringP("image", "i", fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.GetK3sVersion(false)), "Specify k3s image that you want to use for the nodes") cmd.Flags().String("network", "", "Join an existing network") - cmd.Flags().String("secret", "", "Specify a cluster secret. By default, we generate one.") + cmd.Flags().String("token", "", "Specify a cluster token. By default, we generate one.") cmd.Flags().StringArrayP("volume", "v", nil, "Mount volumes into the nodes (Format: `--volume [SOURCE:]DEST[@NODEFILTER[;NODEFILTER...]]`\n - Example: `k3d create -w 2 -v /my/path@worker[0,1] -v /tmp/test:/tmp/other@master[0]`") 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", false, "Wait for the master(s) to be ready before returning. Use '--timeout DURATION' to not wait forever.") @@ -206,8 +206,8 @@ func parseCreateClusterCmd(cmd *cobra.Command, args []string, createClusterOpts log.Fatalln("Can only run a single node in hostnetwork mode") } - // --secret - secret, err := cmd.Flags().GetString("secret") + // --token + token, err := cmd.Flags().GetString("token") if err != nil { log.Fatalln(err) } @@ -309,7 +309,7 @@ func parseCreateClusterCmd(cmd *cobra.Command, args []string, createClusterOpts cluster := &k3d.Cluster{ Name: clustername, Network: network, - Secret: secret, + Token: token, CreateClusterOpts: createClusterOpts, ExposeAPI: exposeAPI, } diff --git a/cmd/get/getClusterToken.go b/cmd/get/getClusterToken.go index 006528c4..cf065115 100644 --- a/cmd/get/getClusterToken.go +++ b/cmd/get/getClusterToken.go @@ -74,8 +74,8 @@ func NewCmdGetClusterToken() *cobra.Command { } } - // pretty print secret - printSecret(clusters, getClusterTokenFlags.noHeader) + // pretty print token + printToken(clusters, getClusterTokenFlags.noHeader) }, } @@ -87,13 +87,13 @@ func NewCmdGetClusterToken() *cobra.Command { return cmd } -func printSecret(clusters []*k3d.Cluster, headersOff bool) { +func printToken(clusters []*k3d.Cluster, headersOff bool) { tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) defer tabwriter.Flush() if !headersOff { - headers := []string{"CLUSTER", "SECRET"} + headers := []string{"CLUSTER", "TOKEN"} _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) if err != nil { log.Fatalln("Failed to print headers") @@ -106,6 +106,6 @@ func printSecret(clusters []*k3d.Cluster, headersOff bool) { }) for _, cluster := range clusters { - fmt.Fprintf(tabwriter, "%s\t%s\n", cluster.Name, string(cluster.Secret)) + fmt.Fprintf(tabwriter, "%s\t%s\n", cluster.Name, string(cluster.Token)) } } diff --git a/cmd/root.go b/cmd/root.go index 2646a0de..8173c561 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -67,8 +67,7 @@ All Nodes of a k3d cluster are part of the same docker network.`, printVersion() } else { if err := cmd.Usage(); err != nil { - log.Errorln(err) - os.Exit(1) + log.Fatalln(err) } } }, @@ -78,8 +77,7 @@ All Nodes of a k3d cluster are part of the same docker network.`, // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { - log.Errorln(err) - os.Exit(1) + log.Fatalln(err) } } diff --git a/docs/usage/commands.md b/docs/usage/commands.md index 12e8e430..5c2de08a 100644 --- a/docs/usage/commands.md +++ b/docs/usage/commands.md @@ -14,7 +14,7 @@ k3d --network # specify a network you want to connect to --no-image-volume # disable the creation of a volume for storing images (used for the 'k3d load image' command) -p, --port # add some more port mappings - --secret # specify a cluster secret (default: auto-generated) + --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 diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 43056c6c..ea0b6c96 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -79,19 +79,19 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus } cluster.Network.Name = networkID extraLabels := map[string]string{ - k3d.NetworkLabelName: networkID, - k3d.NetworkExternalLabelName: strconv.FormatBool(cluster.Network.External), + k3d.LabelNetwork: networkID, + k3d.LabelNetworkExternal: strconv.FormatBool(cluster.Network.External), } if networkExists { - extraLabels[k3d.NetworkExternalLabelName] = "true" // if the network wasn't created, we say that it's managed externally (important for cluster deletion) + extraLabels[k3d.LabelNetworkExternal] = "true" // if the network wasn't created, we say that it's managed externally (important for cluster deletion) } /* - * Cluster Secret + * Cluster Token */ - if cluster.Secret == "" { - cluster.Secret = GenerateClusterSecret() + if cluster.Token == "" { + cluster.Token = GenerateClusterToken() } /* @@ -105,7 +105,7 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus return err } - extraLabels[k3d.ImageVolumeLabelName] = imageVolumeName + extraLabels[k3d.LabelImageVolume] = imageVolumeName // attach volume to nodes for _, node := range cluster.Nodes { @@ -127,8 +127,8 @@ func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus node.Labels = make(map[string]string) // TODO: maybe create an init function? } node.Labels["k3d.cluster"] = cluster.Name - node.Env = append(node.Env, fmt.Sprintf("K3S_TOKEN=%s", cluster.Secret)) - node.Labels[k3d.SecretLabelName] = cluster.Secret + node.Env = append(node.Env, fmt.Sprintf("K3S_TOKEN=%s", cluster.Token)) + node.Labels[k3d.LabelToken] = cluster.Token node.Labels["k3d.cluster.url"] = connectionURL // append extra labels @@ -419,7 +419,7 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { // get the name of the cluster network if cluster.Network.Name == "" { - if networkName, ok := node.Labels[k3d.NetworkLabelName]; ok { + if networkName, ok := node.Labels[k3d.LabelNetwork]; ok { cluster.Network.Name = networkName } } @@ -427,7 +427,7 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { // check if the network is external // since the struct value is a bool, initialized as false, we cannot check if it's unset if !cluster.Network.External && !networkExternalSet { - if networkExternalString, ok := node.Labels[k3d.NetworkExternalLabelName]; ok { + if networkExternalString, ok := node.Labels[k3d.LabelNetworkExternal]; ok { if networkExternal, err := strconv.ParseBool(networkExternalString); err == nil { cluster.Network.External = networkExternal networkExternalSet = true @@ -437,15 +437,15 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { // get image volume // TODO: enable external image volumes the same way we do it with networks if cluster.ImageVolume == "" { - if imageVolumeName, ok := node.Labels[k3d.ImageVolumeLabelName]; ok { + if imageVolumeName, ok := node.Labels[k3d.LabelImageVolume]; ok { cluster.ImageVolume = imageVolumeName } } - // get k3s cluster's secret - if cluster.Secret == "" { - if secretToken, ok := node.Labels[k3d.SecretLabelName]; ok { - cluster.Secret = secretToken + // get k3s cluster's token + if cluster.Token == "" { + if token, ok := node.Labels[k3d.LabelToken]; ok { + cluster.Token = token } } } @@ -493,8 +493,8 @@ func GetCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluster return cluster, nil } -// GenerateClusterSecret generates a random 20 character string -func GenerateClusterSecret() string { +// GenerateClusterToken generates a random 20 character string +func GenerateClusterToken() string { return util.GenerateRandomString(20) } diff --git a/pkg/tools/tools.go b/pkg/tools/tools.go index 5704b550..21d87bbe 100644 --- a/pkg/tools/tools.go +++ b/pkg/tools/tools.go @@ -50,7 +50,7 @@ func LoadImagesIntoCluster(ctx context.Context, runtime runtimes.Runtime, images var ok bool for _, node := range cluster.Nodes { if node.Role == k3d.MasterRole || node.Role == k3d.WorkerRole { - if imageVolume, ok = node.Labels[k3d.ImageVolumeLabelName]; ok { + if imageVolume, ok = node.Labels[k3d.LabelImageVolume]; ok { break } } diff --git a/pkg/types/types.go b/pkg/types/types.go index 469219e6..cb06a182 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -76,10 +76,10 @@ var DefaultObjectLabels = map[string]string{ // List of k3d technical label name const ( - SecretLabelName string = "k3d.cluster.secret" - ImageVolumeLabelName string = "k3d.cluster.imageVolume" - NetworkExternalLabelName string = "k3d.cluster.network.external" - NetworkLabelName string = "k3d.cluster.network" + LabelToken string = "k3d.cluster.token" + LabelImageVolume string = "k3d.cluster.imageVolume" + LabelNetworkExternal string = "k3d.cluster.network.external" + LabelNetwork string = "k3d.cluster.network" ) // DefaultRoleCmds maps the node roles to their respective default commands @@ -160,7 +160,7 @@ type ClusterNetwork struct { type Cluster struct { Name string `yaml:"name" json:"name,omitempty"` Network ClusterNetwork `yaml:"network" json:"network,omitempty"` - Secret string `yaml:"cluster_secret" json:"clusterSecret,omitempty"` + Token string `yaml:"cluster_token" json:"clusterToken,omitempty"` Nodes []*Node `yaml:"nodes" json:"nodes,omitempty"` InitNode *Node // init master node ExternalDatastore ExternalDatastore `yaml:"external_datastore" json:"externalDatastore,omitempty"` diff --git a/pkg/util/files.go b/pkg/util/files.go index 2eb48c2d..2276a3a8 100644 --- a/pkg/util/files.go +++ b/pkg/util/files.go @@ -43,7 +43,7 @@ func GetConfigDirOrCreate() (string, error) { // create directories if necessary if err := createDirIfNotExists(configDir); err != nil { - log.Errorln("Failed to create config path '%s'", configDir) + log.Errorf("Failed to create config path '%s'", configDir) return "", err } diff --git a/pkg/util/randomString.go b/pkg/util/randomString.go index 03ff2ee4..def2d959 100644 --- a/pkg/util/randomString.go +++ b/pkg/util/randomString.go @@ -37,7 +37,7 @@ const ( var src = rand.NewSource(time.Now().UnixNano()) // GenerateRandomString thanks to https://stackoverflow.com/a/31832326/6450189 -// GenerateRandomString is used to generate a random string that is used as a cluster secret +// GenerateRandomString is used to generate a random string that is used as a cluster token func GenerateRandomString(n int) string { sb := strings.Builder{} From ad06e90e13d5931ad2dab413d03cfbb63681f70f Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Wed, 3 Jun 2020 15:19:33 +0200 Subject: [PATCH 6/7] fix issues with context.Context and remove LOG_LEVEL=debug in e2e test --- cmd/get/getClusterToken.go | 4 ++-- tests/test_full_lifecycle.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/get/getClusterToken.go b/cmd/get/getClusterToken.go index cf065115..199b19ee 100644 --- a/cmd/get/getClusterToken.go +++ b/cmd/get/getClusterToken.go @@ -60,13 +60,13 @@ func NewCmdGetClusterToken() *cobra.Command { // generate list of clusters if getClusterTokenFlags.validableFlags.All { - clusters, err = cluster.GetClusters(runtimes.SelectedRuntime) + clusters, err = cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } } else { for _, clusterName := range args { - retrievedCluster, err := cluster.GetCluster(&k3d.Cluster{Name: clusterName}, runtimes.SelectedRuntime) + retrievedCluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: clusterName}) if err != nil { log.Fatalln(err) } diff --git a/tests/test_full_lifecycle.sh b/tests/test_full_lifecycle.sh index f3c5cf1a..fe0a48c8 100755 --- a/tests/test_full_lifecycle.sh +++ b/tests/test_full_lifecycle.sh @@ -40,7 +40,7 @@ check_multi_node "$clustername" 2 || failed "failed to verify number of nodes" # 4. adding another worker node info "Adding one worker node..." -LOG_LEVEL=debug $EXE create node "extra-worker" --cluster "$clustername" --role "worker" --wait --timeout 360s || failed "failed to add worker node" +$EXE create node "extra-worker" --cluster "$clustername" --role "worker" --wait --timeout 360s || failed "failed to add worker node" info "Checking that we have 3 nodes available now..." check_multi_node "$clustername" 3 || failed "failed to verify number of nodes" From 0862f1132495ecb49a4226d719b960018aaf572b Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 4 Jun 2020 00:38:45 +0200 Subject: [PATCH 7/7] :ok_hand: replace `k3d getk3stoken` command by `--token` flag onto command `k3d get cluster` --- cmd/get/get.go | 1 - cmd/get/getCluster.go | 96 +++++++++++++++----------------- cmd/get/getClusterToken.go | 111 ------------------------------------- cmd/util/args.go | 44 --------------- pkg/cluster/cluster.go | 9 +++ pkg/types/types.go | 21 +++++++ tests/common.sh | 2 +- 7 files changed, 76 insertions(+), 208 deletions(-) delete mode 100644 cmd/get/getClusterToken.go delete mode 100644 cmd/util/args.go diff --git a/cmd/get/get.go b/cmd/get/get.go index 0ab7bd5d..e46f14f5 100644 --- a/cmd/get/get.go +++ b/cmd/get/get.go @@ -47,7 +47,6 @@ func NewCmdGet() *cobra.Command { cmd.AddCommand(NewCmdGetCluster()) cmd.AddCommand(NewCmdGetNode()) cmd.AddCommand(NewCmdGetKubeconfig()) - cmd.AddCommand(NewCmdGetClusterToken()) // done return cmd diff --git a/cmd/get/getCluster.go b/cmd/get/getCluster.go index 1063001c..e2708fc5 100644 --- a/cmd/get/getCluster.go +++ b/cmd/get/getCluster.go @@ -22,9 +22,9 @@ THE SOFTWARE. package get import ( + "context" "fmt" "os" - "sort" "strings" k3cluster "github.com/rancher/k3d/pkg/cluster" @@ -37,41 +37,32 @@ import ( "github.com/liggitt/tabwriter" ) +// TODO : deal with --all flag to manage differentiate started cluster and stopped cluster like `docker ps` and `docker ps -a` +type clusterFlags struct { + noHeader bool + token bool +} + // NewCmdGetCluster returns a new cobra command func NewCmdGetCluster() *cobra.Command { + clusterFlags := clusterFlags{} + // create new command cmd := &cobra.Command{ Use: "cluster [NAME [NAME...]]", Aliases: []string{"clusters"}, Short: "Get cluster(s)", Long: `Get cluster(s).`, - Args: cobra.MinimumNArgs(0), // 0 or more; 0 = all Run: func(cmd *cobra.Command, args []string) { - clusters, headersOff := parseGetClusterCmd(cmd, args) - var existingClusters []*k3d.Cluster - if len(clusters) == 0 { // Option a) no cluster name specified -> get all clusters - found, err := k3cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) - if err != nil { - log.Fatalln(err) - } - existingClusters = append(existingClusters, found...) - } else { // Option b) cluster name specified -> get specific cluster - for _, cluster := range clusters { - found, err := k3cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, cluster) - if err != nil { - log.Fatalln(err) - } - existingClusters = append(existingClusters, found) - } - } - // print existing clusters - printClusters(existingClusters, headersOff) + clusters := buildClusterList(cmd.Context(), args) + PrintClusters(clusters, clusterFlags) }, } // add flags - cmd.Flags().Bool("no-headers", false, "Disable headers") + cmd.Flags().BoolVar(&clusterFlags.noHeader, "no-headers", false, "Disable headers") + cmd.Flags().BoolVar(&clusterFlags.token, "token", false, "Print k3s cluster token") // add subcommands @@ -79,54 +70,57 @@ func NewCmdGetCluster() *cobra.Command { return cmd } -func parseGetClusterCmd(cmd *cobra.Command, args []string) ([]*k3d.Cluster, bool) { +func buildClusterList(ctx context.Context, args []string) []*k3d.Cluster { + var clusters []*k3d.Cluster + var err error - // --no-headers - headersOff, err := cmd.Flags().GetBool("no-headers") - if err != nil { - log.Fatalln(err) - } - - // Args = cluster name if len(args) == 0 { - return nil, headersOff + // cluster name not specified : get all clusters + clusters, err = k3cluster.GetClusters(ctx, runtimes.SelectedRuntime) + if err != nil { + log.Fatalln(err) + } + } else { + for _, clusterName := range args { + // cluster name specified : get specific cluster + retrievedCluster, err := k3cluster.GetCluster(ctx, runtimes.SelectedRuntime, &k3d.Cluster{Name: clusterName}) + if err != nil { + log.Fatalln(err) + } + clusters = append(clusters, retrievedCluster) + } } - clusters := []*k3d.Cluster{} - for _, name := range args { - clusters = append(clusters, &k3d.Cluster{Name: name}) - } - - return clusters, headersOff + return clusters } -func printClusters(clusters []*k3d.Cluster, headersOff bool) { +// PrintPrintClusters : display list of cluster +func PrintClusters(clusters []*k3d.Cluster, flags clusterFlags) { tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) defer tabwriter.Flush() - if !headersOff { + if !flags.noHeader { headers := []string{"NAME", "MASTERS", "WORKERS"} // TODO: getCluster: add status column + if flags.token { + headers = append(headers, "TOKEN") + } _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) if err != nil { log.Fatalln("Failed to print headers") } } - sort.Slice(clusters, func(i, j int) bool { - return clusters[i].Name < clusters[j].Name - }) + k3cluster.SortClusters(clusters) for _, cluster := range clusters { - masterCount := 0 - workerCount := 0 - for _, node := range cluster.Nodes { - if node.Role == k3d.MasterRole { - masterCount++ - } else if node.Role == k3d.WorkerRole { - workerCount++ - } + masterCount := cluster.MasterCount() + workerCount := cluster.WorkerCount() + + if flags.token { + fmt.Fprintf(tabwriter, "%s\t%d\t%d\t%s\n", cluster.Name, masterCount, workerCount, cluster.Token) + } else { + fmt.Fprintf(tabwriter, "%s\t%d\t%d\n", cluster.Name, masterCount, workerCount) } - fmt.Fprintf(tabwriter, "%s\t%d\t%d\n", cluster.Name, masterCount, workerCount) } } diff --git a/cmd/get/getClusterToken.go b/cmd/get/getClusterToken.go deleted file mode 100644 index 199b19ee..00000000 --- a/cmd/get/getClusterToken.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright © 2020 The k3d Author(s) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -package get - -import ( - "fmt" - "os" - "sort" - "strings" - - "github.com/liggitt/tabwriter" - cliutil "github.com/rancher/k3d/cmd/util" - "github.com/rancher/k3d/pkg/cluster" - "github.com/rancher/k3d/pkg/runtimes" - k3d "github.com/rancher/k3d/pkg/types" - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -type clusterTokenFlags struct { - validableFlags cliutil.ValidableFlags - noHeader bool -} - -// NewCmdGetClusterToken returns a new cobra command -func NewCmdGetClusterToken() *cobra.Command { - - getClusterTokenFlags := clusterTokenFlags{} - - // create new command - cmd := &cobra.Command{ - Use: "k3stoken [CLUSTER [CLUSTER [...]] | --all]", - Short: "Get cluster token", - Long: `Get k3s cluster token.`, - Args: func(cmd *cobra.Command, args []string) error { - return cliutil.ValidateClusterNameOrAllFlag(args, getClusterTokenFlags.validableFlags) - }, - Run: func(cmd *cobra.Command, args []string) { - var clusters []*k3d.Cluster - var err error - - // generate list of clusters - if getClusterTokenFlags.validableFlags.All { - clusters, err = cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) - if err != nil { - log.Fatalln(err) - } - } else { - for _, clusterName := range args { - retrievedCluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: clusterName}) - if err != nil { - log.Fatalln(err) - } - clusters = append(clusters, retrievedCluster) - } - } - - // pretty print token - printToken(clusters, getClusterTokenFlags.noHeader) - }, - } - - // add flags - cmd.Flags().BoolVarP(&getClusterTokenFlags.validableFlags.All, "all", "a", false, "Get k3s token from all existing clusters") - cmd.Flags().BoolVar(&getClusterTokenFlags.noHeader, "no-headers", false, "Disable headers") - - // done - return cmd -} - -func printToken(clusters []*k3d.Cluster, headersOff bool) { - - tabwriter := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', tabwriter.RememberWidths) - defer tabwriter.Flush() - - if !headersOff { - headers := []string{"CLUSTER", "TOKEN"} - _, err := fmt.Fprintf(tabwriter, "%s\n", strings.Join(headers, "\t")) - if err != nil { - log.Fatalln("Failed to print headers") - } - } - - // alphabetical sort by cluster name - sort.Slice(clusters, func(i, j int) bool { - return clusters[i].Name < clusters[j].Name - }) - - for _, cluster := range clusters { - fmt.Fprintf(tabwriter, "%s\t%s\n", cluster.Name, string(cluster.Token)) - } -} diff --git a/cmd/util/args.go b/cmd/util/args.go deleted file mode 100644 index e2a7fa79..00000000 --- a/cmd/util/args.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright © 2020 The k3d Author(s) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -package util - -import ( - "fmt" -) - -// struct describe flags which could be validate thanks to this file's functions -type ValidableFlags struct { - All bool -} - -// ValidateClusterNameOrAllFlag checks, if cluster name or --all arg is set -func ValidateClusterNameOrAllFlag(args []string, flagStruct ValidableFlags) error { - - clusterNamePresent := len(args) > 0 - getAllFlagPresent := flagStruct.All - - // xor : provide cluster name or all flags - if clusterNamePresent != getAllFlagPresent { - return nil - } - return fmt.Errorf("Need to specify one or more cluster names *or* set `--all` flag") -} diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index ea0b6c96..3b6634c7 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -25,6 +25,7 @@ import ( "bytes" "context" "fmt" + "sort" "strconv" "strings" "time" @@ -591,3 +592,11 @@ func StopCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluste } return nil } + +// SortClusters : in place sort cluster list by cluster name alphabetical order +func SortClusters(clusters []*k3d.Cluster) []*k3d.Cluster { + sort.Slice(clusters, func(i, j int) bool { + return clusters[i].Name < clusters[j].Name + }) + return clusters +} \ No newline at end of file diff --git a/pkg/types/types.go b/pkg/types/types.go index cb06a182..b1950259 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -170,6 +170,27 @@ type Cluster struct { ImageVolume string `yaml:"image_volume" json:"imageVolume,omitempty"` } +// MasterCount return number of master node into cluster +func (c *Cluster) MasterCount() int { + masterCount := 0 + for _, node := range c.Nodes { + if node.Role == MasterRole { + masterCount++ + } + } + return masterCount +} +// WorkerCount return number of worker node into cluster +func (c *Cluster) WorkerCount() int { + workerCount := 0 + for _, node := range c.Nodes { + if node.Role == WorkerRole { + workerCount++ + } + } + return workerCount +} + // Node describes a k3d node type Node struct { Name string `yaml:"name" json:"name,omitempty"` diff --git a/tests/common.sh b/tests/common.sh index 7b9eb5ca..42b180f4 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -110,5 +110,5 @@ check_volume_exists() { check_cluster_token_exist() { [ -n "$EXE" ] || abort "EXE is not defined" - $EXE get k3stoken "$1" >/dev/null 2>&1 + $EXE get cluster "$1" --token | grep "TOKEN" >/dev/null 2>&1 }