mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
kubeadm: join ux changes
This commit is contained in:
parent
81d01a84e0
commit
1d37c6be49
@ -37,7 +37,12 @@ func KubeadmFuzzerFuncs(t apitesting.TestingCommon) []interface{} {
|
||||
func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj)
|
||||
obj.CACertPath = "foo"
|
||||
obj.Discovery.Token = &kubeadm.TokenDiscovery{}
|
||||
obj.CACertPath = "foo"
|
||||
obj.DiscoveryFile = "foo"
|
||||
obj.DiscoveryToken = "foo"
|
||||
obj.DiscoveryTokenAPIServers = []string{"foo"}
|
||||
obj.TLSBootstrapToken = "foo"
|
||||
obj.Token = "foo"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -92,8 +92,13 @@ type Etcd struct {
|
||||
type NodeConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
Discovery Discovery
|
||||
CACertPath string
|
||||
CACertPath string
|
||||
DiscoveryFile string
|
||||
DiscoveryToken string
|
||||
// Currently we only pay attention to one api server but hope to support >1 in the future
|
||||
DiscoveryTokenAPIServers []string
|
||||
TLSBootstrapToken string
|
||||
Token string
|
||||
}
|
||||
|
||||
// ClusterInfo TODO add description
|
||||
|
@ -16,7 +16,11 @@ limitations under the License.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import "k8s.io/apimachinery/pkg/runtime"
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultServiceDNSDomain = "cluster.local"
|
||||
@ -68,4 +72,17 @@ func SetDefaults_NodeConfiguration(obj *NodeConfiguration) {
|
||||
if obj.CACertPath == "" {
|
||||
obj.CACertPath = DefaultCACertPath
|
||||
}
|
||||
if len(obj.TLSBootstrapToken) == 0 {
|
||||
obj.TLSBootstrapToken = obj.Token
|
||||
}
|
||||
if len(obj.DiscoveryToken) == 0 && len(obj.DiscoveryFile) == 0 {
|
||||
obj.DiscoveryToken = obj.Token
|
||||
}
|
||||
// Make sure file URLs become paths
|
||||
if len(obj.DiscoveryFile) != 0 {
|
||||
u, err := url.Parse(obj.DiscoveryFile)
|
||||
if err == nil && u.Scheme == "file" {
|
||||
obj.DiscoveryFile = u.Path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,8 +82,12 @@ type Etcd struct {
|
||||
type NodeConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Discovery Discovery `json:"discovery"`
|
||||
CACertPath string `json:"caCertPath"`
|
||||
CACertPath string `json:"caCertPath"`
|
||||
DiscoveryFile string `json:"discoveryFile"`
|
||||
DiscoveryToken string `json:"discoveryToken"`
|
||||
DiscoveryTokenAPIServers []string `json:"discoveryTokenAPIServers"`
|
||||
TLSBootstrapToken string `json:"tlsBootstrapToken"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// ClusterInfo TODO add description
|
||||
|
@ -8,6 +8,17 @@ load(
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["validation_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["validation.go"],
|
||||
@ -15,6 +26,7 @@ go_library(
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util/token:go_default_library",
|
||||
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
|
||||
"//pkg/registry/core/service/ipallocator:go_default_library",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
||||
@ -33,14 +45,3 @@ filegroup(
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["validation_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
||||
],
|
||||
)
|
||||
|
@ -17,13 +17,17 @@ limitations under the License.
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
||||
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
)
|
||||
@ -45,7 +49,6 @@ var cloudproviders = []string{
|
||||
|
||||
func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
|
||||
allErrs = append(allErrs, ValidateServiceSubnet(c.Networking.ServiceSubnet, field.NewPath("service subnet"))...)
|
||||
allErrs = append(allErrs, ValidateCloudProvider(c.CloudProvider, field.NewPath("cloudprovider"))...)
|
||||
allErrs = append(allErrs, ValidateAuthorizationMode(c.AuthorizationMode, field.NewPath("authorization-mode"))...)
|
||||
@ -54,70 +57,107 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList
|
||||
|
||||
func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
|
||||
allErrs = append(allErrs, ValidateDiscovery(c, field.NewPath("discovery"))...)
|
||||
|
||||
if !path.IsAbs(c.CACertPath) || !strings.HasSuffix(c.CACertPath, ".crt") {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("ca-cert-path"), nil, "the ca certificate path must be an absolute path"))
|
||||
}
|
||||
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{}
|
||||
if len(c.ID) == 0 || len(c.Secret) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, nil, "token must be specific as <ID>:<Secret>"))
|
||||
}
|
||||
if len(c.Addresses) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, nil, "at least one address is required"))
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("ca-cert-path"), c.CACertPath, "the ca certificate path must be an absolute path"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateAuthorizationMode(authzMode string, fldPath *field.Path) field.ErrorList {
|
||||
if !authzmodes.IsValidAuthorizationMode(authzMode) {
|
||||
return field.ErrorList{field.Invalid(fldPath, nil, "invalid authorization mode")}
|
||||
return field.ErrorList{field.Invalid(fldPath, authzMode, "invalid authorization mode")}
|
||||
}
|
||||
return field.ErrorList{}
|
||||
}
|
||||
|
||||
func ValidateDiscovery(c *kubeadm.NodeConfiguration, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(c.DiscoveryToken) != 0 {
|
||||
allErrs = append(allErrs, ValidateToken(c.DiscoveryToken, fldPath)...)
|
||||
}
|
||||
if len(c.DiscoveryFile) != 0 {
|
||||
allErrs = append(allErrs, ValidateDiscoveryFile(c.DiscoveryFile, fldPath)...)
|
||||
}
|
||||
allErrs = append(allErrs, ValidateArgSelection(c, fldPath)...)
|
||||
allErrs = append(allErrs, ValidateToken(c.TLSBootstrapToken, fldPath)...)
|
||||
allErrs = append(allErrs, ValidateJoinDiscoveryTokenAPIServer(c, fldPath)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateArgSelection(cfg *kubeadm.NodeConfiguration, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(cfg.DiscoveryToken) != 0 && len(cfg.DiscoveryFile) != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, "", "DiscoveryToken and DiscoveryFile cannot both be set"))
|
||||
}
|
||||
if len(cfg.DiscoveryToken) == 0 && len(cfg.DiscoveryFile) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, "", "DiscoveryToken or DiscoveryFile must be set"))
|
||||
}
|
||||
if len(cfg.DiscoveryTokenAPIServers) < 1 && len(cfg.DiscoveryToken) != 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "DiscoveryTokenAPIServers not set"))
|
||||
}
|
||||
// TODO remove once we support multiple api servers
|
||||
if len(cfg.DiscoveryTokenAPIServers) > 1 {
|
||||
fmt.Println("[validation] WARNING: kubeadm doesn't fully support multiple API Servers yet")
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateJoinDiscoveryTokenAPIServer(c *kubeadm.NodeConfiguration, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for _, m := range c.DiscoveryTokenAPIServers {
|
||||
_, _, err := net.SplitHostPort(m)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, m, err.Error()))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateDiscoveryFile(discoveryFile string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
u, err := url.Parse(discoveryFile)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, discoveryFile, "not a valid HTTPS URL or a file on disk"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if u.Scheme == "" {
|
||||
// URIs with no scheme should be treated as files
|
||||
if _, err := os.Stat(discoveryFile); os.IsNotExist(err) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, discoveryFile, "not a valid HTTPS URL or a file on disk"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if u.Scheme != "https" {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, discoveryFile, "if an URL is used, the scheme must be https"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateToken(t string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
id, secret, err := tokenutil.ParseToken(t)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, nil, err.Error()))
|
||||
}
|
||||
|
||||
if len(id) == 0 || len(secret) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, nil, "token must be of form '[a-z0-9]{6}.[a-z0-9]{16}'"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateServiceSubnet(subnet string, fldPath *field.Path) field.ErrorList {
|
||||
_, svcSubnet, err := net.ParseCIDR(subnet)
|
||||
if err != nil {
|
||||
return field.ErrorList{field.Invalid(fldPath, nil, "couldn't parse the service subnet")}
|
||||
}
|
||||
numAddresses := ipallocator.RangeSize(svcSubnet)
|
||||
if numAddresses < kubeadmconstants.MinimumAddressesInServiceSubnet {
|
||||
if numAddresses < constants.MinimumAddressesInServiceSubnet {
|
||||
return field.ErrorList{field.Invalid(fldPath, nil, "service subnet is too small")}
|
||||
}
|
||||
return field.ErrorList{}
|
||||
|
@ -25,17 +25,16 @@ import (
|
||||
|
||||
func TestValidateTokenDiscovery(t *testing.T) {
|
||||
var tests = []struct {
|
||||
c *kubeadm.TokenDiscovery
|
||||
c *kubeadm.NodeConfiguration
|
||||
f *field.Path
|
||||
expected bool
|
||||
}{
|
||||
{&kubeadm.TokenDiscovery{ID: "772ef5", Secret: "6b6baab1d4a0a171", Addresses: []string{"192.168.122.100:9898"}}, nil, true},
|
||||
{&kubeadm.TokenDiscovery{ID: "", Secret: "6b6baab1d4a0a171", Addresses: []string{"192.168.122.100:9898"}}, nil, false},
|
||||
{&kubeadm.TokenDiscovery{ID: "772ef5", Secret: "", Addresses: []string{"192.168.122.100:9898"}}, nil, false},
|
||||
{&kubeadm.TokenDiscovery{ID: "772ef5", Secret: "6b6baab1d4a0a171", Addresses: []string{}}, nil, false},
|
||||
{&kubeadm.NodeConfiguration{Token: "772ef5.6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, true},
|
||||
{&kubeadm.NodeConfiguration{Token: ".6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, false},
|
||||
{&kubeadm.NodeConfiguration{Token: "772ef5.", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, false},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
err := ValidateTokenDiscovery(rt.c, rt.f).ToAggregate()
|
||||
err := ValidateToken(rt.c.Token, rt.f).ToAggregate()
|
||||
if (err == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed ValidateTokenDiscovery:\n\texpected: %t\n\t actual: %t",
|
||||
@ -125,21 +124,6 @@ func TestValidateMasterConfiguration(t *testing.T) {
|
||||
expected bool
|
||||
}{
|
||||
{&kubeadm.MasterConfiguration{}, false},
|
||||
{&kubeadm.MasterConfiguration{
|
||||
Discovery: kubeadm.Discovery{
|
||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
||||
File: &kubeadm.FileDiscovery{Path: "foo"},
|
||||
Token: &kubeadm.TokenDiscovery{
|
||||
ID: "abcdef",
|
||||
Secret: "1234567890123456",
|
||||
Addresses: []string{"foobar"},
|
||||
},
|
||||
},
|
||||
AuthorizationMode: "RBAC",
|
||||
Networking: kubeadm.Networking{
|
||||
ServiceSubnet: "10.96.0.1/12",
|
||||
},
|
||||
}, false},
|
||||
{&kubeadm.MasterConfiguration{
|
||||
Discovery: kubeadm.Discovery{
|
||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
||||
@ -191,45 +175,10 @@ func TestValidateNodeConfiguration(t *testing.T) {
|
||||
}{
|
||||
{&kubeadm.NodeConfiguration{}, false},
|
||||
{&kubeadm.NodeConfiguration{
|
||||
Discovery: kubeadm.Discovery{
|
||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
||||
File: &kubeadm.FileDiscovery{Path: "foo"},
|
||||
Token: &kubeadm.TokenDiscovery{
|
||||
ID: "abcdef",
|
||||
Secret: "1234567890123456",
|
||||
Addresses: []string{"foobar"},
|
||||
},
|
||||
},
|
||||
CACertPath: "/some/cert.crt",
|
||||
DiscoveryFile: "foo",
|
||||
DiscoveryToken: "abcdef.1234567890123456@foobar",
|
||||
CACertPath: "/some/cert.crt",
|
||||
}, false},
|
||||
{&kubeadm.NodeConfiguration{
|
||||
Discovery: kubeadm.Discovery{
|
||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
||||
},
|
||||
CACertPath: "/some/path", // no .crt suffix
|
||||
}, false},
|
||||
{&kubeadm.NodeConfiguration{
|
||||
Discovery: kubeadm.Discovery{
|
||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
||||
},
|
||||
CACertPath: "/some/cert.crt",
|
||||
}, true},
|
||||
{&kubeadm.NodeConfiguration{
|
||||
Discovery: kubeadm.Discovery{
|
||||
File: &kubeadm.FileDiscovery{Path: "foo"},
|
||||
},
|
||||
CACertPath: "/some/other/cert.crt",
|
||||
}, true},
|
||||
{&kubeadm.NodeConfiguration{
|
||||
Discovery: kubeadm.Discovery{
|
||||
Token: &kubeadm.TokenDiscovery{
|
||||
ID: "abcdef",
|
||||
Secret: "1234567890123456",
|
||||
Addresses: []string{"foobar"},
|
||||
},
|
||||
},
|
||||
CACertPath: "/a/third/cert.crt",
|
||||
}, true},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
actual := ValidateNodeConfiguration(rt.s)
|
||||
|
@ -52,37 +52,76 @@ var (
|
||||
|
||||
// NewCmdJoin returns "kubeadm join" command.
|
||||
func NewCmdJoin(out io.Writer) *cobra.Command {
|
||||
versioned := &kubeadmapiext.NodeConfiguration{}
|
||||
api.Scheme.Default(versioned)
|
||||
cfg := kubeadmapi.NodeConfiguration{}
|
||||
api.Scheme.Convert(versioned, &cfg, nil)
|
||||
cfg := &kubeadmapiext.NodeConfiguration{}
|
||||
api.Scheme.Default(cfg)
|
||||
|
||||
var skipPreFlight bool
|
||||
var cfgPath string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "join <master address>",
|
||||
Use: "join <flags> [DiscoveryTokenAPIServers]",
|
||||
Short: "Run this on any machine you wish to join an existing cluster",
|
||||
Long: dedent.Dedent(`
|
||||
When joining a kubeadm initialized cluster, we need to establish
|
||||
bidirectional trust. This is split into discovery (having the Node
|
||||
trust the Kubernetes Master) and TLS bootstrap (having the Kubernetes
|
||||
Master trust the Node).
|
||||
|
||||
There are 2 main schemes for discovery. The first is to use a shared
|
||||
token along with the IP address of the API server. The second is to
|
||||
provide a file (a subset of the standard kubeconfig file). This file
|
||||
can be a local file or downloaded via an HTTPS URL. The forms are
|
||||
kubeadm join --discovery-token abcdef.1234567890abcdef 1.2.3.4:6443,
|
||||
kubeadm join --discovery-file path/to/file.conf or kubeadm join
|
||||
--discovery-file https://url/file.conf. Only one form can be used. If
|
||||
the discovery information is loaded from a URL, HTTPS must be used and
|
||||
the host installed CA bundle is used to verify the connection.
|
||||
|
||||
The TLS bootstrap mechanism is also driven via a shared token. This is
|
||||
used to temporarily authenticate with the Kubernetes Master to submit a
|
||||
certificate signing request (CSR) for a locally created key pair. By
|
||||
default kubeadm will set up the Kubernetes Master to automatically
|
||||
approve these signing requests. This token is passed in with the
|
||||
--tls-bootstrap-token abcdef.1234567890abcdef flag.
|
||||
|
||||
Often times the same token is use for both parts. In this case, the
|
||||
--token flag can be used instead of specifying the each token
|
||||
individually.
|
||||
`),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
j, err := NewJoin(cfgPath, args, &cfg, skipPreFlight)
|
||||
cfg.DiscoveryTokenAPIServers = args
|
||||
api.Scheme.Default(cfg)
|
||||
internalcfg := &kubeadmapi.NodeConfiguration{}
|
||||
api.Scheme.Convert(cfg, internalcfg, nil)
|
||||
j, err := NewJoin(cfgPath, args, internalcfg, skipPreFlight)
|
||||
kubeadmutil.CheckErr(err)
|
||||
kubeadmutil.CheckErr(j.Validate())
|
||||
kubeadmutil.CheckErr(j.Run(out))
|
||||
},
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfgPath, "config", cfgPath,
|
||||
"Path to kubeadm config file")
|
||||
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfg.DiscoveryFile, "discovery-file", "",
|
||||
"A file or url from which to load cluster information")
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfg.DiscoveryToken, "discovery-token", "",
|
||||
"A token used to validate cluster information fetched from the master")
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfg.TLSBootstrapToken, "tls-bootstrap-token", "",
|
||||
"A token used for TLS bootstrapping")
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfg.Token, "token", "",
|
||||
"Use this token for both discovery-token and tls-bootstrap-token")
|
||||
|
||||
cmd.PersistentFlags().BoolVar(
|
||||
&skipPreFlight, "skip-preflight-checks", false,
|
||||
"skip preflight checks normally run before modifying the system",
|
||||
)
|
||||
|
||||
cmd.PersistentFlags().Var(
|
||||
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
|
||||
"The discovery method kubeadm will use for connecting nodes to the master",
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -131,7 +170,7 @@ func (j *Join) Validate() error {
|
||||
|
||||
// Run executes worker node provisioning and tries to join an existing cluster.
|
||||
func (j *Join) Run(out io.Writer) error {
|
||||
cfg, err := discovery.For(j.cfg.Discovery)
|
||||
cfg, err := discovery.For(j.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
@ -29,27 +30,34 @@ import (
|
||||
)
|
||||
|
||||
// For identifies and executes the desired discovery mechanism.
|
||||
func For(d kubeadmapi.Discovery) (*clientcmdapi.Config, error) {
|
||||
func For(d *kubeadmapi.NodeConfiguration) (*clientcmdapi.Config, error) {
|
||||
switch {
|
||||
case d.File != nil:
|
||||
return runFileDiscovery(d.File)
|
||||
case d.HTTPS != nil:
|
||||
return runHTTPSDiscovery(d.HTTPS)
|
||||
case d.Token != nil:
|
||||
return runTokenDiscovery(d.Token)
|
||||
case len(d.DiscoveryFile) != 0:
|
||||
if isHTTPSURL(d.DiscoveryFile) {
|
||||
return runHTTPSDiscovery(d.DiscoveryFile)
|
||||
}
|
||||
return runFileDiscovery(d.DiscoveryFile)
|
||||
case len(d.DiscoveryToken) != 0:
|
||||
return runTokenDiscovery(d.DiscoveryToken, d.DiscoveryTokenAPIServers)
|
||||
default:
|
||||
return nil, fmt.Errorf("couldn't find a valid discovery configuration.")
|
||||
}
|
||||
}
|
||||
|
||||
// isHTTPSURL checks whether the string is parsable as an URL
|
||||
func isHTTPSURL(s string) bool {
|
||||
u, err := url.Parse(s)
|
||||
return err == nil && u.Scheme == "https"
|
||||
}
|
||||
|
||||
// runFileDiscovery executes file-based discovery.
|
||||
func runFileDiscovery(fd *kubeadmapi.FileDiscovery) (*clientcmdapi.Config, error) {
|
||||
return clientcmd.LoadFromFile(fd.Path)
|
||||
func runFileDiscovery(fd string) (*clientcmdapi.Config, error) {
|
||||
return clientcmd.LoadFromFile(fd)
|
||||
}
|
||||
|
||||
// runHTTPSDiscovery executes HTTPS-based discovery.
|
||||
func runHTTPSDiscovery(hd *kubeadmapi.HTTPSDiscovery) (*clientcmdapi.Config, error) {
|
||||
response, err := http.Get(hd.URL)
|
||||
func runHTTPSDiscovery(hd string) (*clientcmdapi.Config, error) {
|
||||
response, err := http.Get(hd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -64,17 +72,23 @@ func runHTTPSDiscovery(hd *kubeadmapi.HTTPSDiscovery) (*clientcmdapi.Config, err
|
||||
}
|
||||
|
||||
// runTokenDiscovery executes token-based discovery.
|
||||
func runTokenDiscovery(td *kubeadmapi.TokenDiscovery) (*clientcmdapi.Config, error) {
|
||||
if valid, err := tokenutil.ValidateToken(td); valid == false {
|
||||
func runTokenDiscovery(td string, m []string) (*clientcmdapi.Config, error) {
|
||||
id, secret, err := tokenutil.ParseToken(td)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := &kubeadmapi.TokenDiscovery{ID: id, Secret: secret, Addresses: m}
|
||||
|
||||
if valid, err := tokenutil.ValidateToken(t); valid == false {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(td)
|
||||
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, err := kubenode.EstablishMasterConnection(td, clusterInfo)
|
||||
cfg, err := kubenode.EstablishMasterConnection(t, clusterInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -24,40 +24,37 @@ import (
|
||||
|
||||
func TestFor(t *testing.T) {
|
||||
tests := []struct {
|
||||
d kubeadm.Discovery
|
||||
d kubeadm.NodeConfiguration
|
||||
expect bool
|
||||
}{
|
||||
{d: kubeadm.Discovery{}, expect: false},
|
||||
{d: kubeadm.NodeConfiguration{}, expect: false},
|
||||
{
|
||||
d: kubeadm.Discovery{
|
||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "notnil"},
|
||||
d: kubeadm.NodeConfiguration{
|
||||
DiscoveryFile: "notnil",
|
||||
},
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
d: kubeadm.Discovery{
|
||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "http://localhost"},
|
||||
d: kubeadm.NodeConfiguration{
|
||||
DiscoveryFile: "https://localhost",
|
||||
},
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
d: kubeadm.Discovery{
|
||||
File: &kubeadm.FileDiscovery{Path: "notnil"},
|
||||
d: kubeadm.NodeConfiguration{
|
||||
DiscoveryFile: "notnil",
|
||||
},
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
d: kubeadm.Discovery{
|
||||
Token: &kubeadm.TokenDiscovery{
|
||||
ID: "foo",
|
||||
Secret: "bar",
|
||||
Addresses: []string{"foobar"},
|
||||
},
|
||||
}, expect: false,
|
||||
d: kubeadm.NodeConfiguration{
|
||||
DiscoveryToken: "foo.bar@foobar",
|
||||
},
|
||||
expect: false,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
_, actual := For(rt.d)
|
||||
_, actual := For(&rt.d)
|
||||
if (actual == nil) != rt.expect {
|
||||
t.Errorf(
|
||||
"failed For:\n\texpected: %t\n\t actual: %t",
|
||||
|
@ -46,32 +46,3 @@ func TestCmdJoinConfig(t *testing.T) {
|
||||
kubeadmReset()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmdJoinDiscovery(t *testing.T) {
|
||||
if *kubeadmCmdSkip {
|
||||
t.Log("kubeadm cmd tests being skipped")
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var initTest = []struct {
|
||||
args string
|
||||
expected bool
|
||||
}{
|
||||
{"--discovery=foobar", false},
|
||||
{"--dicovery=magic", false},
|
||||
}
|
||||
|
||||
for _, rt := range initTest {
|
||||
_, _, actual := RunCmd(*kubeadmPath, "join", rt.args, "--skip-preflight-checks")
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CmdJoinDiscovery running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t",
|
||||
rt.args,
|
||||
actual,
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
kubeadmReset()
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +154,9 @@ disable-attach-detach-reconcile-sync
|
||||
disable-filter
|
||||
disable-kubenet
|
||||
disable-log-dump
|
||||
discovery-file
|
||||
discovery-port
|
||||
discovery-token
|
||||
dns-bind-address
|
||||
dns-port
|
||||
dns-provider
|
||||
@ -647,6 +649,7 @@ tcp-services
|
||||
terminated-pod-gc-threshold
|
||||
test-flags
|
||||
test-timeout
|
||||
tls-bootstrap-token
|
||||
tls-ca-file
|
||||
tls-cert-file
|
||||
tls-private-key-file
|
||||
@ -666,6 +669,8 @@ upgrade-image
|
||||
upgrade-target
|
||||
use-kubernetes-cluster-service
|
||||
use-kubernetes-version
|
||||
use-service-account-credentials
|
||||
use-taint-based-evictions
|
||||
user-whitelist
|
||||
use-service-account-credentials
|
||||
use-service-account-credentials
|
||||
|
Loading…
Reference in New Issue
Block a user