diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 93ad6803344..62010f10bea 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -17,7 +17,6 @@ limitations under the License. package kubeadm import ( - "fmt" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -116,7 +115,3 @@ type NodeConfiguration struct { // the security of kubeadm since other nodes can impersonate the master. DiscoveryTokenUnsafeSkipCAVerification bool } - -func (cfg *MasterConfiguration) GetMasterEndpoint() string { - return fmt.Sprintf("https://%s:%d", cfg.API.AdvertiseAddress, cfg.API.BindPort) -} diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD index c7412cc8938..5e5dbb15225 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD +++ b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD @@ -24,6 +24,7 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/cmd/features:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", + "//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util/token:go_default_library", "//pkg/api/validation:go_default_library", "//pkg/kubeapiserver/authorizer/modes:go_default_library", diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index dbdd252dccb..0bc24c57398 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -31,6 +31,7 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/features" "k8s.io/kubernetes/cmd/kubeadm/app/constants" + kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token" apivalidation "k8s.io/kubernetes/pkg/api/validation" authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" @@ -68,6 +69,7 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList allErrs = append(allErrs, ValidateNodeName(c.NodeName, field.NewPath("node-name"))...) allErrs = append(allErrs, ValidateToken(c.Token, field.NewPath("token"))...) allErrs = append(allErrs, ValidateFeatureFlags(c.FeatureFlags, field.NewPath("feature-flags"))...) + allErrs = append(allErrs, ValidateAPIEndpoint(c, field.NewPath("api-endpoint"))...) return allErrs } @@ -309,3 +311,13 @@ func ValidateFeatureFlags(featureFlags map[string]bool, fldPath *field.Path) fie return allErrs } + +func ValidateAPIEndpoint(c *kubeadm.MasterConfiguration, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + endpoint, err := kubeadmutil.GetMasterEndpoint(c) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, endpoint, "Invalid API Endpoint")) + } + return allErrs +} diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index e4fe08cf57c..ad84a58a646 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -209,6 +209,71 @@ func TestValidateIPNetFromString(t *testing.T) { } } +func TestValidateAPIEndpoint(t *testing.T) { + var tests = []struct { + name string + s *kubeadm.MasterConfiguration + expected bool + }{ + { + name: "Missing configuration", + s: &kubeadm.MasterConfiguration{}, + expected: false, + }, + { + name: "Valid IPv4 address and default port", + s: &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 6443, + }, + }, + expected: true, + }, + { + name: "Valid IPv6 address and port", + s: &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "2001:db7::1", + BindPort: 3446, + }, + }, + expected: true, + }, + { + name: "Invalid IPv4 address", + s: &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "1.2.34", + BindPort: 6443, + }, + }, + expected: false, + }, + { + name: "Invalid IPv6 address", + s: &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "2001:db7:1", + BindPort: 3446, + }, + }, + expected: false, + }, + } + for _, rt := range tests { + actual := ValidateAPIEndpoint(rt.s, nil) + if (len(actual) == 0) != rt.expected { + t.Errorf( + "%s test case failed:\n\texpected: %t\n\t actual: %t", + rt.name, + rt.expected, + (len(actual) == 0), + ) + } + } +} + func TestValidateMasterConfiguration(t *testing.T) { nodename := "valid-nodename" var tests = []struct { @@ -220,6 +285,10 @@ func TestValidateMasterConfiguration(t *testing.T) { &kubeadm.MasterConfiguration{}, false}, {"invalid missing token with IPv4 service subnet", &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 6443, + }, AuthorizationModes: []string{"Node", "RBAC"}, Networking: kubeadm.Networking{ ServiceSubnet: "10.96.0.1/12", @@ -230,6 +299,10 @@ func TestValidateMasterConfiguration(t *testing.T) { }, false}, {"invalid missing token with IPv6 service subnet", &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 6443, + }, AuthorizationModes: []string{"Node", "RBAC"}, Networking: kubeadm.Networking{ ServiceSubnet: "2001:db8::1/98", @@ -240,6 +313,10 @@ func TestValidateMasterConfiguration(t *testing.T) { }, false}, {"invalid missing node name", &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 6443, + }, AuthorizationModes: []string{"Node", "RBAC"}, Networking: kubeadm.Networking{ ServiceSubnet: "10.96.0.1/12", @@ -250,6 +327,10 @@ func TestValidateMasterConfiguration(t *testing.T) { }, false}, {"valid master configuration with IPv4 service subnet", &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 6443, + }, AuthorizationModes: []string{"Node", "RBAC"}, Networking: kubeadm.Networking{ ServiceSubnet: "10.96.0.1/12", @@ -261,6 +342,10 @@ func TestValidateMasterConfiguration(t *testing.T) { }, true}, {"valid master configuration using IPv6 service subnet", &kubeadm.MasterConfiguration{ + API: kubeadm.API{ + AdvertiseAddress: "1:2:3::4", + BindPort: 3446, + }, AuthorizationModes: []string{"Node", "RBAC"}, Networking: kubeadm.Networking{ ServiceSubnet: "2001:db8::1/98", diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 46d613b2e89..f1f88127397 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "io/ioutil" - "strconv" "text/template" "time" @@ -73,7 +72,7 @@ var ( You can now join any number of machines by running the following on each node as root: - kubeadm join --token {{.Token}} {{.MasterIP}}:{{.MasterPort}} --discovery-token-ca-cert-hash {{.CAPubKeyPin}} + kubeadm join --token {{.Token}} {{.MasterHostPort}} --discovery-token-ca-cert-hash {{.CAPubKeyPin}} `))) ) @@ -329,6 +328,9 @@ func (i *Init) Run(out io.Writer) error { // Load the CA certificate from so we can pin its public key caCert, err := pkiutil.TryLoadCertFromDisk(i.cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName) + + // Generate the Master host/port pair used by initDoneTempl + masterHostPort, err := kubeadmutil.GetMasterHostPort(i.cfg) if err != nil { return err } @@ -338,8 +340,7 @@ func (i *Init) Run(out io.Writer) error { "KubeConfigName": kubeadmconstants.AdminKubeConfigFileName, "Token": i.cfg.Token, "CAPubKeyPin": pubkeypin.Hash(caCert), - "MasterIP": i.cfg.API.AdvertiseAddress, - "MasterPort": strconv.Itoa(int(i.cfg.API.BindPort)), + "MasterHostPort": masterHostPort, } if i.skipTokenPrint { ctx["Token"] = "" diff --git a/cmd/kubeadm/app/phases/addons/proxy/proxy.go b/cmd/kubeadm/app/phases/addons/proxy/proxy.go index 51d53a37d4d..3cab1968131 100644 --- a/cmd/kubeadm/app/phases/addons/proxy/proxy.go +++ b/cmd/kubeadm/app/phases/addons/proxy/proxy.go @@ -49,10 +49,15 @@ func EnsureProxyAddon(cfg *kubeadmapi.MasterConfiguration, client clientset.Inte return fmt.Errorf("error when creating kube-proxy service account: %v", err) } + // Generate Master Enpoint kubeconfig file + masterEndpoint, err := kubeadmutil.GetMasterEndpoint(cfg) + if err != nil { + return err + } + proxyConfigMapBytes, err := kubeadmutil.ParseTemplate(KubeProxyConfigMap, struct{ MasterEndpoint string }{ // Fetch this value from the kubeconfig file - MasterEndpoint: fmt.Sprintf("https://%s:%d", cfg.API.AdvertiseAddress, cfg.API.BindPort), - }) + MasterEndpoint: masterEndpoint}) if err != nil { return fmt.Errorf("error when parsing kube-proxy configmap template: %v", err) } diff --git a/cmd/kubeadm/app/phases/kubeconfig/BUILD b/cmd/kubeadm/app/phases/kubeconfig/BUILD index 2ead488e93e..d62577dd5b3 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/BUILD +++ b/cmd/kubeadm/app/phases/kubeconfig/BUILD @@ -16,6 +16,7 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library", + "//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util/kubeconfig:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library", @@ -44,6 +45,7 @@ go_test( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library", + "//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/test:go_default_library", "//cmd/kubeadm/test/certs:go_default_library", "//cmd/kubeadm/test/kubeconfig:go_default_library", diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go index 667283b9258..1d1474417ba 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -32,6 +32,7 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil" + kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" ) @@ -134,10 +135,15 @@ func getKubeConfigSpecs(cfg *kubeadmapi.MasterConfiguration) (map[string]*kubeCo return nil, fmt.Errorf("couldn't create a kubeconfig; the CA files couldn't be loaded: %v", err) } + masterEndpoint, err := kubeadmutil.GetMasterEndpoint(cfg) + if err != nil { + return nil, err + } + var kubeConfigSpec = map[string]*kubeConfigSpec{ kubeadmconstants.AdminKubeConfigFileName: { CACert: caCert, - APIServer: cfg.GetMasterEndpoint(), + APIServer: masterEndpoint, ClientName: "kubernetes-admin", ClientCertAuth: &clientCertAuth{ CAKey: caKey, @@ -146,7 +152,7 @@ func getKubeConfigSpecs(cfg *kubeadmapi.MasterConfiguration) (map[string]*kubeCo }, kubeadmconstants.KubeletKubeConfigFileName: { CACert: caCert, - APIServer: cfg.GetMasterEndpoint(), + APIServer: masterEndpoint, ClientName: fmt.Sprintf("system:node:%s", cfg.NodeName), ClientCertAuth: &clientCertAuth{ CAKey: caKey, @@ -155,7 +161,7 @@ func getKubeConfigSpecs(cfg *kubeadmapi.MasterConfiguration) (map[string]*kubeCo }, kubeadmconstants.ControllerManagerKubeConfigFileName: { CACert: caCert, - APIServer: cfg.GetMasterEndpoint(), + APIServer: masterEndpoint, ClientName: kubeadmconstants.ControllerManagerUser, ClientCertAuth: &clientCertAuth{ CAKey: caKey, @@ -163,7 +169,7 @@ func getKubeConfigSpecs(cfg *kubeadmapi.MasterConfiguration) (map[string]*kubeCo }, kubeadmconstants.SchedulerKubeConfigFileName: { CACert: caCert, - APIServer: cfg.GetMasterEndpoint(), + APIServer: masterEndpoint, ClientName: kubeadmconstants.SchedulerUser, ClientCertAuth: &clientCertAuth{ CAKey: caKey, @@ -266,9 +272,14 @@ func WriteKubeConfigWithClientCert(out io.Writer, cfg *kubeadmapi.MasterConfigur return fmt.Errorf("couldn't create a kubeconfig; the CA files couldn't be loaded: %v", err) } + masterEndpoint, err := kubeadmutil.GetMasterEndpoint(cfg) + if err != nil { + return err + } + spec := &kubeConfigSpec{ ClientName: clientName, - APIServer: cfg.GetMasterEndpoint(), + APIServer: masterEndpoint, CACert: caCert, ClientCertAuth: &clientCertAuth{ CAKey: caKey, @@ -287,9 +298,14 @@ func WriteKubeConfigWithToken(out io.Writer, cfg *kubeadmapi.MasterConfiguration return fmt.Errorf("couldn't create a kubeconfig; the CA files couldn't be loaded: %v", err) } + masterEndpoint, err := kubeadmutil.GetMasterEndpoint(cfg) + if err != nil { + return err + } + spec := &kubeConfigSpec{ ClientName: clientName, - APIServer: cfg.GetMasterEndpoint(), + APIServer: masterEndpoint, CACert: caCert, TokenAuth: &tokenAuth{ Token: token, diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go index c2e376c248e..5290b2fb598 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go @@ -34,6 +34,7 @@ import ( pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil" + kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" testutil "k8s.io/kubernetes/cmd/kubeadm/test" certstestutil "k8s.io/kubernetes/cmd/kubeadm/test/certs" kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig" @@ -117,8 +118,12 @@ func TestGetKubeConfigSpecs(t *testing.T) { } // Asserts MasterConfiguration values injected into spec - if spec.APIServer != cfg.GetMasterEndpoint() { - t.Errorf("getKubeConfigSpecs didn't injected cfg.APIServer address into spec for %s", assertion.kubeConfigFile) + masterEndpoint, err := kubeadmutil.GetMasterEndpoint(cfg) + if err != nil { + t.Error(err) + } + if spec.APIServer != masterEndpoint { + t.Errorf("getKubeConfigSpecs didn't injected cfg.APIServer endpoint into spec for %s", assertion.kubeConfigFile) } // Asserts CA certs and CA keys loaded into specs diff --git a/cmd/kubeadm/app/util/BUILD b/cmd/kubeadm/app/util/BUILD index e06553c7893..b762a5d1fb2 100644 --- a/cmd/kubeadm/app/util/BUILD +++ b/cmd/kubeadm/app/util/BUILD @@ -9,11 +9,13 @@ load( go_library( name = "go_default_library", srcs = [ + "endpoint.go", "error.go", "template.go", "version.go", ], deps = [ + "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", ], @@ -22,12 +24,16 @@ go_library( go_test( name = "go_default_test", srcs = [ + "endpoint_test.go", "error_test.go", "template_test.go", "version_test.go", ], library = ":go_default_library", - deps = ["//cmd/kubeadm/app/preflight:go_default_library"], + deps = [ + "//cmd/kubeadm/app/apis/kubeadm:go_default_library", + "//cmd/kubeadm/app/preflight:go_default_library", + ], ) filegroup( diff --git a/cmd/kubeadm/app/util/endpoint.go b/cmd/kubeadm/app/util/endpoint.go new file mode 100644 index 00000000000..d3e32dbecea --- /dev/null +++ b/cmd/kubeadm/app/util/endpoint.go @@ -0,0 +1,51 @@ +/* +Copyright 2017 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 util + +import ( + "fmt" + "net" + "strconv" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" +) + +// GetMasterEndpoint returns a properly formatted Master Endpoint +// or passes the error from GetMasterHostPort. +func GetMasterEndpoint(cfg *kubeadmapi.MasterConfiguration) (string, error) { + hostPort, err := GetMasterHostPort(cfg) + if err != nil { + return "", err + } + return fmt.Sprintf("https://%s", hostPort), nil +} + +// GetMasterHostPort returns a properly formatted Master IP/port pair or error +// if the IP address can not be parsed or port is outside the valid TCP range. +func GetMasterHostPort(cfg *kubeadmapi.MasterConfiguration) (string, error) { + masterIP := net.ParseIP(cfg.API.AdvertiseAddress) + if masterIP == nil { + return "", fmt.Errorf("error parsing address %s", cfg.API.AdvertiseAddress) + } + + if cfg.API.BindPort < 0 || cfg.API.BindPort > 65535 { + return "", fmt.Errorf("api server port must be between 0 and 65535") + } + + hostPort := net.JoinHostPort(masterIP.String(), strconv.Itoa(int(cfg.API.BindPort))) + return hostPort, nil +} diff --git a/cmd/kubeadm/app/util/endpoint_test.go b/cmd/kubeadm/app/util/endpoint_test.go new file mode 100644 index 00000000000..f5bef19dbb5 --- /dev/null +++ b/cmd/kubeadm/app/util/endpoint_test.go @@ -0,0 +1,223 @@ +/* +Copyright 2017 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 util + +import ( + "testing" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" +) + +func TestGetMasterEndpoint(t *testing.T) { + var tests = []struct { + name string + cfg *kubeadmapi.MasterConfiguration + endpoint string + expected bool + }{ + { + name: "valid IPv4 endpoint", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 1234, + }, + }, + endpoint: "https://1.2.3.4:1234", + expected: true, + }, + { + name: "valid IPv6 endpoint", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "2001:db8::1", + BindPort: 4321, + }, + }, + endpoint: "https://[2001:db8::1]:4321", + expected: true, + }, + { + name: "invalid IPv4 endpoint", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 1234, + }, + }, + endpoint: "https://[1.2.3.4]:1234", + expected: false, + }, + { + name: "invalid IPv6 endpoint", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "2001:db8::1", + BindPort: 4321, + }, + }, + endpoint: "https://2001:db8::1:4321", + expected: false, + }, + { + name: "invalid IPv4 AdvertiseAddress", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.34", + BindPort: 1234, + }, + }, + endpoint: "https://1.2.3.4:1234", + expected: false, + }, + { + name: "invalid IPv6 AdvertiseAddress", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "2001::db8::1", + BindPort: 4321, + }, + }, + endpoint: "https://[2001:db8::1]:4321", + expected: false, + }, + } + for _, rt := range tests { + actual, err := GetMasterEndpoint(rt.cfg) + if err != nil && rt.expected { + t.Error(err) + } + if actual != rt.endpoint && rt.expected { + t.Errorf( + "%s test case failed:\n\texpected: %s\n\t actual: %s", + rt.name, + rt.endpoint, + (actual), + ) + } + } +} + +func TestGetMasterHostPort(t *testing.T) { + var tests = []struct { + name string + cfg *kubeadmapi.MasterConfiguration + hostPort string + expected bool + }{ + { + name: "valid IPv4 master host and port", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 1234, + }, + }, + hostPort: "1.2.3.4:1234", + expected: true, + }, + { + name: "valid IPv6 master host port", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "2001:db8::1", + BindPort: 4321, + }, + }, + hostPort: "[2001:db8::1]:4321", + expected: true, + }, + { + name: "invalid IPv4 address", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.34", + BindPort: 1234, + }, + }, + hostPort: "1.2.3.4:1234", + expected: false, + }, + { + name: "invalid IPv6 address", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "2001::db8::1", + BindPort: 4321, + }, + }, + hostPort: "[2001:db8::1]:4321", + expected: false, + }, + { + name: "invalid TCP port number", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: 987654321, + }, + }, + hostPort: "1.2.3.4:987654321", + expected: false, + }, + { + name: "invalid negative TCP port number", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.3.4", + BindPort: -987654321, + }, + }, + hostPort: "1.2.3.4:-987654321", + expected: false, + }, + { + name: "unspecified IPv4 TCP port", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1.2.3.4", + }, + }, + hostPort: "1.2.3.4:0", + expected: true, + }, + { + name: "unspecified IPv6 TCP port", + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{ + AdvertiseAddress: "1:2:3::4", + }, + }, + hostPort: "[1:2:3::4]:0", + expected: true, + }, + } + for _, rt := range tests { + actual, err := GetMasterHostPort(rt.cfg) + if err != nil && rt.expected { + t.Error(err) + } + if actual != rt.hostPort && rt.expected { + t.Errorf( + "%s test case failed:\n\texpected: %s\n\t actual: %s", + rt.name, + rt.hostPort, + (actual), + ) + } + } +}