From f65c58124d2f8021b75ac23763bfde7105d1128b Mon Sep 17 00:00:00 2001 From: Jacob Beacham Date: Thu, 27 Oct 2016 17:04:23 -0700 Subject: [PATCH 1/3] New command: "kubeadm token generate" This surfaces the token generation logic so that users can first generate and store a token, then pass it to kubeadm init/join. Otherwise, users have to capture and parse the output of "kubeadm init" to feed the token to "kubeadm join." --- cmd/kubeadm/app/cmd/cmd.go | 1 + cmd/kubeadm/app/cmd/token.go | 86 +++++++++++++++++++++++++++++++ cmd/kubeadm/app/cmd/token_test.go | 46 +++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 cmd/kubeadm/app/cmd/token.go create mode 100644 cmd/kubeadm/app/cmd/token_test.go diff --git a/cmd/kubeadm/app/cmd/cmd.go b/cmd/kubeadm/app/cmd/cmd.go index 8826cd38c0..068a57a8b8 100644 --- a/cmd/kubeadm/app/cmd/cmd.go +++ b/cmd/kubeadm/app/cmd/cmd.go @@ -81,6 +81,7 @@ func NewKubeadmCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob cmds.AddCommand(NewCmdInit(out)) cmds.AddCommand(NewCmdJoin(out)) cmds.AddCommand(NewCmdReset(out)) + cmds.AddCommand(NewCmdToken(out)) cmds.AddCommand(NewCmdVersion(out)) return cmds diff --git a/cmd/kubeadm/app/cmd/token.go b/cmd/kubeadm/app/cmd/token.go new file mode 100644 index 0000000000..8776510520 --- /dev/null +++ b/cmd/kubeadm/app/cmd/token.go @@ -0,0 +1,86 @@ +/* +Copyright 2016 The Kubernetes 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 cmd + +import ( + "errors" + "fmt" + "io" + + "github.com/renstrom/dedent" + "github.com/spf13/cobra" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + "k8s.io/kubernetes/cmd/kubeadm/app/util" + kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" +) + +func NewCmdToken(out io.Writer) *cobra.Command { + cmd := &cobra.Command{ + Use: "token", + Short: "Manage tokens used by init/join", + + // Without this callback, if a user runs just the "token" + // command without a subcommand, or with an invalid subcommand, + // cobra will print usage information, but still exit cleanly. + // We want to return an error code in these cases so that the + // user knows that their command was invalid. + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("missing subcommand; 'token' is not meant to be run on its own") + } else { + return fmt.Errorf("invalid subcommand: %s", args[0]) + } + }, + } + + cmd.AddCommand(NewCmdTokenGenerate(out)) + return cmd +} + +func NewCmdTokenGenerate(out io.Writer) *cobra.Command { + return &cobra.Command{ + Use: "generate", + Short: "Generate and print a token suitable for use with init/join", + Long: dedent.Dedent(` + This command will print out a randomly-generated token that you can use with + the "init" and "join" commands. + + You don't have to use this command in order to generate a token, you can do so + yourself as long as it's in the format "<6 characters>.<16 characters>". This + command is provided for convenience to generate tokens in that format. + + You can also use "kubeadm init" without specifying a token, and it will + generate and print one for you. + `), + Run: func(cmd *cobra.Command, args []string) { + err := RunGenerateToken(out) + kubeadmutil.CheckErr(err) + }, + } +} + +func RunGenerateToken(out io.Writer) error { + s := &kubeadmapi.Secrets{} + err := util.GenerateToken(s) + if err != nil { + return err + } + + fmt.Fprintln(out, s.GivenToken) + return nil +} diff --git a/cmd/kubeadm/app/cmd/token_test.go b/cmd/kubeadm/app/cmd/token_test.go new file mode 100644 index 0000000000..2db9cd9e0b --- /dev/null +++ b/cmd/kubeadm/app/cmd/token_test.go @@ -0,0 +1,46 @@ +/* +Copyright 2016 The Kubernetes 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 cmd + +import ( + "bytes" + "regexp" + "testing" +) + +const ( + TokenExpectedRegex = "^\\S{6}\\.\\S{16}\n$" +) + +func TestRunGenerateToken(t *testing.T) { + var buf bytes.Buffer + + err := RunGenerateToken(&buf) + if err != nil { + t.Errorf("RunGenerateToken returned an error: %v", err) + } + + output := buf.String() + + matched, err := regexp.MatchString(TokenExpectedRegex, output) + if err != nil { + t.Fatalf("encountered an error while trying to match RunGenerateToken's output: %v", err) + } + if !matched { + t.Errorf("RunGenerateToken's output did not match expected regex; wanted: [%s], got: [%s]", TokenExpectedRegex, output) + } +} From 5e66509a24bedad4d912e132019d378d4c207bea Mon Sep 17 00:00:00 2001 From: Jacob Beacham Date: Thu, 27 Oct 2016 17:25:35 -0700 Subject: [PATCH 2/3] Cloning kubectl.sh logic into new kubeadm.sh. --- cluster/kubeadm.sh | 106 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 cluster/kubeadm.sh diff --git a/cluster/kubeadm.sh b/cluster/kubeadm.sh new file mode 100755 index 0000000000..557cb10b90 --- /dev/null +++ b/cluster/kubeadm.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# Copyright 2016 The Kubernetes 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=${KUBE_ROOT:-$(dirname "${BASH_SOURCE}")/..} +source "${KUBE_ROOT}/cluster/kube-util.sh" + +# Get the absolute path of the directory component of a file, i.e. the +# absolute path of the dirname of $1. +get_absolute_dirname() { + echo "$(cd "$(dirname "$1")" && pwd)" +} + +# Detect the OS name/arch so that we can find our binary +case "$(uname -s)" in + Darwin) + host_os=darwin + ;; + Linux) + host_os=linux + ;; + *) + echo "Unsupported host OS. Must be Linux or Mac OS X." >&2 + exit 1 + ;; +esac + +case "$(uname -m)" in + x86_64*) + host_arch=amd64 + ;; + i?86_64*) + host_arch=amd64 + ;; + amd64*) + host_arch=amd64 + ;; + arm*) + host_arch=arm + ;; + i?86*) + host_arch=386 + ;; + s390x*) + host_arch=s390x + ;; + ppc64le*) + host_arch=ppc64le + ;; + *) + echo "Unsupported host arch. Must be x86_64, 386, arm, s390x or ppc64le." >&2 + exit 1 + ;; +esac + +# If KUBEADM_PATH isn't set, gather up the list of likely places and use ls +# to find the latest one. +if [[ -z "${KUBEADM_PATH:-}" ]]; then + locations=( + "${KUBE_ROOT}/_output/bin/kubeadm" + "${KUBE_ROOT}/_output/dockerized/bin/${host_os}/${host_arch}/kubeadm" + "${KUBE_ROOT}/_output/local/bin/${host_os}/${host_arch}/kubeadm" + "${KUBE_ROOT}/platforms/${host_os}/${host_arch}/kubeadm" + ) + kubeadm=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) + + if [[ ! -x "$kubeadm" ]]; then + { + echo "It looks as if you don't have a compiled kubeadm binary" + echo + echo "If you are running from a clone of the git repo, please run" + echo "'./build/run.sh make cross'. Note that this requires having" + echo "Docker installed." + echo + echo "If you are running from a binary release tarball, something is wrong. " + echo "Look at http://kubernetes.io/ for information on how to contact the " + echo "development team for help." + } >&2 + exit 1 + fi +elif [[ ! -x "${KUBEADM_PATH}" ]]; then + { + echo "KUBEADM_PATH environment variable set to '${KUBEADM_PATH}', but " + echo "this doesn't seem to be a valid executable." + } >&2 + exit 1 +fi +kubeadm="${KUBEADM_PATH:-${kubeadm}}" + +"${kubeadm}" "${@+$@}" From cf6b6778dcd682013fa4403be21c2373e76be9dd Mon Sep 17 00:00:00 2001 From: Jacob Beacham Date: Thu, 27 Oct 2016 17:27:15 -0700 Subject: [PATCH 3/3] Adding CLI tests for kubeadm. --- Makefile | 1 + cmd/kubeadm/app/cmd/BUILD | 6 ++- cmd/kubeadm/test/BUILD | 25 +++++++++++ cmd/kubeadm/test/token_test.go | 65 +++++++++++++++++++++++++++++ cmd/kubeadm/test/util.go | 41 ++++++++++++++++++ hack/make-rules/test-kubeadm-cmd.sh | 32 ++++++++++++++ hack/make-rules/test.sh | 1 + hack/verify-flags/known-flags.txt | 1 + test/test_owners.csv | 1 + 9 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 cmd/kubeadm/test/BUILD create mode 100644 cmd/kubeadm/test/token_test.go create mode 100644 cmd/kubeadm/test/util.go create mode 100755 hack/make-rules/test-kubeadm-cmd.sh diff --git a/Makefile b/Makefile index f4485c362c..f682c194ca 100644 --- a/Makefile +++ b/Makefile @@ -185,6 +185,7 @@ test-e2e-node: ginkgo generated_files # make test-cmd .PHONY: test-cmd test-cmd: generated_files + hack/make-rules/test-kubeadm-cmd.sh hack/make-rules/test-cmd.sh # Remove all build artifacts. diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index 2edf2c923e..925020a390 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -17,6 +17,7 @@ go_library( "init.go", "join.go", "reset.go", + "token.go", "version.go", ], tags = ["automanaged"], @@ -42,7 +43,10 @@ go_library( go_test( name = "go_default_test", - srcs = ["reset_test.go"], + srcs = [ + "reset_test.go", + "token_test.go", + ], library = "go_default_library", tags = ["automanaged"], deps = ["//cmd/kubeadm/app/preflight:go_default_library"], diff --git a/cmd/kubeadm/test/BUILD b/cmd/kubeadm/test/BUILD new file mode 100644 index 0000000000..f6609ba422 --- /dev/null +++ b/cmd/kubeadm/test/BUILD @@ -0,0 +1,25 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_binary", + "go_library", + "go_test", + "cgo_library", +) + +go_library( + name = "go_default_library", + srcs = ["util.go"], + tags = ["automanaged"], +) + +go_test( + name = "go_default_test", + srcs = ["token_test.go"], + library = "go_default_library", + tags = ["automanaged"], + deps = [], +) diff --git a/cmd/kubeadm/test/token_test.go b/cmd/kubeadm/test/token_test.go new file mode 100644 index 0000000000..b9bda37d8e --- /dev/null +++ b/cmd/kubeadm/test/token_test.go @@ -0,0 +1,65 @@ +/* +Copyright 2016 The Kubernetes 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 kubeadm + +import ( + "flag" + "regexp" + "testing" +) + +const ( + TokenExpectedRegex = "^\\S{6}\\.\\S{16}\n$" +) + +var kubeadmPath string + +func init() { + flag.StringVar(&kubeadmPath, "kubeadm-path", "cluster/kubeadm.sh", "Location of kubeadm") +} + +func TestCmdTokenGenerate(t *testing.T) { + stdout, _, err := RunCmd(kubeadmPath, "token", "generate") + if err != nil { + t.Errorf("'kubeadm token generate' exited uncleanly: %v", err) + } + + matched, err := regexp.MatchString(TokenExpectedRegex, stdout) + if err != nil { + t.Fatalf("encountered an error while trying to match 'kubeadm token generate' stdout: %v", err) + } + if !matched { + t.Errorf("'kubeadm token generate' stdout did not match expected regex; wanted: [%s], got: [%s]", TokenExpectedRegex, stdout) + } +} + +func TestCmdTokenGenerateTypoError(t *testing.T) { + /* + Since we expect users to do things like this: + + $ TOKEN=$(kubeadm token generate) + + we want to make sure that if they have a typo in their command, we exit + with a non-zero status code after showing the command's usage, so that + the usage itself isn't captured as a token without the user noticing. + */ + + _, _, err := RunCmd(kubeadmPath, "token", "genorate") // subtle typo + if err == nil { + t.Error("'kubeadm token genorate' (a deliberate typo) exited without an error when we expected non-zero exit status") + } +} diff --git a/cmd/kubeadm/test/util.go b/cmd/kubeadm/test/util.go new file mode 100644 index 0000000000..d17836f3c6 --- /dev/null +++ b/cmd/kubeadm/test/util.go @@ -0,0 +1,41 @@ +/* +Copyright 2016 The Kubernetes 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 kubeadm + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" +) + +// Forked from test/e2e/framework because the e2e framework is quite bloated +// for our purposes here, and modified to remove undesired logging. +func RunCmd(command string, args ...string) (string, string, error) { + var bout, berr bytes.Buffer + cmd := exec.Command(command, args...) + cmd.Stdout = io.MultiWriter(os.Stdout, &bout) + cmd.Stderr = io.MultiWriter(os.Stderr, &berr) + err := cmd.Run() + stdout, stderr := bout.String(), berr.String() + if err != nil { + return "", "", fmt.Errorf("error running %s %v; got error %v, stdout %q, stderr %q", + command, args, err, stdout, stderr) + } + return stdout, stderr, nil +} diff --git a/hack/make-rules/test-kubeadm-cmd.sh b/hack/make-rules/test-kubeadm-cmd.sh new file mode 100755 index 0000000000..c28d7d00e5 --- /dev/null +++ b/hack/make-rules/test-kubeadm-cmd.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Copyright 2016 The Kubernetes 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../.. +source "${KUBE_ROOT}/hack/lib/init.sh" + +KUBEADM_PATH="${KUBEADM_PATH:=$(kube::realpath "${KUBE_ROOT}")/cluster/kubeadm.sh}" + +# If testing a different version of kubeadm than the current build, you can +# comment this out to save yourself from needlessly building here. +make -C "${KUBE_ROOT}" WHAT=cmd/kubeadm + +make -C "${KUBE_ROOT}" test \ + WHAT=cmd/kubeadm/test \ + KUBE_TEST_ARGS="--kubeadm-path '${KUBEADM_PATH}'" diff --git a/hack/make-rules/test.sh b/hack/make-rules/test.sh index db6a399849..93946fa344 100755 --- a/hack/make-rules/test.sh +++ b/hack/make-rules/test.sh @@ -35,6 +35,7 @@ kube::test::find_dirs() { -path './_artifacts/*' \ -o -path './_output/*' \ -o -path './_gopath/*' \ + -o -path './cmd/kubeadm/test/*' \ -o -path './contrib/podex/*' \ -o -path './output/*' \ -o -path './release/*' \ diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index e73dec1d75..668244b3b0 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -295,6 +295,7 @@ kube-master kube-master kube-master-url kube-reserved +kubeadm-path kubecfg-file kubectl-path kubelet-address diff --git a/test/test_owners.csv b/test/test_owners.csv index abaa63625b..115a32bfda 100644 --- a/test/test_owners.csv +++ b/test/test_owners.csv @@ -457,6 +457,7 @@ k8s.io/kubernetes/cmd/kubeadm/app/cmd,caesarxuchao,1 k8s.io/kubernetes/cmd/kubeadm/app/images,davidopp,1 k8s.io/kubernetes/cmd/kubeadm/app/preflight,apprenda,0 k8s.io/kubernetes/cmd/kubeadm/app/util,krousey,1 +k8s.io/kubernetes/cmd/kubeadm/test,pipejakob,0 k8s.io/kubernetes/cmd/kubelet/app,hurf,1 k8s.io/kubernetes/cmd/libs/go2idl/client-gen/types,caesarxuchao,0 k8s.io/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf,smarterclayton,0