mirror of https://github.com/k3s-io/k3s
kubeadm: refactor discovery behind an interface
parent
82edbf1ddc
commit
690c7e578b
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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{},
|
||||
},
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
|
||||
|
|
|
@ -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",
|
||||
)
|
||||
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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")},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue