diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index c4ae376c7af..6b806d65410 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -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" }, } } diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 8e4a3be87a3..653416570db 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -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 diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go index a6385a31bd2..ea89e7b46fd 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go @@ -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 + } + } } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go index 99131d607be..b1c1b257814 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go @@ -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 diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD index 710989116a4..0c3f1130072 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD +++ b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD @@ -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", - ], -) diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index f71a19bdc4d..8505a4de2b5 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -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 :")) - } - 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{} diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 2486972cd26..916028cc293 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -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) diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index 7df4c5b8339..790c4be32b6 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -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 ", + Use: "join [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 } diff --git a/cmd/kubeadm/app/discovery/discovery.go b/cmd/kubeadm/app/discovery/discovery.go index ac6118e94fc..a1463e9fd70 100644 --- a/cmd/kubeadm/app/discovery/discovery.go +++ b/cmd/kubeadm/app/discovery/discovery.go @@ -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 } diff --git a/cmd/kubeadm/app/discovery/discovery_test.go b/cmd/kubeadm/app/discovery/discovery_test.go index 7c0f2f8ee4d..159a626b3e7 100644 --- a/cmd/kubeadm/app/discovery/discovery_test.go +++ b/cmd/kubeadm/app/discovery/discovery_test.go @@ -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", diff --git a/cmd/kubeadm/test/cmd/join_test.go b/cmd/kubeadm/test/cmd/join_test.go index 0e9eea7dc71..00ac7576df9 100644 --- a/cmd/kubeadm/test/cmd/join_test.go +++ b/cmd/kubeadm/test/cmd/join_test.go @@ -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() - } -} diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index a1d5a810df9..53909d9199a 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -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