Skip to content

Commit

Permalink
Merge pull request #49982 from luxas/kubeadm_node_bootstrap_token_phase
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue (batch tested with PRs 49615, 49321, 49982, 49788, 50355)

kubeadm: Move all node bootstrap token related code in one phase package

**What this PR does / why we need it**:
Part of the phases refactoring.
Moves everything Node Bootstrap Token-related into its own package.
In the future there will be a `phases/bootstraptoken/master` pkg as well.
The generic bootstrap token client functions should be moved to client go eventually kubernetes/client-go#114

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

**Special notes for your reviewer**:
I'll yet add the CLI interface for this tomorrow.
Not sure if this compiles currently, but I'm uploading this now for initial review.

**Release note**:

```release-note
NONE
```
@kubernetes/sig-cluster-lifecycle-pr-reviews @mattmoyer
  • Loading branch information
Kubernetes Submit Queue authored Aug 10, 2017
2 parents f6d90ea + cb73972 commit 938bc61
Show file tree
Hide file tree
Showing 26 changed files with 828 additions and 327 deletions.
2 changes: 2 additions & 0 deletions cmd/kubeadm/app/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ filegroup(
"//cmd/kubeadm/app/node:all-srcs",
"//cmd/kubeadm/app/phases/addons:all-srcs",
"//cmd/kubeadm/app/phases/apiconfig:all-srcs",
"//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:all-srcs",
"//cmd/kubeadm/app/phases/bootstraptoken/node:all-srcs",
"//cmd/kubeadm/app/phases/certs:all-srcs",
"//cmd/kubeadm/app/phases/controlplane:all-srcs",
"//cmd/kubeadm/app/phases/kubeconfig:all-srcs",
Expand Down
3 changes: 2 additions & 1 deletion cmd/kubeadm/app/cmd/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ go_library(
"//cmd/kubeadm/app/discovery:go_default_library",
"//cmd/kubeadm/app/phases/addons:go_default_library",
"//cmd/kubeadm/app/phases/apiconfig:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/markmaster:go_default_library",
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
"//cmd/kubeadm/app/phases/token:go_default_library",
"//cmd/kubeadm/app/phases/uploadconfig:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
Expand Down
27 changes: 21 additions & 6 deletions cmd/kubeadm/app/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ import (
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
addonsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons"
apiconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/apiconfig"
clusterinfophase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
nodebootstraptokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster"
selfhostingphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/selfhosting"
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/token"
uploadconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
Expand Down Expand Up @@ -247,25 +248,39 @@ func (i *Init) Run(out io.Writer) error {
return err
}

// PHASE 4: Mark the master with the right label/taint
if err := markmasterphase.MarkMaster(client, i.cfg.NodeName); err != nil {
return err
}

// PHASE 4: Set up the bootstrap tokens
// PHASE 5: Set up the node bootstrap tokens
if !i.skipTokenPrint {
fmt.Printf("[token] Using token: %s\n", i.cfg.Token)
}

// Create the default node bootstrap token
tokenDescription := "The default bootstrap token generated by 'kubeadm init'."
if err := tokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL, kubeadmconstants.DefaultTokenUsages, tokenDescription); err != nil {
if err := nodebootstraptokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL, kubeadmconstants.DefaultTokenUsages, tokenDescription); err != nil {
return err
}
// Create RBAC rules that makes the bootstrap tokens able to post CSRs
if err := nodebootstraptokenphase.AllowBootstrapTokensToPostCSRs(client); err != nil {
return err
}
// Create RBAC rules that makes the bootstrap tokens able to get their CSRs approved automatically
if err := nodebootstraptokenphase.AutoApproveNodeBootstrapTokens(client, k8sVersion); err != nil {
return err
}

if err := tokenphase.CreateBootstrapConfigMapIfNotExists(client, kubeadmconstants.GetAdminKubeConfigPath()); err != nil {
// Create the cluster-info ConfigMap with the associated RBAC rules
if err := clusterinfophase.CreateBootstrapConfigMapIfNotExists(client, kubeadmconstants.GetAdminKubeConfigPath()); err != nil {
return err
}
if err := clusterinfophase.CreateClusterInfoRBACRules(client); err != nil {
return err
}

// PHASE 5: Install and deploy all addons, and configure things as necessary
// PHASE 6: Install and deploy all addons, and configure things as necessary

// Upload currently used configuration to the cluster
if err := uploadconfigphase.UploadConfiguration(i.cfg, client); err != nil {
Expand All @@ -285,7 +300,7 @@ func (i *Init) Run(out io.Writer) error {
return err
}

// Is deployment type self-hosted?
// PHASE 7: Make the control plane self-hosted if feature gate is enabled
if features.Enabled(i.cfg.FeatureFlags, features.SelfHosting) {
// Temporary control plane is up, now we create our self hosted control
// plane components and remove the static manifests:
Expand Down
6 changes: 6 additions & 0 deletions cmd/kubeadm/app/cmd/phases/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ load(
go_library(
name = "go_default_library",
srcs = [
"bootstraptoken.go",
"certs.go",
"kubeconfig.go",
"markmaster.go",
Expand All @@ -24,6 +25,8 @@ go_library(
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
Expand All @@ -35,7 +38,9 @@ go_library(
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//pkg/api:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],
)

Expand All @@ -44,6 +49,7 @@ go_test(
srcs = [
"certs_test.go",
"kubeconfig_test.go",
"phase_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
Expand Down
139 changes: 139 additions & 0 deletions cmd/kubeadm/app/cmd/phases/bootstraptoken.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
Copyright 2017 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 phases

import (
"fmt"

"github.com/spf13/cobra"

clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
versionutil "k8s.io/kubernetes/pkg/util/version"
)

// NewCmdBootstrapToken returns the Cobra command for running the mark-master phase
func NewCmdBootstrapToken() *cobra.Command {
var kubeConfigFile string
cmd := &cobra.Command{
Use: "bootstrap-token",
Short: "Manage kubeadm-specific Bootstrap Token functions.",
Aliases: []string{"bootstraptoken"},
RunE: subCmdRunE("bootstrap-token"),
}

cmd.PersistentFlags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")

// Add subcommands
cmd.AddCommand(NewSubCmdClusterInfo(&kubeConfigFile))
cmd.AddCommand(NewSubCmdNodeBootstrapToken(&kubeConfigFile))

return cmd
}

// NewSubCmdClusterInfo returns the Cobra command for running the cluster-info sub-phase
func NewSubCmdClusterInfo(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "cluster-info <clusterinfo-file>",
Short: "Uploads and exposes the cluster-info ConfigMap publicly from the given cluster-info file",
Aliases: []string{"clusterinfo"},
Run: func(cmd *cobra.Command, args []string) {
err := validateExactArgNumber(args, []string{"clusterinfo-file"})
kubeadmutil.CheckErr(err)

client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)

// Here it's safe to get args[0], since we've validated that the argument exists above in validateExactArgNumber
clusterInfoFile := args[0]
// Create the cluster-info ConfigMap or update if it already exists
err = clusterinfo.CreateBootstrapConfigMapIfNotExists(client, clusterInfoFile)
kubeadmutil.CheckErr(err)

// Create the RBAC rules that expose the cluster-info ConfigMap properly
err = clusterinfo.CreateClusterInfoRBACRules(client)
kubeadmutil.CheckErr(err)
},
}
return cmd
}

// NewSubCmdNodeBootstrapToken returns the Cobra command for running the node sub-phase
func NewSubCmdNodeBootstrapToken(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "node",
Short: "Manages Node Bootstrap Tokens",
Aliases: []string{"clusterinfo"},
RunE: subCmdRunE("node"),
}

cmd.AddCommand(NewSubCmdNodeBootstrapTokenPostCSRs(kubeConfigFile))
cmd.AddCommand(NewSubCmdNodeBootstrapTokenAutoApprove(kubeConfigFile))

return cmd
}

// NewSubCmdNodeBootstrapTokenPostCSRs returns the Cobra command for running the allow-post-csrs sub-phase
func NewSubCmdNodeBootstrapTokenPostCSRs(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "allow-post-csrs",
Short: "Configure RBAC to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials",
Run: func(cmd *cobra.Command, args []string) {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)

err = node.AllowBootstrapTokensToPostCSRs(client)
kubeadmutil.CheckErr(err)
},
}
return cmd
}

// NewSubCmdNodeBootstrapToken returns the Cobra command for running the allow-auto-approve sub-phase
func NewSubCmdNodeBootstrapTokenAutoApprove(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "allow-auto-approve",
Short: "Configure RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token",
Run: func(cmd *cobra.Command, args []string) {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)

clusterVersion, err := getClusterVersion(client)
kubeadmutil.CheckErr(err)

err = node.AutoApproveNodeBootstrapTokens(client, clusterVersion)
kubeadmutil.CheckErr(err)
},
}
return cmd
}

// getClusterVersion fetches the API server version and parses it
func getClusterVersion(client clientset.Interface) (*versionutil.Version, error) {
clusterVersionInfo, err := client.Discovery().ServerVersion()
if err != nil {
return nil, fmt.Errorf("failed to check server version: %v", err)
}
clusterVersion, err := versionutil.ParseSemantic(clusterVersionInfo.String())
if err != nil {
return nil, fmt.Errorf("failed to parse server version: %v", err)
}
return clusterVersion, nil
}
14 changes: 5 additions & 9 deletions cmd/kubeadm/app/cmd/phases/markmaster.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/spf13/cobra"

markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)

Expand All @@ -33,16 +34,11 @@ func NewCmdMarkMaster() *cobra.Command {
Short: "Create KubeConfig files from given credentials.",
Aliases: []string{"markmaster"},
RunE: func(_ *cobra.Command, args []string) error {
if len(args) < 1 || len(args[0]) == 0 {
return fmt.Errorf("missing required argument node-name")
}
if len(args) > 1 {
return fmt.Errorf("too many arguments, only one argument supported: node-name")
}
err := validateExactArgNumber(args, []string{"node-name"})
kubeadmutil.CheckErr(err)

client, err := kubeconfigutil.ClientSetFromFile(kubeConfigFile)
if err != nil {
return err
}
kubeadmutil.CheckErr(err)

nodeName := args[0]
fmt.Printf("[markmaster] Will mark node %s as master by adding a label and a taint\n", nodeName)
Expand Down
21 changes: 21 additions & 0 deletions cmd/kubeadm/app/cmd/phases/phase.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
)

// NewCmdPhase returns the cobra command for the "kubeadm phase" command (currently alpha-gated)
func NewCmdPhase(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "phase",
Expand All @@ -36,6 +37,7 @@ func NewCmdPhase(out io.Writer) *cobra.Command {
cmd.AddCommand(NewCmdSelfhosting())
cmd.AddCommand(NewCmdMarkMaster())
cmd.AddCommand(NewCmdUploadConfig())
cmd.AddCommand(NewCmdBootstrapToken())

return cmd
}
Expand All @@ -54,3 +56,22 @@ func subCmdRunE(name string) func(*cobra.Command, []string) error {
return fmt.Errorf("invalid subcommand: %q", args[0])
}
}

// validateExactArgNumber validates that the required top-level arguments are specified
func validateExactArgNumber(args []string, supportedArgs []string) error {
validArgs := 0
// Disregard possible "" arguments; they are invalid
for _, arg := range args {
if len(arg) > 0 {
validArgs++
}
}

if validArgs < len(supportedArgs) {
return fmt.Errorf("missing one or more required arguments. Required arguments: %v", supportedArgs)
}
if validArgs > len(supportedArgs) {
return fmt.Errorf("too many arguments, only %d argument(s) supported: %v", validArgs, supportedArgs)
}
return nil
}
64 changes: 64 additions & 0 deletions cmd/kubeadm/app/cmd/phases/phase_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2017 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 phases

import (
"testing"
)

func TestValidateExactArgNumber(t *testing.T) {
var tests = []struct {
args, supportedArgs []string
expectedErr bool
}{
{ // one arg given and one arg expected
args: []string{"my-node-1234"},
supportedArgs: []string{"node-name"},
expectedErr: false,
},
{ // two args given and two args expected
args: []string{"my-node-1234", "foo"},
supportedArgs: []string{"node-name", "second-toplevel-arg"},
expectedErr: false,
},
{ // too few supplied args
args: []string{},
supportedArgs: []string{"node-name"},
expectedErr: true,
},
{ // too few non-empty args
args: []string{""},
supportedArgs: []string{"node-name"},
expectedErr: true,
},
{ // too many args
args: []string{"my-node-1234", "foo"},
supportedArgs: []string{"node-name"},
expectedErr: true,
},
}
for _, rt := range tests {
actual := validateExactArgNumber(rt.args, rt.supportedArgs)
if (actual != nil) != rt.expectedErr {
t.Errorf(
"failed validateExactArgNumber:\n\texpected error: %t\n\t actual error: %t",
rt.expectedErr,
(actual != nil),
)
}
}
}
Loading

0 comments on commit 938bc61

Please sign in to comment.