kubeadm: refactor discovery behind an interface

pull/6/head
Mike Danese 2016-11-08 11:27:16 -08:00
parent 82edbf1ddc
commit 690c7e578b
39 changed files with 784 additions and 506 deletions

View File

@ -33,7 +33,6 @@ type EnvParams struct {
type MasterConfiguration struct {
metav1.TypeMeta
Secrets Secrets
API API
Discovery Discovery
Etcd Etcd
@ -45,11 +44,27 @@ type MasterConfiguration struct {
type API struct {
AdvertiseAddresses []string
ExternalDNSNames []string
BindPort int32
Port int32
}
type Discovery struct {
BindPort int32
HTTPS *HTTPSDiscovery
File *FileDiscovery
Token *TokenDiscovery
}
type HTTPSDiscovery struct {
URL string
}
type FileDiscovery struct {
Path string
}
type TokenDiscovery struct {
ID string
Secret string
Addresses []string
}
type Networking struct {
@ -65,20 +80,10 @@ type Etcd struct {
KeyFile string
}
type Secrets struct {
GivenToken string // dot-separated `<TokenID>.<Token>` set by the user
TokenID string // optional on master side, will be generated if not specified
Token []byte // optional on master side, will be generated if not specified
BearerToken string // set based on Token
}
type NodeConfiguration struct {
metav1.TypeMeta
MasterAddresses []string
Secrets Secrets
APIPort int32
DiscoveryPort int32
Discovery Discovery
}
// ClusterInfo TODO add description

View File

@ -33,7 +33,6 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
RegisterDefaults(scheme)
return scheme.AddDefaultingFuncs(
SetDefaults_MasterConfiguration,
SetDefaults_NodeConfiguration,
)
}
@ -42,12 +41,8 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
obj.KubernetesVersion = DefaultKubernetesVersion
}
if obj.API.BindPort == 0 {
obj.API.BindPort = DefaultAPIBindPort
}
if obj.Discovery.BindPort == 0 {
obj.Discovery.BindPort = DefaultDiscoveryBindPort
if obj.API.Port == 0 {
obj.API.Port = DefaultAPIBindPort
}
if obj.Networking.ServiceSubnet == "" {
@ -58,13 +53,3 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
obj.Networking.DNSDomain = DefaultServiceDNSDomain
}
}
func SetDefaults_NodeConfiguration(obj *NodeConfiguration) {
if obj.APIPort == 0 {
obj.APIPort = DefaultAPIBindPort
}
if obj.DiscoveryPort == 0 {
obj.DiscoveryPort = DefaultDiscoveryBindPort
}
}

View File

@ -23,10 +23,9 @@ import (
type MasterConfiguration struct {
metav1.TypeMeta `json:",inline"`
Secrets Secrets `json:"secrets"`
API API `json:"api"`
Etcd Etcd `json:"etcd"`
Discovery Discovery `json:"discovery"`
Etcd Etcd `json:"etcd"`
Networking Networking `json:"networking"`
KubernetesVersion string `json:"kubernetesVersion"`
CloudProvider string `json:"cloudProvider"`
@ -35,11 +34,27 @@ type MasterConfiguration struct {
type API struct {
AdvertiseAddresses []string `json:"advertiseAddresses"`
ExternalDNSNames []string `json:"externalDNSNames"`
BindPort int32 `json:"bindPort"`
Port int32 `json:"port"`
}
type Discovery struct {
BindPort int32 `json:"bindPort"`
HTTPS *HTTPSDiscovery `json:"https"`
File *FileDiscovery `json:"file"`
Token *TokenDiscovery `json:"token"`
}
type HTTPSDiscovery struct {
URL string `json:"url"`
}
type FileDiscovery struct {
Path string `json:"path"`
}
type TokenDiscovery struct {
ID string `json:"id"`
Secret string `json:"secret"`
Addresses []string `json:"addresses"`
}
type Networking struct {
@ -65,10 +80,7 @@ type Secrets struct {
type NodeConfiguration struct {
metav1.TypeMeta `json:",inline"`
MasterAddresses []string `json:"masterAddresses"`
Secrets Secrets `json:"secrets"`
APIPort int32 `json:"apiPort"`
DiscoveryPort int32 `json:"discoveryPort"`
Discovery Discovery `json:"discovery"`
}
// ClusterInfo TODO add description

View File

@ -29,14 +29,9 @@ import (
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&MasterConfiguration{}, func(obj interface{}) { SetObjectDefaults_MasterConfiguration(obj.(*MasterConfiguration)) })
scheme.AddTypeDefaultingFunc(&NodeConfiguration{}, func(obj interface{}) { SetObjectDefaults_NodeConfiguration(obj.(*NodeConfiguration)) })
return nil
}
func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) {
SetDefaults_MasterConfiguration(in)
}
func SetObjectDefaults_NodeConfiguration(in *NodeConfiguration) {
SetDefaults_NodeConfiguration(in)
}

View File

@ -0,0 +1,18 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["validation.go"],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//pkg/util/validation/field:go_default_library",
],
)

View File

@ -0,0 +1,70 @@
/*
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 validation
import (
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/pkg/util/validation/field"
)
func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
return allErrs
}
func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
return allErrs
}
func ValidateDiscovery(c *kubeadm.Discovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
var count int
if c.Token != nil {
allErrs = append(allErrs, ValidateTokenDiscovery(c.Token, fldPath)...)
count++
}
if c.File != nil {
allErrs = append(allErrs, ValidateFileDiscovery(c.File, fldPath)...)
count++
}
if c.HTTPS != nil {
allErrs = append(allErrs, ValidateHTTPSDiscovery(c.HTTPS, fldPath)...)
count++
}
if count != 1 {
allErrs = append(allErrs, field.Invalid(fldPath, nil, "exactly one discovery strategy can be provided"))
}
return allErrs
}
func ValidateFileDiscovery(c *kubeadm.FileDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
return allErrs
}
func ValidateHTTPSDiscovery(c *kubeadm.HTTPSDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
return allErrs
}
func ValidateTokenDiscovery(c *kubeadm.TokenDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
return allErrs
}

View File

@ -22,12 +22,15 @@ go_library(
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
"//cmd/kubeadm/app/cmd/flags:go_default_library",
"//cmd/kubeadm/app/discovery:go_default_library",
"//cmd/kubeadm/app/master:go_default_library",
"//cmd/kubeadm/app/node:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/flag:go_default_library",

View File

@ -17,9 +17,7 @@ limitations under the License.
package cmd
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
@ -28,7 +26,9 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/flags"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
@ -37,18 +37,6 @@ import (
netutil "k8s.io/kubernetes/pkg/util/net"
)
const (
joinArgsTemplateLiteral = `--token={{.Cfg.Secrets.GivenToken -}}
{{if ne .Cfg.API.BindPort .DefaultAPIBindPort -}}
{{" --api-port="}}{{.Cfg.API.BindPort -}}
{{end -}}
{{if ne .Cfg.Discovery.BindPort .DefaultDiscoveryBindPort -}}
{{" --discovery-port="}}{{.Cfg.Discovery.BindPort -}}
{{end -}}
{{" "}}{{index .Cfg.API.AdvertiseAddresses 0 -}}
`
)
var (
initDoneMsgf = dedent.Dedent(`
Your Kubernetes master has initialized successfully!
@ -59,7 +47,7 @@ var (
You can now join any number of machines by running the following on each node:
kubeadm join %s
kubeadm join --discovery %s
`)
)
@ -78,14 +66,11 @@ func NewCmdInit(out io.Writer) *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
i, err := NewInit(cfgPath, &cfg, skipPreFlight)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(i.Validate())
kubeadmutil.CheckErr(i.Run(out))
},
}
cmd.PersistentFlags().StringVar(
&cfg.Secrets.GivenToken, "token", cfg.Secrets.GivenToken,
"Shared secret used to secure cluster bootstrap; if none is provided, one will be generated for you",
)
cmd.PersistentFlags().StringSliceVar(
&cfg.API.AdvertiseAddresses, "api-advertise-addresses", cfg.API.AdvertiseAddresses,
"The IP addresses to advertise, in case autodetection fails",
@ -148,14 +133,9 @@ func NewCmdInit(out io.Writer) *cobra.Command {
"skip preflight checks normally run before modifying the system",
)
cmd.PersistentFlags().Int32Var(
&cfg.API.BindPort, "api-port", cfg.API.BindPort,
"Port for API to bind to",
)
cmd.PersistentFlags().Int32Var(
&cfg.Discovery.BindPort, "discovery-port", cfg.Discovery.BindPort,
"Port for JWS discovery service to bind to",
cmd.PersistentFlags().Var(
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
"The discovery method kubeadm will use for connecting nodes to the master",
)
return cmd
@ -228,17 +208,17 @@ func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight
return &Init{cfg: cfg}, nil
}
// joinArgsData denotes a data object which is needed by function generateJoinArgs to generate kubeadm join arguments.
type joinArgsData struct {
Cfg *kubeadmapi.MasterConfiguration
DefaultAPIBindPort int32
DefaultDiscoveryBindPort int32
func (i *Init) Validate() error {
return validation.ValidateMasterConfiguration(i.cfg).ToAggregate()
}
// Run executes master node provisioning, including certificates, needed static pod manifests, etc.
func (i *Init) Run(out io.Writer) error {
if err := kubemaster.CreateTokenAuthFile(&i.cfg.Secrets); err != nil {
return err
if i.cfg.Discovery.Token != nil {
if err := kubemaster.CreateTokenAuthFile(i.cfg.Discovery.Token); err != nil {
return err
}
}
if err := kubemaster.WriteStaticPodManifests(i.cfg); err != nil {
@ -275,34 +255,25 @@ func (i *Init) Run(out io.Writer) error {
return err
}
schedulePodsOnMaster := false
if err := kubemaster.UpdateMasterRoleLabelsAndTaints(client, schedulePodsOnMaster); err != nil {
if err := kubemaster.UpdateMasterRoleLabelsAndTaints(client, false); err != nil {
return err
}
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client, caCert); err != nil {
return err
if i.cfg.Discovery.Token != nil {
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client, caCert); err != nil {
return err
}
}
if err := kubemaster.CreateEssentialAddons(i.cfg, client); err != nil {
return err
}
data := joinArgsData{i.cfg, kubeadmapiext.DefaultAPIBindPort, kubeadmapiext.DefaultDiscoveryBindPort}
if joinArgs, err := generateJoinArgs(data); err != nil {
return err
} else {
fmt.Fprintf(out, initDoneMsgf, joinArgs)
}
fmt.Fprintf(out, initDoneMsgf, generateJoinArgs(i.cfg))
return nil
}
// generateJoinArgs generates kubeadm join arguments
func generateJoinArgs(data joinArgsData) (string, error) {
joinArgsTemplate := template.Must(template.New("joinArgsTemplate").Parse(joinArgsTemplateLiteral))
var b bytes.Buffer
if err := joinArgsTemplate.Execute(&b, data); err != nil {
return "", err
}
return b.String(), nil
func generateJoinArgs(cfg *kubeadmapi.MasterConfiguration) string {
return discovery.NewDiscoveryValue(&cfg.Discovery).String()
}

View File

@ -26,10 +26,13 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/runtime"
)
@ -60,15 +63,11 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
j, err := NewJoin(cfgPath, args, &cfg, skipPreFlight)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(j.Validate())
kubeadmutil.CheckErr(j.Run(out))
},
}
cmd.PersistentFlags().StringVar(
&cfg.Secrets.GivenToken, "token", cfg.Secrets.GivenToken,
"(required) Shared secret used to secure bootstrap. Must match the output of 'kubeadm init'",
)
cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
cmd.PersistentFlags().BoolVar(
@ -76,14 +75,9 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
"skip preflight checks normally run before modifying the system",
)
cmd.PersistentFlags().Int32Var(
&cfg.APIPort, "api-port", cfg.APIPort,
"(optional) API server port on the master",
)
cmd.PersistentFlags().Int32Var(
&cfg.DiscoveryPort, "discovery-port", cfg.DiscoveryPort,
"(optional) Discovery port on the master",
cmd.PersistentFlags().Var(
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
"The discovery method kubeadm will use for connecting nodes to the master",
)
return cmd
@ -107,14 +101,6 @@ func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, s
}
}
if len(args) == 0 && len(cfg.MasterAddresses) == 0 {
return nil, fmt.Errorf("must specify master address (see --help)")
}
cfg.MasterAddresses = append(cfg.MasterAddresses, args...)
if len(cfg.MasterAddresses) > 1 {
return nil, fmt.Errorf("must not specify more than one master address (see --help)")
}
if !skipPreFlight {
fmt.Println("[preflight] Running pre-flight checks")
@ -134,40 +120,46 @@ func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, s
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet()
ok, err := kubeadmutil.UseGivenTokenIfValid(&cfg.Secrets)
if !ok {
if err != nil {
return nil, fmt.Errorf("%v (see --help)", err)
}
return nil, fmt.Errorf("Must specify --token (see --help)")
}
return &Join{cfg: cfg}, nil
}
func (j *Join) Validate() error {
return validation.ValidateNodeConfiguration(j.cfg).ToAggregate()
}
// Run executes worked node provisioning and tries to join an existing cluster.
func (j *Join) Run(out io.Writer) error {
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(j.cfg)
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(j.cfg.Discovery.Token)
if err != nil {
return err
}
connectionDetails, err := kubenode.EstablishMasterConnection(j.cfg, clusterInfo)
if err != nil {
return err
var cfg *clientcmdapi.Config
// TODO: delete this first block when we move Token to the discovery interface
if j.cfg.Discovery.Token != nil {
connectionDetails, err := kubenode.EstablishMasterConnection(j.cfg.Discovery.Token, clusterInfo)
if err != nil {
return err
}
err = kubenode.CheckForNodeNameDuplicates(connectionDetails)
if err != nil {
return err
}
cfg, err = kubenode.PerformTLSBootstrapDeprecated(connectionDetails)
if err != nil {
return err
}
} else {
cfg, err = discovery.For(j.cfg.Discovery)
if err != nil {
return err
}
if err := kubenode.PerformTLSBootstrap(cfg); err != nil {
return err
}
}
err = kubenode.CheckForNodeNameDuplicates(connectionDetails)
if err != nil {
return err
}
kubeconfig, err := kubenode.PerformTLSBootstrap(connectionDetails)
if err != nil {
return err
}
err = kubeadmutil.WriteKubeconfigIfNotExists("kubelet", kubeconfig)
err = kubeadmutil.WriteKubeconfigIfNotExists("kubelet", cfg)
if err != nil {
return err
}

View File

@ -75,12 +75,12 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
}
func RunGenerateToken(out io.Writer) error {
s := &kubeadmapi.Secrets{}
err := util.GenerateToken(s)
d := &kubeadmapi.TokenDiscovery{}
err := util.GenerateToken(d)
if err != nil {
return err
}
fmt.Fprintln(out, s.GivenToken)
fmt.Fprintln(out, util.BearerToken(d))
return nil
}

View File

@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"discovery.go",
"flags.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/discovery/file:go_default_library",
"//cmd/kubeadm/app/discovery/https:go_default_library",
"//cmd/kubeadm/app/discovery/token:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//vendor:github.com/spf13/pflag",
],
)
go_test(
name = "go_default_test",
srcs = ["flags_test.go"],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor:github.com/davecgh/go-spew/spew",
],
)

View File

@ -0,0 +1,31 @@
/*
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 discovery
import (
"fmt"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
)
func For(c kubeadmapi.Discovery) (*clientcmdapi.Config, error) {
switch {
default:
return nil, fmt.Errorf("unimplemented")
}
}

View File

@ -0,0 +1,15 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["file.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)

View File

@ -0,0 +1,30 @@
/*
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 file
import (
"net/url"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func Parse(u *url.URL, c *kubeadm.Discovery) error {
c.File = &kubeadm.FileDiscovery{
Path: u.Path,
}
return nil
}

View File

@ -0,0 +1,83 @@
/*
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 discovery
import (
"fmt"
"net/url"
"strings"
"github.com/spf13/pflag"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/file"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/https"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/token"
)
type discoveryValue struct {
v *kubeadm.Discovery
}
func NewDiscoveryValue(d *kubeadm.Discovery) pflag.Value {
return &discoveryValue{
v: d,
}
}
func (d *discoveryValue) String() string {
switch {
case d.v.HTTPS != nil:
return d.v.HTTPS.URL
case d.v.File != nil:
return "file://" + d.v.File.Path
case d.v.Token != nil:
return fmt.Sprintf("token://%s:%s@%s", d.v.Token.ID, d.v.Token.Secret, strings.Join(d.v.Token.Addresses, ","))
default:
return "unknown"
}
}
func (d *discoveryValue) Set(s string) error {
var kd kubeadm.Discovery
if err := ParseURL(&kd, s); err != nil {
return err
}
*d.v = kd
return nil
}
func (d *discoveryValue) Type() string {
return "discovery"
}
func ParseURL(d *kubeadm.Discovery, s string) error {
u, err := url.Parse(s)
if err != nil {
return err
}
switch u.Scheme {
case "https":
return https.Parse(u, d)
case "file":
return file.Parse(u, d)
case "token":
return token.Parse(u, d)
default:
return fmt.Errorf("unknown discovery scheme")
}
}

View File

@ -0,0 +1,83 @@
/*
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 discovery
import (
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestParseURL(t *testing.T) {
cases := []struct {
url string
expect kubeadm.Discovery
expectErr bool
}{
{
url: "token://",
expect: kubeadm.Discovery{
Token: &kubeadm.TokenDiscovery{},
},
},
{
url: "token://c05de9:ab224260fb3cd718@192.168.0.1:6555,191.168.0.2:6443",
expect: kubeadm.Discovery{
Token: &kubeadm.TokenDiscovery{
ID: "c05de9",
Secret: "ab224260fb3cd718",
Addresses: []string{
"192.168.0.1:6555",
"191.168.0.2:6443",
},
},
},
},
{
url: "file:///foo/bar/baz",
expect: kubeadm.Discovery{
File: &kubeadm.FileDiscovery{
Path: "/foo/bar/baz",
},
},
},
{
url: "https://storage.googleapis.com/kubeadm-disco/clusters/217651295213",
expect: kubeadm.Discovery{
HTTPS: &kubeadm.HTTPSDiscovery{
URL: "https://storage.googleapis.com/kubeadm-disco/clusters/217651295213",
},
},
},
}
for _, c := range cases {
var d kubeadm.Discovery
if err := ParseURL(&d, c.url); err != nil {
if !c.expectErr {
t.Errorf("unexpected error parsing discovery url: %v", err)
}
continue
}
if !reflect.DeepEqual(d, c.expect) {
t.Errorf("expected discovery config to be equeal but got:\n\ta: %s\n\tb: %s", spew.Sdump(d), spew.Sdump(c.expect))
}
}
}

View File

@ -0,0 +1,15 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["https.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)

View File

@ -0,0 +1,30 @@
/*
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 https
import (
"net/url"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func Parse(u *url.URL, c *kubeadm.Discovery) error {
c.HTTPS = &kubeadm.HTTPSDiscovery{
URL: u.String(),
}
return nil
}

View File

@ -0,0 +1,15 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["token.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)

View File

@ -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 token
import (
"net/url"
"strings"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func Parse(u *url.URL, c *kubeadm.Discovery) error {
var (
hosts []string
tokenID, token string
)
if u.Host != "" {
hosts = strings.Split(u.Host, ",")
}
if u.User != nil {
if p, ok := u.User.Password(); ok {
tokenID = u.User.Username()
token = p
}
}
c.Token = &kubeadm.TokenDiscovery{
ID: tokenID,
Secret: token,
Addresses: hosts,
}
return nil
}

View File

@ -24,6 +24,7 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
@ -43,18 +44,18 @@ const (
kubeDiscoverySecretName = "clusterinfo"
)
func encodeKubeDiscoverySecretData(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate) map[string][]byte {
func encodeKubeDiscoverySecretData(dcfg *kubeadmapi.TokenDiscovery, apicfg kubeadmapi.API, caCert *x509.Certificate) map[string][]byte {
var (
data = map[string][]byte{}
endpointList = []string{}
tokenMap = map[string]string{}
)
for _, addr := range cfg.API.AdvertiseAddresses {
endpointList = append(endpointList, fmt.Sprintf("https://%s:%d", addr, cfg.API.BindPort))
for _, addr := range apicfg.AdvertiseAddresses {
endpointList = append(endpointList, fmt.Sprintf("https://%s:%d", addr, apicfg.Port))
}
tokenMap[cfg.Secrets.TokenID] = cfg.Secrets.BearerToken
tokenMap[dcfg.ID] = dcfg.Secret
data["endpoint-list.json"], _ = json.Marshal(endpointList)
data["token-map.json"], _ = json.Marshal(tokenMap)
@ -83,7 +84,7 @@ func newKubeDiscoveryPodSpec(cfg *kubeadmapi.MasterConfiguration) v1.PodSpec {
Ports: []v1.ContainerPort{
// TODO when CNI issue (#31307) is resolved, we should consider adding
// `HostIP: s.API.AdvertiseAddrs[0]`, if there is only one address`
{Name: "http", ContainerPort: kubeadmapiext.DefaultDiscoveryBindPort, HostPort: cfg.Discovery.BindPort},
{Name: "http", ContainerPort: kubeadmapiext.DefaultDiscoveryBindPort, HostPort: kubeadmutil.DiscoveryPort(cfg.Discovery.Token)},
},
SecurityContext: &v1.SecurityContext{
SELinuxOptions: &v1.SELinuxOptions{
@ -110,7 +111,7 @@ func newKubeDiscovery(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certific
Secret: &v1.Secret{
ObjectMeta: v1.ObjectMeta{Name: kubeDiscoverySecretName},
Type: v1.SecretTypeOpaque,
Data: encodeKubeDiscoverySecretData(cfg, caCert),
Data: encodeKubeDiscoverySecretData(cfg.Discovery.Token, cfg.API, caCert),
},
}

View File

@ -23,62 +23,6 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestEncodeKubeDiscoverySecretData(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{BindPort: 123, AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
},
expected: true,
},
}
for _, rt := range tests {
caCert := &x509.Certificate{}
actual := encodeKubeDiscoverySecretData(rt.cfg, caCert)
if (actual != nil) != rt.expected {
t.Errorf(
"failed encodeKubeDiscoverySecretData, return map[string][]byte was nil",
)
}
}
}
func TestNewKubeDiscoveryPodSpec(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
p int32
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{
Discovery: kubeadmapi.Discovery{BindPort: 123},
},
p: 123,
},
{
cfg: &kubeadmapi.MasterConfiguration{
Discovery: kubeadmapi.Discovery{BindPort: 456},
},
p: 456,
},
}
for _, rt := range tests {
actual := newKubeDiscoveryPodSpec(rt.cfg)
if actual.Containers[0].Ports[0].HostPort != rt.p {
t.Errorf(
"failed newKubeDiscoveryPodSpec:\n\texpected: %d\n\t actual: %d",
rt.p,
actual.Containers[0].Ports[0].HostPort,
)
}
}
}
func TestNewKubeDiscovery(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
@ -87,8 +31,9 @@ func TestNewKubeDiscovery(t *testing.T) {
}{
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{BindPort: 123, AdvertiseAddresses: []string{"10.0.0.1"}},
API: kubeadmapi.API{Port: 123, AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
Discovery: kubeadmapi.Discovery{Token: &kubeadmapi.TokenDiscovery{}},
},
caCert: &x509.Certificate{},
},

View File

@ -34,7 +34,7 @@ func CreateCertsAndConfigForClients(cfg kubeadmapi.API, clientNames []string, ca
// TODO this is not great, but there is only one address we can use here
// so we'll pick the first one, there is much of chance to have an empty
// slice by the time this gets called
fmt.Sprintf("https://%s:%d", cfg.AdvertiseAddresses[0], cfg.BindPort),
fmt.Sprintf("https://%s:%d", cfg.AdvertiseAddresses[0], cfg.Port),
certutil.EncodeCertPEM(caCert),
)

View File

@ -292,7 +292,7 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration) []string {
"--tls-cert-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver.pem",
"--tls-private-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem",
"--token-auth-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/tokens.csv",
fmt.Sprintf("--secure-port=%d", cfg.API.BindPort),
fmt.Sprintf("--secure-port=%d", cfg.API.Port),
"--allow-privileged",
)

View File

@ -364,7 +364,7 @@ func TestGetAPIServerCommand(t *testing.T) {
}{
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{BindPort: 123},
API: kubeadm.API{Port: 123},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
},
expected: []string{
@ -384,7 +384,7 @@ func TestGetAPIServerCommand(t *testing.T) {
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{BindPort: 123, AdvertiseAddresses: []string{"foo"}},
API: kubeadm.API{Port: 123, AdvertiseAddresses: []string{"foo"}},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
},
expected: []string{
@ -405,7 +405,7 @@ func TestGetAPIServerCommand(t *testing.T) {
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{BindPort: 123},
API: kubeadm.API{Port: 123},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
Etcd: kubeadm.Etcd{CertFile: "fiz", KeyFile: "faz"},
},
@ -429,7 +429,7 @@ func TestGetAPIServerCommand(t *testing.T) {
// Make sure --kubelet-preferred-address-types
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{BindPort: 123, AdvertiseAddresses: []string{"foo"}},
API: kubeadm.API{Port: 123, AdvertiseAddresses: []string{"foo"}},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
KubernetesVersion: "v1.5.3",
},

View File

@ -28,34 +28,32 @@ import (
"k8s.io/kubernetes/pkg/util/uuid"
)
func generateTokenIfNeeded(s *kubeadmapi.Secrets) error {
ok, err := kubeadmutil.UseGivenTokenIfValid(s)
// TODO(phase1+) @krousey: I know it won't happen with the way it is currently implemented, but this doesn't handle case where ok is true and err is non-nil.
if !ok {
if err != nil {
return err
}
err = kubeadmutil.GenerateToken(s)
if err != nil {
return err
}
fmt.Printf("[tokens] Generated token: %q\n", s.GivenToken)
} else {
fmt.Println("[tokens] Accepted provided token")
func generateTokenIfNeeded(d *kubeadmapi.TokenDiscovery) error {
ok, err := kubeadmutil.IsTokenValid(d)
if err != nil {
return err
}
if ok {
fmt.Println("[tokens] Accepted provided token")
return nil
}
if err := kubeadmutil.GenerateToken(d); err != nil {
fmt.Printf("[tokens] Generated token: %q\n", kubeadmutil.BearerToken(d))
return nil
} else {
return err
}
return nil
}
func CreateTokenAuthFile(s *kubeadmapi.Secrets) error {
func CreateTokenAuthFile(d *kubeadmapi.TokenDiscovery) error {
tokenAuthFilePath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath, "tokens.csv")
if err := generateTokenIfNeeded(s); err != nil {
if err := generateTokenIfNeeded(d); err != nil {
return fmt.Errorf("failed to generate token(s) [%v]", err)
}
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.HostPKIPath, 0700); err != nil {
return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.HostPKIPath, err)
}
serialized := []byte(fmt.Sprintf("%s,kubeadm-node-csr,%s,system:kubelet-bootstrap\n", s.BearerToken, uuid.NewUUID()))
serialized := []byte(fmt.Sprintf("%s,kubeadm-node-csr,%s,system:kubelet-bootstrap\n", kubeadmutil.BearerToken(d), uuid.NewUUID()))
// DumpReaderToFile create a file with mode 0600
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), tokenAuthFilePath); err != nil {
return fmt.Errorf("failed to save token auth file (%q) [%v]", tokenAuthFilePath, err)

View File

@ -17,67 +17,44 @@ limitations under the License.
package master
import (
"fmt"
"io/ioutil"
"os"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestGenerateTokenIfNeeded(t *testing.T) {
var tests = []struct {
s kubeadmapi.Secrets
expected bool
}{
{kubeadmapi.Secrets{GivenToken: "noperiod"}, false}, // not 2-part '.' format
{kubeadmapi.Secrets{GivenToken: "abcd.a"}, false}, // len(tokenID) != 6
{kubeadmapi.Secrets{GivenToken: "abcdef.a"}, true},
{kubeadmapi.Secrets{GivenToken: ""}, true},
}
for _, rt := range tests {
actual := generateTokenIfNeeded(&rt.s)
if (actual == nil) != rt.expected {
t.Errorf(
"failed UseGivenTokenIfValid:\n\texpected: %t\n\t actual: %t\n\t token:%s",
rt.expected,
(actual == nil),
rt.s.GivenToken,
)
func TestValidTokenPopulatesSecrets(t *testing.T) {
t.Run("provided", func(t *testing.T) {
expectedID := "123456"
expectedSecret := "0123456789abcdef"
s := &kubeadmapi.TokenDiscovery{
ID: expectedID,
Secret: expectedSecret,
}
}
}
func TestCreateTokenAuthFile(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.Remove(tmpdir)
// set up tmp GlobalEnvParams values for testing
oldEnv := kubeadmapi.GlobalEnvParams
kubeadmapi.GlobalEnvParams.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
var tests = []struct {
s kubeadmapi.Secrets
expected bool
}{
{kubeadmapi.Secrets{GivenToken: "noperiod"}, false}, // not 2-part '.' format
{kubeadmapi.Secrets{GivenToken: "abcd.a"}, false}, // len(tokenID) != 6
{kubeadmapi.Secrets{GivenToken: "abcdef.a"}, true},
{kubeadmapi.Secrets{GivenToken: ""}, true},
}
for _, rt := range tests {
actual := CreateTokenAuthFile(&rt.s)
if (actual == nil) != rt.expected {
t.Errorf(
"failed WriteKubeconfigIfNotExists with an error:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
err := generateTokenIfNeeded(s)
if err != nil {
t.Errorf("generateTokenIfNeeded gave an error for a valid token: %v", err)
}
}
if s.ID != expectedID {
t.Errorf("generateTokenIfNeeded did not populate the TokenID correctly; expected [%s] but got [%s]", expectedID, s.ID)
}
if s.Secret != expectedSecret {
t.Errorf("generateTokenIfNeeded did not populate the Token correctly; expected %v but got %v", expectedSecret, s.Secret)
}
})
t.Run("not provided", func(t *testing.T) {
s := &kubeadmapi.TokenDiscovery{}
err := generateTokenIfNeeded(s)
if err != nil {
t.Errorf("generateTokenIfNeeded gave an error for a valid token: %v", err)
}
if s.ID != "" {
t.Errorf("generateTokenIfNeeded did not populate the TokenID correctly; expected ID to be non-empty")
}
if s.Secret != "" {
t.Errorf("generateTokenIfNeeded did not populate the Token correctly; expected Secret to be non-empty")
}
})
}

View File

@ -50,7 +50,7 @@ const retryTimeout = 5
// The function builds a client for every endpoint and concurrently keeps trying to connect to any one
// of the provided endpoints. Blocks until at least one connection is established, then it stops the
// connection attempts for other endpoints.
func EstablishMasterConnection(s *kubeadmapi.NodeConfiguration, clusterInfo *kubeadmapi.ClusterInfo) (*ConnectionDetails, error) {
func EstablishMasterConnection(c *kubeadmapi.TokenDiscovery, clusterInfo *kubeadmapi.ClusterInfo) (*ConnectionDetails, error) {
hostName, err := os.Hostname()
if err != nil {
return nil, fmt.Errorf("failed to get node hostname [%v]", err)
@ -65,7 +65,7 @@ func EstablishMasterConnection(s *kubeadmapi.NodeConfiguration, clusterInfo *kub
result := make(chan *ConnectionDetails)
var wg sync.WaitGroup
for _, endpoint := range endpoints {
clientSet, err := createClients(caCert, endpoint, s.Secrets.BearerToken, nodeName)
clientSet, err := createClients(caCert, endpoint, kubeadmutil.BearerToken(c), nodeName)
if err != nil {
fmt.Printf("[bootstrap] Warning: %s. Skipping endpoint %s\n", err, endpoint)
continue

View File

@ -107,7 +107,7 @@ func TestEstablishMasterConnection(t *testing.T) {
},
}
for _, rt := range tests {
s := &kubeadmapi.NodeConfiguration{}
s := &kubeadmapi.TokenDiscovery{}
c := &kubeadmapi.ClusterInfo{Endpoints: []string{rt.e}, CertificateAuthorities: []string{rt.c}}
_, actual := EstablishMasterConnection(s, c)
if (actual == nil) != rt.expect {

View File

@ -18,16 +18,18 @@ package node
import (
"fmt"
"os"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubelet/util/csr"
"k8s.io/kubernetes/pkg/types"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
// PerformTLSBootstrap executes a certificate signing request with the
// provided connection details.
func PerformTLSBootstrap(connection *ConnectionDetails) (*clientcmdapi.Config, error) {
func PerformTLSBootstrapDeprecated(connection *ConnectionDetails) (*clientcmdapi.Config, error) {
csrClient := connection.CertClient.CertificateSigningRequests()
fmt.Println("[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request")
@ -55,3 +57,49 @@ func PerformTLSBootstrap(connection *ConnectionDetails) (*clientcmdapi.Config, e
return finalConfig, nil
}
// PerformTLSBootstrap executes a certificate signing request with the
// provided connection details.
func PerformTLSBootstrap(cfg *clientcmdapi.Config) error {
hostName, err := os.Hostname()
if err != nil {
return err
}
name := types.NodeName(hostName)
rc, err := clientcmd.NewDefaultClientConfig(*cfg, nil).ClientConfig()
if err != nil {
return err
}
c, err := clientset.NewForConfig(rc)
if err != nil {
return err
}
fmt.Println("<node/csr> created API client to obtain unique certificate for this node, generating keys and certificate signing request")
key, err := certutil.MakeEllipticPrivateKeyPEM()
if err != nil {
return fmt.Errorf("<node/csr> failed to generating private key [%v]", err)
}
cert, err := csr.RequestNodeCertificate(c.Certificates().CertificateSigningRequests(), key, name)
if err != nil {
return fmt.Errorf("<node/csr> failed to request signed certificate from the API server [%v]", err)
}
fmtCert, err := certutil.FormatBytesCert(cert)
if err != nil {
return fmt.Errorf("<node/csr> failed to format certificate [%v]", err)
}
fmt.Printf("<node/csr> received signed certificate from the API server:\n%s\n", fmtCert)
fmt.Println("<node/csr> generating kubelet configuration")
cfg.AuthInfos["kubelet"] = &clientcmdapi.AuthInfo{
ClientKeyData: key,
ClientCertificateData: []byte(fmtCert),
}
cfg.Contexts["kubelet"] = &clientcmdapi.Context{
AuthInfo: "kubelet",
Cluster: cfg.Contexts[cfg.CurrentContext].Cluster,
}
cfg.CurrentContext = "kubelet"
return nil
}

View File

@ -67,7 +67,7 @@ func TestPerformTLSBootstrap(t *testing.T) {
t.Fatalf("encountered an error while trying to get New Cert Client: %v", err)
}
cd.CertClient = tmpConfig
_, actual := PerformTLSBootstrap(cd)
_, actual := PerformTLSBootstrapDeprecated(cd)
if (actual == nil) != rt.expect {
t.Errorf(
"failed createClients:\n\texpected: %t\n\t actual: %t",

View File

@ -32,9 +32,8 @@ import (
// the amount of time to wait between each request to the discovery API
const discoveryRetryTimeout = 5 * time.Second
func RetrieveTrustedClusterInfo(s *kubeadmapi.NodeConfiguration) (*kubeadmapi.ClusterInfo, error) {
host, port := s.MasterAddresses[0], s.DiscoveryPort
requestURL := fmt.Sprintf("http://%s:%d/cluster-info/v1/?token-id=%s", host, port, s.Secrets.TokenID)
func RetrieveTrustedClusterInfo(d *kubeadmapi.TokenDiscovery) (*kubeadmapi.ClusterInfo, error) {
requestURL := fmt.Sprintf("http://%s/cluster-info/v1/?token-id=%s", d.Addresses[0], d.ID)
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to consturct an HTTP request [%v]", err)
@ -63,7 +62,7 @@ func RetrieveTrustedClusterInfo(s *kubeadmapi.NodeConfiguration) (*kubeadmapi.Cl
fmt.Println("[discovery] Cluster info object received, verifying signature using given token")
output, err := object.Verify(s.Secrets.Token)
output, err := object.Verify([]byte(d.Secret))
if err != nil {
return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object [%v]", err)
}

View File

@ -67,26 +67,26 @@ func TestRetrieveTrustedClusterInfo(t *testing.T) {
}
tests := []struct {
h string
p int32
p int
payload string
expect bool
}{
{
h: host,
p: int32(iPort),
p: iPort,
payload: "",
expect: false,
},
{
h: host,
p: int32(iPort),
p: iPort,
payload: "foo",
expect: false,
},
}
for _, rt := range tests {
j.Payload = rt.payload
nc := &kubeadmapi.NodeConfiguration{MasterAddresses: []string{host}, DiscoveryPort: int32(iPort)}
nc := &kubeadmapi.TokenDiscovery{Addresses: []string{rt.h + ":" + strconv.Itoa(rt.p)}}
_, actual := RetrieveTrustedClusterInfo(nc)
if (actual == nil) != rt.expect {
t.Errorf(

View File

@ -280,14 +280,13 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
HostnameCheck{},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
ServiceCheck{Service: "docker", CheckIfActive: true},
FirewalldCheck{ports: []int{int(cfg.API.BindPort), int(cfg.Discovery.BindPort), 10250}},
PortOpenCheck{port: int(cfg.API.BindPort)},
FirewalldCheck{ports: []int{int(cfg.API.Port), 10250}},
PortOpenCheck{port: int(cfg.API.Port)},
PortOpenCheck{port: 8080},
PortOpenCheck{port: int(cfg.Discovery.BindPort)},
PortOpenCheck{port: 10250},
PortOpenCheck{port: 10251},
PortOpenCheck{port: 10252},
HTTPProxyCheck{Proto: "https", Host: cfg.API.AdvertiseAddresses[0], Port: int(cfg.API.BindPort)},
HTTPProxyCheck{Proto: "https", Host: cfg.API.AdvertiseAddresses[0], Port: int(cfg.API.Port)},
DirAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests")},
DirAvailableCheck{Path: kubeadmapi.GlobalEnvParams.HostPKIPath},
DirAvailableCheck{Path: "/var/lib/kubelet"},
@ -323,8 +322,6 @@ func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
ServiceCheck{Service: "kubelet", CheckIfActive: false},
ServiceCheck{Service: "docker", CheckIfActive: true},
PortOpenCheck{port: 10250},
HTTPProxyCheck{Proto: "https", Host: cfg.MasterAddresses[0], Port: int(cfg.APIPort)},
HTTPProxyCheck{Proto: "http", Host: cfg.MasterAddresses[0], Port: int(cfg.DiscoveryPort)},
DirAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests")},
DirAvailableCheck{Path: "/var/lib/kubelet"},
FileAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "kubelet.conf")},

View File

@ -19,6 +19,7 @@ go_library(
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",

View File

@ -20,65 +20,76 @@ import (
"crypto/rand"
"encoding/hex"
"fmt"
"regexp"
"strconv"
"strings"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)
const (
TokenIDLen = 6
TokenBytes = 8
TokenIDBytes = 3
TokenBytes = 8
)
func RandBytes(length int) ([]byte, string, error) {
func RandBytes(length int) (string, error) {
b := make([]byte, length)
_, err := rand.Read(b)
if err != nil {
return nil, "", err
return "", err
}
// It's only the tokenID that doesn't care about raw byte slice,
// so we just encoded it in place and ignore bytes slice where we
// do not want it
return b, hex.EncodeToString(b), nil
return hex.EncodeToString(b), nil
}
func GenerateToken(s *kubeadmapi.Secrets) error {
_, tokenID, err := RandBytes(TokenIDLen / 2)
func GenerateToken(d *kubeadmapi.TokenDiscovery) error {
tokenID, err := RandBytes(TokenIDBytes)
if err != nil {
return err
}
tokenBytes, token, err := RandBytes(TokenBytes)
token, err := RandBytes(TokenBytes)
if err != nil {
return err
}
s.TokenID = tokenID
s.BearerToken = token
s.Token = tokenBytes
s.GivenToken = fmt.Sprintf("%s.%s", tokenID, token)
d.ID = tokenID
d.Secret = token
return nil
}
func UseGivenTokenIfValid(s *kubeadmapi.Secrets) (bool, error) {
if s.GivenToken == "" {
return false, nil // not given
var (
tokenRegexpString = "^([a-zA-Z0-9]{6})\\.([a-zA-Z0-9]{16})$"
tokenRegexp = regexp.MustCompile(tokenRegexpString)
)
func ParseToken(s string) (string, string, error) {
split := tokenRegexp.FindStringSubmatch(s)
if len(split) != 3 {
return "", "", fmt.Errorf("token %q was not of form %q", s, tokenRegexpString)
}
fmt.Println("[tokens] Validating provided token")
givenToken := strings.Split(strings.ToLower(s.GivenToken), ".")
// TODO(phase1+) could also print more specific messages in each case
invalidErr := "[tokens] Provided token does not match expected <6 characters>.<16 characters> format - %s"
if len(givenToken) != 2 {
return false, fmt.Errorf(invalidErr, "not in 2-part dot-separated format")
}
if len(givenToken[0]) != TokenIDLen {
return false, fmt.Errorf(invalidErr, fmt.Sprintf(
"length of first part is incorrect [%d (given) != %d (expected) ]",
len(givenToken[0]), TokenIDLen))
}
tokenBytes := []byte(givenToken[1])
s.TokenID = givenToken[0]
s.BearerToken = givenToken[1]
s.Token = tokenBytes
return true, nil // given and valid
return split[1], split[2], nil
}
func BearerToken(d *kubeadmapi.TokenDiscovery) string {
return fmt.Sprintf("%s.%s", d.ID, d.Secret)
}
func IsTokenValid(d *kubeadmapi.TokenDiscovery) (bool, error) {
return true, nil
}
func DiscoveryPort(d *kubeadmapi.TokenDiscovery) int32 {
if len(d.Addresses) == 0 {
return kubeadmapiext.DefaultDiscoveryBindPort
}
split := strings.Split(d.Addresses[0], ":")
if len(split) == 1 {
return kubeadmapiext.DefaultDiscoveryBindPort
}
if i, err := strconv.Atoi(split[1]); err != nil {
return int32(i)
}
return kubeadmapiext.DefaultDiscoveryBindPort
}

View File

@ -17,185 +17,54 @@ limitations under the License.
package util
import (
"bytes"
"strings"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestUsingEmptyTokenFails(t *testing.T) {
// Simulates what happens when you omit --token on the CLI
s := newSecretsWithToken("")
given, err := UseGivenTokenIfValid(s)
if err != nil {
t.Errorf("UseGivenTokenIfValid returned an error when the token was omitted: %v", err)
}
if given {
t.Error("UseGivenTokenIfValid returned given = true when the token was omitted; expected false")
}
}
func TestTokenValidationFailures(t *testing.T) {
var tests = []struct {
t string
expected bool
}{
{
t: "1234567890123456789012",
expected: false,
},
{
t: "12345.1234567890123456",
expected: false,
},
{
t: ".1234567890123456",
expected: false,
},
{
t: "123456.1234567890.123456",
expected: false,
},
func TestTokenParseErrors(t *testing.T) {
invalidTokens := []string{
"1234567890123456789012",
"12345.1234567890123456",
".1234567890123456",
"123456.1234567890.123456",
}
for _, rt := range tests {
s := newSecretsWithToken(rt.t)
_, err := UseGivenTokenIfValid(s)
if (err == nil) != rt.expected {
t.Errorf(
"failed UseGivenTokenIfValid and did not return an error for this invalid token: [%s]",
rt.t,
)
for _, token := range invalidTokens {
if _, _, err := ParseToken(token); err == nil {
t.Errorf("generateTokenIfNeeded did not return an error for this invalid token: [%s]", token)
}
}
}
func TestValidTokenPopulatesSecrets(t *testing.T) {
var tests = []struct {
token string
expectedToken []byte
expectedTokenID string
expectedBearerToken string
}{
{
token: "123456.0123456789AbCdEf",
expectedToken: []byte("0123456789abcdef"),
expectedTokenID: "123456",
expectedBearerToken: "0123456789abcdef",
},
}
for _, rt := range tests {
s := newSecretsWithToken(rt.token)
given, err := UseGivenTokenIfValid(s)
if err != nil {
t.Errorf("UseGivenTokenIfValid gave an error for a valid token: %v", err)
}
if !given {
t.Error("UseGivenTokenIfValid returned given = false when given a valid token")
}
if s.TokenID != rt.expectedTokenID {
t.Errorf("UseGivenTokenIfValid did not populate the TokenID correctly; expected [%s] but got [%s]", rt.expectedTokenID, s.TokenID)
}
if s.BearerToken != rt.expectedBearerToken {
t.Errorf("UseGivenTokenIfValid did not populate the BearerToken correctly; expected [%s] but got [%s]", rt.expectedBearerToken, s.BearerToken)
}
if !bytes.Equal(s.Token, rt.expectedToken) {
t.Errorf("UseGivenTokenIfValid did not populate the Token correctly; expected %v but got %v", rt.expectedToken, s.Token)
}
}
}
func newSecretsWithToken(token string) *kubeadmapi.Secrets {
s := new(kubeadmapi.Secrets)
s.GivenToken = token
return s
}
func TestGenerateToken(t *testing.T) {
var genTest = []struct {
s kubeadmapi.Secrets
l int
n int
}{
{kubeadmapi.Secrets{}, 2, 6},
}
var cfg kubeadmapi.TokenDiscovery
for _, rt := range genTest {
GenerateToken(&rt.s)
givenToken := strings.Split(strings.ToLower(rt.s.GivenToken), ".")
if len(givenToken) != rt.l {
t.Errorf(
"failed GenerateToken num parts:\n\texpected: %d\n\t actual: %d",
rt.l,
len(givenToken),
)
}
if len(givenToken[0]) != rt.n {
t.Errorf(
"failed GenerateToken first part length:\n\texpected: %d\n\t actual: %d",
rt.l,
len(givenToken),
)
}
GenerateToken(&cfg)
if len(cfg.ID) != 6 {
t.Errorf("failed GenerateToken first part length:\n\texpected: 6\n\t actual: %d", len(cfg.ID))
}
}
func TestUseGivenTokenIfValid(t *testing.T) {
var tokenTest = []struct {
s kubeadmapi.Secrets
expected bool
}{
{kubeadmapi.Secrets{GivenToken: ""}, false}, // GivenToken == ""
{kubeadmapi.Secrets{GivenToken: "noperiod"}, false}, // not 2-part '.' format
{kubeadmapi.Secrets{GivenToken: "abcd.a"}, false}, // len(tokenID) != 6
{kubeadmapi.Secrets{GivenToken: "abcdef.a"}, true},
}
for _, rt := range tokenTest {
actual, _ := UseGivenTokenIfValid(&rt.s)
if actual != rt.expected {
t.Errorf(
"failed UseGivenTokenIfValid:\n\texpected: %t\n\t actual: %t\n\t token:%s",
rt.expected,
actual,
rt.s.GivenToken,
)
}
if len(cfg.Secret) != 16 {
t.Errorf("failed GenerateToken first part length:\n\texpected: 16\n\t actual: %d", len(cfg.Secret))
}
}
func TestRandBytes(t *testing.T) {
var randTest = []struct {
r int
l int
expected error
}{
{0, 0, nil},
{1, 1, nil},
{2, 2, nil},
{3, 3, nil},
{100, 100, nil},
var randTest = []int{
0,
1,
2,
3,
100,
}
for _, rt := range randTest {
actual, _, err := RandBytes(rt.r)
if err != rt.expected {
t.Errorf(
"failed RandBytes:\n\texpected: %s\n\t actual: %s",
rt.expected,
err,
)
actual, err := RandBytes(rt)
if err != nil {
t.Errorf("failed RandBytes: %v", err)
}
if len(actual) != rt.l {
t.Errorf(
"failed RandBytes:\n\texpected: %d\n\t actual: %d\n",
rt.l,
len(actual),
)
if len(actual) != rt*2 {
t.Errorf("failed RandBytes:\n\texpected: %d\n\t actual: %d\n", rt*2, len(actual))
}
}
}

View File

@ -18,6 +18,8 @@ package kubeadm
import (
"flag"
"os"
"path/filepath"
"regexp"
"testing"
)
@ -29,7 +31,7 @@ const (
var kubeadmPath string
func init() {
flag.StringVar(&kubeadmPath, "kubeadm-path", "cluster/kubeadm.sh", "Location of kubeadm")
flag.StringVar(&kubeadmPath, "kubeadm-path", filepath.Join(os.Getenv("KUBE_ROOT"), "cluster/kubeadm.sh"), "Location of kubeadm")
}
func TestCmdTokenGenerate(t *testing.T) {

View File

@ -544,16 +544,10 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz
func(obj *kubeadm.MasterConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(obj)
obj.KubernetesVersion = "v10"
obj.API.BindPort = 20
obj.Discovery.BindPort = 20
obj.API.Port = 20
obj.Networking.ServiceSubnet = "foo"
obj.Networking.DNSDomain = "foo"
},
func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(obj)
obj.APIPort = 20
obj.DiscoveryPort = 20
},
func(s *policy.PodDisruptionBudgetStatus, c fuzz.Continue) {
c.FuzzNoCustom(s) // fuzz self without calling this function again
s.PodDisruptionsAllowed = int32(c.Rand.Intn(2))