mirror of https://github.com/k3s-io/k3s
Merge pull request #36462 from mikedanese/discovery
Automatic merge from submit-queue kubeadm: refactor discovery behind an interface This adds support for alternative discovery methods using discovery urls. It is a breaking change. This is a WIP. Example usage: ``` $ kubeadm init --discovery token:// $ kubeadm join --discovery token://c05de9:ab224260fb3cd718@192.168.0.1:6555,191.168.0.2:6443 $ kubeadm join --discovery file:///etc/kubernetes/cluster.json $ kubeadm join --discovery https://storage.google.apis.com/kube-discovery/98ea6e4/kubeconfig.json ``` @kubernetes/sig-cluster-lifecyclepull/6/head
commit
8d71970dcd
|
@ -33,7 +33,6 @@ type EnvParams struct {
|
||||||
type MasterConfiguration struct {
|
type MasterConfiguration struct {
|
||||||
metav1.TypeMeta
|
metav1.TypeMeta
|
||||||
|
|
||||||
Secrets Secrets
|
|
||||||
API API
|
API API
|
||||||
Discovery Discovery
|
Discovery Discovery
|
||||||
Etcd Etcd
|
Etcd Etcd
|
||||||
|
@ -45,11 +44,27 @@ type MasterConfiguration struct {
|
||||||
type API struct {
|
type API struct {
|
||||||
AdvertiseAddresses []string
|
AdvertiseAddresses []string
|
||||||
ExternalDNSNames []string
|
ExternalDNSNames []string
|
||||||
BindPort int32
|
Port int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type Discovery struct {
|
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 {
|
type Networking struct {
|
||||||
|
@ -65,20 +80,10 @@ type Etcd struct {
|
||||||
KeyFile string
|
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 {
|
type NodeConfiguration struct {
|
||||||
metav1.TypeMeta
|
metav1.TypeMeta
|
||||||
|
|
||||||
MasterAddresses []string
|
Discovery Discovery
|
||||||
Secrets Secrets
|
|
||||||
APIPort int32
|
|
||||||
DiscoveryPort int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClusterInfo TODO add description
|
// ClusterInfo TODO add description
|
||||||
|
|
|
@ -33,7 +33,6 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||||
RegisterDefaults(scheme)
|
RegisterDefaults(scheme)
|
||||||
return scheme.AddDefaultingFuncs(
|
return scheme.AddDefaultingFuncs(
|
||||||
SetDefaults_MasterConfiguration,
|
SetDefaults_MasterConfiguration,
|
||||||
SetDefaults_NodeConfiguration,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,12 +41,8 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
|
||||||
obj.KubernetesVersion = DefaultKubernetesVersion
|
obj.KubernetesVersion = DefaultKubernetesVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj.API.BindPort == 0 {
|
if obj.API.Port == 0 {
|
||||||
obj.API.BindPort = DefaultAPIBindPort
|
obj.API.Port = DefaultAPIBindPort
|
||||||
}
|
|
||||||
|
|
||||||
if obj.Discovery.BindPort == 0 {
|
|
||||||
obj.Discovery.BindPort = DefaultDiscoveryBindPort
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj.Networking.ServiceSubnet == "" {
|
if obj.Networking.ServiceSubnet == "" {
|
||||||
|
@ -58,13 +53,3 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
|
||||||
obj.Networking.DNSDomain = DefaultServiceDNSDomain
|
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 {
|
type MasterConfiguration struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
|
||||||
Secrets Secrets `json:"secrets"`
|
|
||||||
API API `json:"api"`
|
API API `json:"api"`
|
||||||
Etcd Etcd `json:"etcd"`
|
|
||||||
Discovery Discovery `json:"discovery"`
|
Discovery Discovery `json:"discovery"`
|
||||||
|
Etcd Etcd `json:"etcd"`
|
||||||
Networking Networking `json:"networking"`
|
Networking Networking `json:"networking"`
|
||||||
KubernetesVersion string `json:"kubernetesVersion"`
|
KubernetesVersion string `json:"kubernetesVersion"`
|
||||||
CloudProvider string `json:"cloudProvider"`
|
CloudProvider string `json:"cloudProvider"`
|
||||||
|
@ -35,11 +34,27 @@ type MasterConfiguration struct {
|
||||||
type API struct {
|
type API struct {
|
||||||
AdvertiseAddresses []string `json:"advertiseAddresses"`
|
AdvertiseAddresses []string `json:"advertiseAddresses"`
|
||||||
ExternalDNSNames []string `json:"externalDNSNames"`
|
ExternalDNSNames []string `json:"externalDNSNames"`
|
||||||
BindPort int32 `json:"bindPort"`
|
Port int32 `json:"port"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Discovery struct {
|
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 {
|
type Networking struct {
|
||||||
|
@ -65,10 +80,7 @@ type Secrets struct {
|
||||||
type NodeConfiguration struct {
|
type NodeConfiguration struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
|
||||||
MasterAddresses []string `json:"masterAddresses"`
|
Discovery Discovery `json:"discovery"`
|
||||||
Secrets Secrets `json:"secrets"`
|
|
||||||
APIPort int32 `json:"apiPort"`
|
|
||||||
DiscoveryPort int32 `json:"discoveryPort"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClusterInfo TODO add description
|
// ClusterInfo TODO add description
|
||||||
|
|
|
@ -29,14 +29,9 @@ import (
|
||||||
// All generated defaulters are covering - they call all nested defaulters.
|
// All generated defaulters are covering - they call all nested defaulters.
|
||||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||||
scheme.AddTypeDefaultingFunc(&MasterConfiguration{}, func(obj interface{}) { SetObjectDefaults_MasterConfiguration(obj.(*MasterConfiguration)) })
|
scheme.AddTypeDefaultingFunc(&MasterConfiguration{}, func(obj interface{}) { SetObjectDefaults_MasterConfiguration(obj.(*MasterConfiguration)) })
|
||||||
scheme.AddTypeDefaultingFunc(&NodeConfiguration{}, func(obj interface{}) { SetObjectDefaults_NodeConfiguration(obj.(*NodeConfiguration)) })
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) {
|
func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) {
|
||||||
SetDefaults_MasterConfiguration(in)
|
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 = [
|
deps = [
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1: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/cmd/flags:go_default_library",
|
||||||
|
"//cmd/kubeadm/app/discovery:go_default_library",
|
||||||
"//cmd/kubeadm/app/master:go_default_library",
|
"//cmd/kubeadm/app/master:go_default_library",
|
||||||
"//cmd/kubeadm/app/node:go_default_library",
|
"//cmd/kubeadm/app/node:go_default_library",
|
||||||
"//cmd/kubeadm/app/preflight:go_default_library",
|
"//cmd/kubeadm/app/preflight:go_default_library",
|
||||||
"//cmd/kubeadm/app/util:go_default_library",
|
"//cmd/kubeadm/app/util:go_default_library",
|
||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
|
"//pkg/client/unversioned/clientcmd/api:go_default_library",
|
||||||
"//pkg/kubectl/cmd/util:go_default_library",
|
"//pkg/kubectl/cmd/util:go_default_library",
|
||||||
"//pkg/runtime:go_default_library",
|
"//pkg/runtime:go_default_library",
|
||||||
"//pkg/util/flag:go_default_library",
|
"//pkg/util/flag:go_default_library",
|
||||||
|
|
|
@ -17,9 +17,7 @@ limitations under the License.
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
|
@ -28,7 +26,9 @@ import (
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
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/cmd/flags"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
|
||||||
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
|
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
|
@ -37,18 +37,6 @@ import (
|
||||||
netutil "k8s.io/kubernetes/pkg/util/net"
|
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 (
|
var (
|
||||||
initDoneMsgf = dedent.Dedent(`
|
initDoneMsgf = dedent.Dedent(`
|
||||||
Your Kubernetes master has initialized successfully!
|
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:
|
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) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
i, err := NewInit(cfgPath, &cfg, skipPreFlight)
|
i, err := NewInit(cfgPath, &cfg, skipPreFlight)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
kubeadmutil.CheckErr(i.Validate())
|
||||||
kubeadmutil.CheckErr(i.Run(out))
|
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(
|
cmd.PersistentFlags().StringSliceVar(
|
||||||
&cfg.API.AdvertiseAddresses, "api-advertise-addresses", cfg.API.AdvertiseAddresses,
|
&cfg.API.AdvertiseAddresses, "api-advertise-addresses", cfg.API.AdvertiseAddresses,
|
||||||
"The IP addresses to advertise, in case autodetection fails",
|
"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",
|
"skip preflight checks normally run before modifying the system",
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd.PersistentFlags().Int32Var(
|
cmd.PersistentFlags().Var(
|
||||||
&cfg.API.BindPort, "api-port", cfg.API.BindPort,
|
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
|
||||||
"Port for API to bind to",
|
"The discovery method kubeadm will use for connecting nodes to the master",
|
||||||
)
|
|
||||||
|
|
||||||
cmd.PersistentFlags().Int32Var(
|
|
||||||
&cfg.Discovery.BindPort, "discovery-port", cfg.Discovery.BindPort,
|
|
||||||
"Port for JWS discovery service to bind to",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -228,18 +208,21 @@ func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight
|
||||||
return &Init{cfg: cfg}, nil
|
return &Init{cfg: cfg}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// joinArgsData denotes a data object which is needed by function generateJoinArgs to generate kubeadm join arguments.
|
func (i *Init) Validate() error {
|
||||||
type joinArgsData struct {
|
return validation.ValidateMasterConfiguration(i.cfg).ToAggregate()
|
||||||
Cfg *kubeadmapi.MasterConfiguration
|
|
||||||
DefaultAPIBindPort int32
|
|
||||||
DefaultDiscoveryBindPort int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run executes master node provisioning, including certificates, needed static pod manifests, etc.
|
// Run executes master node provisioning, including certificates, needed static pod manifests, etc.
|
||||||
func (i *Init) Run(out io.Writer) error {
|
func (i *Init) Run(out io.Writer) error {
|
||||||
if err := kubemaster.CreateTokenAuthFile(&i.cfg.Secrets); err != nil {
|
|
||||||
|
if i.cfg.Discovery.Token != nil {
|
||||||
|
if err := kubemaster.PrepareTokenDiscovery(i.cfg.Discovery.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := kubemaster.CreateTokenAuthFile(kubeadmutil.BearerToken(i.cfg.Discovery.Token)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := kubemaster.WriteStaticPodManifests(i.cfg); err != nil {
|
if err := kubemaster.WriteStaticPodManifests(i.cfg); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -275,34 +258,25 @@ func (i *Init) Run(out io.Writer) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
schedulePodsOnMaster := false
|
if err := kubemaster.UpdateMasterRoleLabelsAndTaints(client, false); err != nil {
|
||||||
if err := kubemaster.UpdateMasterRoleLabelsAndTaints(client, schedulePodsOnMaster); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i.cfg.Discovery.Token != nil {
|
||||||
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client, caCert); err != nil {
|
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client, caCert); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := kubemaster.CreateEssentialAddons(i.cfg, client); err != nil {
|
if err := kubemaster.CreateEssentialAddons(i.cfg, client); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := joinArgsData{i.cfg, kubeadmapiext.DefaultAPIBindPort, kubeadmapiext.DefaultDiscoveryBindPort}
|
fmt.Fprintf(out, initDoneMsgf, generateJoinArgs(i.cfg))
|
||||||
if joinArgs, err := generateJoinArgs(data); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(out, initDoneMsgf, joinArgs)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateJoinArgs generates kubeadm join arguments
|
// generateJoinArgs generates kubeadm join arguments
|
||||||
func generateJoinArgs(data joinArgsData) (string, error) {
|
func generateJoinArgs(cfg *kubeadmapi.MasterConfiguration) string {
|
||||||
joinArgsTemplate := template.Must(template.New("joinArgsTemplate").Parse(joinArgsTemplateLiteral))
|
return discovery.NewDiscoveryValue(&cfg.Discovery).String()
|
||||||
var b bytes.Buffer
|
|
||||||
if err := joinArgsTemplate.Execute(&b, data); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return b.String(), nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,13 @@ import (
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
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"
|
kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,15 +63,11 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
j, err := NewJoin(cfgPath, args, &cfg, skipPreFlight)
|
j, err := NewJoin(cfgPath, args, &cfg, skipPreFlight)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
kubeadmutil.CheckErr(j.Validate())
|
||||||
kubeadmutil.CheckErr(j.Run(out))
|
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().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
|
||||||
|
|
||||||
cmd.PersistentFlags().BoolVar(
|
cmd.PersistentFlags().BoolVar(
|
||||||
|
@ -76,14 +75,9 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
|
||||||
"skip preflight checks normally run before modifying the system",
|
"skip preflight checks normally run before modifying the system",
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd.PersistentFlags().Int32Var(
|
cmd.PersistentFlags().Var(
|
||||||
&cfg.APIPort, "api-port", cfg.APIPort,
|
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
|
||||||
"(optional) API server port on the master",
|
"The discovery method kubeadm will use for connecting nodes to the master",
|
||||||
)
|
|
||||||
|
|
||||||
cmd.PersistentFlags().Int32Var(
|
|
||||||
&cfg.DiscoveryPort, "discovery-port", cfg.DiscoveryPort,
|
|
||||||
"(optional) Discovery port on the master",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd
|
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 {
|
if !skipPreFlight {
|
||||||
fmt.Println("[preflight] Running pre-flight checks")
|
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
|
// Try to start the kubelet service in case it's inactive
|
||||||
preflight.TryStartKubelet()
|
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
|
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.
|
// Run executes worked node provisioning and tries to join an existing cluster.
|
||||||
func (j *Join) Run(out io.Writer) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
connectionDetails, err := kubenode.EstablishMasterConnection(j.cfg, clusterInfo)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = kubenode.CheckForNodeNameDuplicates(connectionDetails)
|
err = kubenode.CheckForNodeNameDuplicates(connectionDetails)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
cfg, err = kubenode.PerformTLSBootstrapDeprecated(connectionDetails)
|
||||||
kubeconfig, err := kubenode.PerformTLSBootstrap(connectionDetails)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 = kubeadmutil.WriteKubeconfigIfNotExists("kubelet", kubeconfig)
|
err = kubeadmutil.WriteKubeconfigIfNotExists("kubelet", cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,12 +75,12 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunGenerateToken(out io.Writer) error {
|
func RunGenerateToken(out io.Writer) error {
|
||||||
s := &kubeadmapi.Secrets{}
|
d := &kubeadmapi.TokenDiscovery{}
|
||||||
err := util.GenerateToken(s)
|
err := util.GenerateToken(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(out, s.GivenToken)
|
fmt.Fprintln(out, util.BearerToken(d))
|
||||||
return nil
|
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
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ go_library(
|
||||||
"//pkg/registry/core/service/ipallocator:go_default_library",
|
"//pkg/registry/core/service/ipallocator:go_default_library",
|
||||||
"//pkg/util/cert:go_default_library",
|
"//pkg/util/cert:go_default_library",
|
||||||
"//pkg/util/intstr:go_default_library",
|
"//pkg/util/intstr:go_default_library",
|
||||||
|
"//pkg/util/net:go_default_library",
|
||||||
"//pkg/util/uuid:go_default_library",
|
"//pkg/util/uuid:go_default_library",
|
||||||
"//pkg/util/wait:go_default_library",
|
"//pkg/util/wait:go_default_library",
|
||||||
"//vendor:github.com/blang/semver",
|
"//vendor:github.com/blang/semver",
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
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"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||||
|
@ -43,18 +44,18 @@ const (
|
||||||
kubeDiscoverySecretName = "clusterinfo"
|
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 (
|
var (
|
||||||
data = map[string][]byte{}
|
data = map[string][]byte{}
|
||||||
endpointList = []string{}
|
endpointList = []string{}
|
||||||
tokenMap = map[string]string{}
|
tokenMap = map[string]string{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, addr := range cfg.API.AdvertiseAddresses {
|
for _, addr := range apicfg.AdvertiseAddresses {
|
||||||
endpointList = append(endpointList, fmt.Sprintf("https://%s:%d", addr, cfg.API.BindPort))
|
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["endpoint-list.json"], _ = json.Marshal(endpointList)
|
||||||
data["token-map.json"], _ = json.Marshal(tokenMap)
|
data["token-map.json"], _ = json.Marshal(tokenMap)
|
||||||
|
@ -83,7 +84,7 @@ func newKubeDiscoveryPodSpec(cfg *kubeadmapi.MasterConfiguration) v1.PodSpec {
|
||||||
Ports: []v1.ContainerPort{
|
Ports: []v1.ContainerPort{
|
||||||
// TODO when CNI issue (#31307) is resolved, we should consider adding
|
// TODO when CNI issue (#31307) is resolved, we should consider adding
|
||||||
// `HostIP: s.API.AdvertiseAddrs[0]`, if there is only one address`
|
// `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{
|
SecurityContext: &v1.SecurityContext{
|
||||||
SELinuxOptions: &v1.SELinuxOptions{
|
SELinuxOptions: &v1.SELinuxOptions{
|
||||||
|
@ -110,7 +111,7 @@ func newKubeDiscovery(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certific
|
||||||
Secret: &v1.Secret{
|
Secret: &v1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{Name: kubeDiscoverySecretName},
|
ObjectMeta: v1.ObjectMeta{Name: kubeDiscoverySecretName},
|
||||||
Type: v1.SecretTypeOpaque,
|
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"
|
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) {
|
func TestNewKubeDiscovery(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
cfg *kubeadmapi.MasterConfiguration
|
cfg *kubeadmapi.MasterConfiguration
|
||||||
|
@ -87,8 +31,9 @@ func TestNewKubeDiscovery(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
cfg: &kubeadmapi.MasterConfiguration{
|
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"},
|
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
|
||||||
|
Discovery: kubeadmapi.Discovery{Token: &kubeadmapi.TokenDiscovery{}},
|
||||||
},
|
},
|
||||||
caCert: &x509.Certificate{},
|
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
|
// 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
|
// so we'll pick the first one, there is much of chance to have an empty
|
||||||
// slice by the time this gets called
|
// 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),
|
certutil.EncodeCertPEM(caCert),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,7 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration) []string {
|
||||||
"--tls-cert-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver.pem",
|
"--tls-cert-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver.pem",
|
||||||
"--tls-private-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem",
|
"--tls-private-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem",
|
||||||
"--token-auth-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/tokens.csv",
|
"--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",
|
"--allow-privileged",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -364,7 +364,7 @@ func TestGetAPIServerCommand(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
cfg: &kubeadmapi.MasterConfiguration{
|
cfg: &kubeadmapi.MasterConfiguration{
|
||||||
API: kubeadm.API{BindPort: 123},
|
API: kubeadm.API{Port: 123},
|
||||||
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
|
@ -384,7 +384,7 @@ func TestGetAPIServerCommand(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cfg: &kubeadmapi.MasterConfiguration{
|
cfg: &kubeadmapi.MasterConfiguration{
|
||||||
API: kubeadm.API{BindPort: 123, AdvertiseAddresses: []string{"foo"}},
|
API: kubeadm.API{Port: 123, AdvertiseAddresses: []string{"foo"}},
|
||||||
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
|
@ -405,7 +405,7 @@ func TestGetAPIServerCommand(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cfg: &kubeadmapi.MasterConfiguration{
|
cfg: &kubeadmapi.MasterConfiguration{
|
||||||
API: kubeadm.API{BindPort: 123},
|
API: kubeadm.API{Port: 123},
|
||||||
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
||||||
Etcd: kubeadm.Etcd{CertFile: "fiz", KeyFile: "faz"},
|
Etcd: kubeadm.Etcd{CertFile: "fiz", KeyFile: "faz"},
|
||||||
},
|
},
|
||||||
|
@ -429,7 +429,7 @@ func TestGetAPIServerCommand(t *testing.T) {
|
||||||
// Make sure --kubelet-preferred-address-types
|
// Make sure --kubelet-preferred-address-types
|
||||||
{
|
{
|
||||||
cfg: &kubeadmapi.MasterConfiguration{
|
cfg: &kubeadmapi.MasterConfiguration{
|
||||||
API: kubeadm.API{BindPort: 123, AdvertiseAddresses: []string{"foo"}},
|
API: kubeadm.API{Port: 123, AdvertiseAddresses: []string{"foo"}},
|
||||||
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
|
||||||
KubernetesVersion: "v1.5.3",
|
KubernetesVersion: "v1.5.3",
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,41 +21,52 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
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"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
|
netutil "k8s.io/kubernetes/pkg/util/net"
|
||||||
"k8s.io/kubernetes/pkg/util/uuid"
|
"k8s.io/kubernetes/pkg/util/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateTokenIfNeeded(s *kubeadmapi.Secrets) error {
|
func generateTokenIfNeeded(d *kubeadmapi.TokenDiscovery) error {
|
||||||
ok, err := kubeadmutil.UseGivenTokenIfValid(s)
|
ok, err := kubeadmutil.IsTokenValid(d)
|
||||||
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = kubeadmutil.GenerateToken(s)
|
if ok {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("[tokens] Generated token: %q\n", s.GivenToken)
|
|
||||||
} else {
|
|
||||||
fmt.Println("[tokens] Accepted provided token")
|
fmt.Println("[tokens] Accepted provided token")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
if err := kubeadmutil.GenerateToken(d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("[tokens] Generated token: %q\n", kubeadmutil.BearerToken(d))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateTokenAuthFile(s *kubeadmapi.Secrets) error {
|
func PrepareTokenDiscovery(d *kubeadmapi.TokenDiscovery) error {
|
||||||
tokenAuthFilePath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath, "tokens.csv")
|
if len(d.Addresses) == 0 {
|
||||||
if err := generateTokenIfNeeded(s); err != nil {
|
ip, err := netutil.ChooseHostInterface()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.Addresses = []string{ip.String() + ":" + strconv.Itoa(kubeadmapiext.DefaultDiscoveryBindPort)}
|
||||||
|
}
|
||||||
|
if err := generateTokenIfNeeded(d); err != nil {
|
||||||
return fmt.Errorf("failed to generate token(s) [%v]", err)
|
return fmt.Errorf("failed to generate token(s) [%v]", err)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateTokenAuthFile(bt string) error {
|
||||||
|
tokenAuthFilePath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath, "tokens.csv")
|
||||||
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.HostPKIPath, 0700); err != nil {
|
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.HostPKIPath, 0700); err != nil {
|
||||||
return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.HostPKIPath, err)
|
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", bt, uuid.NewUUID()))
|
||||||
// DumpReaderToFile create a file with mode 0600
|
// DumpReaderToFile create a file with mode 0600
|
||||||
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), tokenAuthFilePath); err != nil {
|
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), tokenAuthFilePath); err != nil {
|
||||||
return fmt.Errorf("failed to save token auth file (%q) [%v]", tokenAuthFilePath, err)
|
return fmt.Errorf("failed to save token auth file (%q) [%v]", tokenAuthFilePath, err)
|
||||||
|
|
|
@ -17,67 +17,44 @@ limitations under the License.
|
||||||
package master
|
package master
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateTokenIfNeeded(t *testing.T) {
|
func TestValidTokenPopulatesSecrets(t *testing.T) {
|
||||||
var tests = []struct {
|
t.Run("provided", func(t *testing.T) {
|
||||||
s kubeadmapi.Secrets
|
expectedID := "123456"
|
||||||
expected bool
|
expectedSecret := "0123456789abcdef"
|
||||||
}{
|
s := &kubeadmapi.TokenDiscovery{
|
||||||
{kubeadmapi.Secrets{GivenToken: "noperiod"}, false}, // not 2-part '.' format
|
ID: expectedID,
|
||||||
{kubeadmapi.Secrets{GivenToken: "abcd.a"}, false}, // len(tokenID) != 6
|
Secret: expectedSecret,
|
||||||
{kubeadmapi.Secrets{GivenToken: "abcdef.a"}, true},
|
|
||||||
{kubeadmapi.Secrets{GivenToken: ""}, true},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rt := range tests {
|
err := generateTokenIfNeeded(s)
|
||||||
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 TestCreateTokenAuthFile(t *testing.T) {
|
|
||||||
tmpdir, err := ioutil.TempDir("", "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Couldn't create tmpdir")
|
t.Errorf("generateTokenIfNeeded gave an error for a valid token: %v", err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpdir)
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// set up tmp GlobalEnvParams values for testing
|
t.Run("not provided", func(t *testing.T) {
|
||||||
oldEnv := kubeadmapi.GlobalEnvParams
|
s := &kubeadmapi.TokenDiscovery{}
|
||||||
kubeadmapi.GlobalEnvParams.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)
|
|
||||||
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
|
|
||||||
|
|
||||||
var tests = []struct {
|
err := generateTokenIfNeeded(s)
|
||||||
s kubeadmapi.Secrets
|
if err != nil {
|
||||||
expected bool
|
t.Errorf("generateTokenIfNeeded gave an error for a valid token: %v", err)
|
||||||
}{
|
|
||||||
{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 {
|
if s.ID == "" {
|
||||||
actual := CreateTokenAuthFile(&rt.s)
|
t.Errorf("generateTokenIfNeeded did not populate the TokenID correctly; expected ID to be non-empty")
|
||||||
if (actual == nil) != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed WriteKubeconfigIfNotExists with an error:\n\texpected: %t\n\t actual: %t",
|
|
||||||
rt.expected,
|
|
||||||
(actual == nil),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
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
|
// 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
|
// of the provided endpoints. Blocks until at least one connection is established, then it stops the
|
||||||
// connection attempts for other endpoints.
|
// 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()
|
hostName, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get node hostname [%v]", err)
|
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)
|
result := make(chan *ConnectionDetails)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for _, endpoint := range endpoints {
|
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 {
|
if err != nil {
|
||||||
fmt.Printf("[bootstrap] Warning: %s. Skipping endpoint %s\n", err, endpoint)
|
fmt.Printf("[bootstrap] Warning: %s. Skipping endpoint %s\n", err, endpoint)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -107,7 +107,7 @@ func TestEstablishMasterConnection(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
s := &kubeadmapi.NodeConfiguration{}
|
s := &kubeadmapi.TokenDiscovery{}
|
||||||
c := &kubeadmapi.ClusterInfo{Endpoints: []string{rt.e}, CertificateAuthorities: []string{rt.c}}
|
c := &kubeadmapi.ClusterInfo{Endpoints: []string{rt.e}, CertificateAuthorities: []string{rt.c}}
|
||||||
_, actual := EstablishMasterConnection(s, c)
|
_, actual := EstablishMasterConnection(s, c)
|
||||||
if (actual == nil) != rt.expect {
|
if (actual == nil) != rt.expect {
|
||||||
|
|
|
@ -18,16 +18,18 @@ package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
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"
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util/csr"
|
"k8s.io/kubernetes/pkg/kubelet/util/csr"
|
||||||
|
"k8s.io/kubernetes/pkg/types"
|
||||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PerformTLSBootstrap executes a certificate signing request with the
|
func PerformTLSBootstrapDeprecated(connection *ConnectionDetails) (*clientcmdapi.Config, error) {
|
||||||
// provided connection details.
|
|
||||||
func PerformTLSBootstrap(connection *ConnectionDetails) (*clientcmdapi.Config, error) {
|
|
||||||
csrClient := connection.CertClient.CertificateSigningRequests()
|
csrClient := connection.CertClient.CertificateSigningRequests()
|
||||||
|
|
||||||
fmt.Println("[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request")
|
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
|
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)
|
t.Fatalf("encountered an error while trying to get New Cert Client: %v", err)
|
||||||
}
|
}
|
||||||
cd.CertClient = tmpConfig
|
cd.CertClient = tmpConfig
|
||||||
_, actual := PerformTLSBootstrap(cd)
|
_, actual := PerformTLSBootstrapDeprecated(cd)
|
||||||
if (actual == nil) != rt.expect {
|
if (actual == nil) != rt.expect {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed createClients:\n\texpected: %t\n\t actual: %t",
|
"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
|
// the amount of time to wait between each request to the discovery API
|
||||||
const discoveryRetryTimeout = 5 * time.Second
|
const discoveryRetryTimeout = 5 * time.Second
|
||||||
|
|
||||||
func RetrieveTrustedClusterInfo(s *kubeadmapi.NodeConfiguration) (*kubeadmapi.ClusterInfo, error) {
|
func RetrieveTrustedClusterInfo(d *kubeadmapi.TokenDiscovery) (*kubeadmapi.ClusterInfo, error) {
|
||||||
host, port := s.MasterAddresses[0], s.DiscoveryPort
|
requestURL := fmt.Sprintf("http://%s/cluster-info/v1/?token-id=%s", d.Addresses[0], d.ID)
|
||||||
requestURL := fmt.Sprintf("http://%s:%d/cluster-info/v1/?token-id=%s", host, port, s.Secrets.TokenID)
|
|
||||||
req, err := http.NewRequest("GET", requestURL, nil)
|
req, err := http.NewRequest("GET", requestURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to consturct an HTTP request [%v]", err)
|
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")
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object [%v]", err)
|
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 {
|
tests := []struct {
|
||||||
h string
|
h string
|
||||||
p int32
|
p int
|
||||||
payload string
|
payload string
|
||||||
expect bool
|
expect bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
h: host,
|
h: host,
|
||||||
p: int32(iPort),
|
p: iPort,
|
||||||
payload: "",
|
payload: "",
|
||||||
expect: false,
|
expect: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
h: host,
|
h: host,
|
||||||
p: int32(iPort),
|
p: iPort,
|
||||||
payload: "foo",
|
payload: "foo",
|
||||||
expect: false,
|
expect: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
j.Payload = rt.payload
|
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)
|
_, actual := RetrieveTrustedClusterInfo(nc)
|
||||||
if (actual == nil) != rt.expect {
|
if (actual == nil) != rt.expect {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
|
|
|
@ -280,14 +280,13 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
HostnameCheck{},
|
HostnameCheck{},
|
||||||
ServiceCheck{Service: "kubelet", CheckIfActive: false},
|
ServiceCheck{Service: "kubelet", CheckIfActive: false},
|
||||||
ServiceCheck{Service: "docker", CheckIfActive: true},
|
ServiceCheck{Service: "docker", CheckIfActive: true},
|
||||||
FirewalldCheck{ports: []int{int(cfg.API.BindPort), int(cfg.Discovery.BindPort), 10250}},
|
FirewalldCheck{ports: []int{int(cfg.API.Port), 10250}},
|
||||||
PortOpenCheck{port: int(cfg.API.BindPort)},
|
PortOpenCheck{port: int(cfg.API.Port)},
|
||||||
PortOpenCheck{port: 8080},
|
PortOpenCheck{port: 8080},
|
||||||
PortOpenCheck{port: int(cfg.Discovery.BindPort)},
|
|
||||||
PortOpenCheck{port: 10250},
|
PortOpenCheck{port: 10250},
|
||||||
PortOpenCheck{port: 10251},
|
PortOpenCheck{port: 10251},
|
||||||
PortOpenCheck{port: 10252},
|
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: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests")},
|
||||||
DirAvailableCheck{Path: kubeadmapi.GlobalEnvParams.HostPKIPath},
|
DirAvailableCheck{Path: kubeadmapi.GlobalEnvParams.HostPKIPath},
|
||||||
DirAvailableCheck{Path: "/var/lib/kubelet"},
|
DirAvailableCheck{Path: "/var/lib/kubelet"},
|
||||||
|
@ -323,8 +322,6 @@ func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
|
||||||
ServiceCheck{Service: "kubelet", CheckIfActive: false},
|
ServiceCheck{Service: "kubelet", CheckIfActive: false},
|
||||||
ServiceCheck{Service: "docker", CheckIfActive: true},
|
ServiceCheck{Service: "docker", CheckIfActive: true},
|
||||||
PortOpenCheck{port: 10250},
|
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: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests")},
|
||||||
DirAvailableCheck{Path: "/var/lib/kubelet"},
|
DirAvailableCheck{Path: "/var/lib/kubelet"},
|
||||||
FileAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "kubelet.conf")},
|
FileAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "kubelet.conf")},
|
||||||
|
|
|
@ -19,6 +19,7 @@ go_library(
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||||
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||||
"//cmd/kubeadm/app/preflight:go_default_library",
|
"//cmd/kubeadm/app/preflight:go_default_library",
|
||||||
"//pkg/client/unversioned/clientcmd:go_default_library",
|
"//pkg/client/unversioned/clientcmd:go_default_library",
|
||||||
"//pkg/client/unversioned/clientcmd/api:go_default_library",
|
"//pkg/client/unversioned/clientcmd/api:go_default_library",
|
||||||
|
|
|
@ -20,65 +20,82 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
|
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TokenIDLen = 6
|
TokenIDBytes = 3
|
||||||
TokenBytes = 8
|
TokenBytes = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
func RandBytes(length int) ([]byte, string, error) {
|
func RandBytes(length int) (string, error) {
|
||||||
b := make([]byte, length)
|
b := make([]byte, length)
|
||||||
_, err := rand.Read(b)
|
_, err := rand.Read(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return "", err
|
||||||
}
|
}
|
||||||
// It's only the tokenID that doesn't care about raw byte slice,
|
return hex.EncodeToString(b), nil
|
||||||
// so we just encoded it in place and ignore bytes slice where we
|
|
||||||
// do not want it
|
|
||||||
return b, hex.EncodeToString(b), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateToken(s *kubeadmapi.Secrets) error {
|
func GenerateToken(d *kubeadmapi.TokenDiscovery) error {
|
||||||
_, tokenID, err := RandBytes(TokenIDLen / 2)
|
tokenID, err := RandBytes(TokenIDBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenBytes, token, err := RandBytes(TokenBytes)
|
token, err := RandBytes(TokenBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.TokenID = tokenID
|
d.ID = tokenID
|
||||||
s.BearerToken = token
|
d.Secret = token
|
||||||
s.Token = tokenBytes
|
|
||||||
s.GivenToken = fmt.Sprintf("%s.%s", tokenID, token)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UseGivenTokenIfValid(s *kubeadmapi.Secrets) (bool, error) {
|
var (
|
||||||
if s.GivenToken == "" {
|
tokenRegexpString = "^([a-zA-Z0-9]{6})\\.([a-zA-Z0-9]{16})$"
|
||||||
return false, nil // not given
|
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")
|
return split[1], split[2], nil
|
||||||
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 {
|
func BearerToken(d *kubeadmapi.TokenDiscovery) string {
|
||||||
return false, fmt.Errorf(invalidErr, "not in 2-part dot-separated format")
|
return fmt.Sprintf("%s.%s", d.ID, d.Secret)
|
||||||
}
|
}
|
||||||
if len(givenToken[0]) != TokenIDLen {
|
|
||||||
return false, fmt.Errorf(invalidErr, fmt.Sprintf(
|
func IsTokenValid(d *kubeadmapi.TokenDiscovery) (bool, error) {
|
||||||
"length of first part is incorrect [%d (given) != %d (expected) ]",
|
if len(d.ID)+len(d.Secret) == 0 {
|
||||||
len(givenToken[0]), TokenIDLen))
|
return false, nil
|
||||||
}
|
}
|
||||||
tokenBytes := []byte(givenToken[1])
|
if _, _, err := ParseToken(d.ID + "." + d.Secret); err != nil {
|
||||||
s.TokenID = givenToken[0]
|
return false, err
|
||||||
s.BearerToken = givenToken[1]
|
}
|
||||||
s.Token = tokenBytes
|
return true, nil
|
||||||
return true, nil // given and valid
|
}
|
||||||
|
|
||||||
|
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
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUsingEmptyTokenFails(t *testing.T) {
|
func TestTokenParseErrors(t *testing.T) {
|
||||||
// Simulates what happens when you omit --token on the CLI
|
invalidTokens := []string{
|
||||||
s := newSecretsWithToken("")
|
"1234567890123456789012",
|
||||||
|
"12345.1234567890123456",
|
||||||
given, err := UseGivenTokenIfValid(s)
|
".1234567890123456",
|
||||||
if err != nil {
|
"123456.1234567890.123456",
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rt := range tests {
|
for _, token := range invalidTokens {
|
||||||
s := newSecretsWithToken(rt.t)
|
if _, _, err := ParseToken(token); err == nil {
|
||||||
_, err := UseGivenTokenIfValid(s)
|
t.Errorf("generateTokenIfNeeded did not return an error for this invalid token: [%s]", token)
|
||||||
if (err == nil) != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed UseGivenTokenIfValid and did not return an error for this invalid token: [%s]",
|
|
||||||
rt.t,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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},
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUseGivenTokenIfValid(t *testing.T) {
|
func TestGenerateToken(t *testing.T) {
|
||||||
var tokenTest = []struct {
|
var cfg kubeadmapi.TokenDiscovery
|
||||||
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 {
|
GenerateToken(&cfg)
|
||||||
actual, _ := UseGivenTokenIfValid(&rt.s)
|
if len(cfg.ID) != 6 {
|
||||||
if actual != rt.expected {
|
t.Errorf("failed GenerateToken first part length:\n\texpected: 6\n\t actual: %d", len(cfg.ID))
|
||||||
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) {
|
func TestRandBytes(t *testing.T) {
|
||||||
var randTest = []struct {
|
var randTest = []int{
|
||||||
r int
|
0,
|
||||||
l int
|
1,
|
||||||
expected error
|
2,
|
||||||
}{
|
3,
|
||||||
{0, 0, nil},
|
100,
|
||||||
{1, 1, nil},
|
|
||||||
{2, 2, nil},
|
|
||||||
{3, 3, nil},
|
|
||||||
{100, 100, nil},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rt := range randTest {
|
for _, rt := range randTest {
|
||||||
actual, _, err := RandBytes(rt.r)
|
actual, err := RandBytes(rt)
|
||||||
if err != rt.expected {
|
if err != nil {
|
||||||
t.Errorf(
|
t.Errorf("failed RandBytes: %v", err)
|
||||||
"failed RandBytes:\n\texpected: %s\n\t actual: %s",
|
|
||||||
rt.expected,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if len(actual) != rt.l {
|
if len(actual) != rt*2 {
|
||||||
t.Errorf(
|
t.Errorf("failed RandBytes:\n\texpected: %d\n\t actual: %d\n", rt*2, len(actual))
|
||||||
"failed RandBytes:\n\texpected: %d\n\t actual: %d\n",
|
|
||||||
rt.l,
|
|
||||||
len(actual),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ package kubeadm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -29,7 +31,7 @@ const (
|
||||||
var kubeadmPath string
|
var kubeadmPath string
|
||||||
|
|
||||||
func init() {
|
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) {
|
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) {
|
func(obj *kubeadm.MasterConfiguration, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(obj)
|
c.FuzzNoCustom(obj)
|
||||||
obj.KubernetesVersion = "v10"
|
obj.KubernetesVersion = "v10"
|
||||||
obj.API.BindPort = 20
|
obj.API.Port = 20
|
||||||
obj.Discovery.BindPort = 20
|
|
||||||
obj.Networking.ServiceSubnet = "foo"
|
obj.Networking.ServiceSubnet = "foo"
|
||||||
obj.Networking.DNSDomain = "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) {
|
func(s *policy.PodDisruptionBudgetStatus, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
||||||
s.PodDisruptionsAllowed = int32(c.Rand.Intn(2))
|
s.PodDisruptionsAllowed = int32(c.Rand.Intn(2))
|
||||||
|
|
Loading…
Reference in New Issue