blob: 5e7c058681aec492ecf6ece018a25621ea85fa00 [file] [log] [blame]
/*
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 validation
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
utilpointer "k8s.io/utils/pointer"
)
func TestValidateToken(t *testing.T) {
var tests = []struct {
token string
expected bool
}{
{"772ef5.6b6baab1d4a0a171", true},
{".6b6baab1d4a0a171", false},
{"772ef5.", false},
{"772ef5.6b6baab1d4a0a171", true},
{".6b6baab1d4a0a171", false},
{"772ef5.", false},
{"abcdef.1234567890123456@foobar", false},
}
for _, rt := range tests {
err := ValidateToken(rt.token, nil).ToAggregate()
if (err == nil) != rt.expected {
t.Errorf(
"failed ValidateToken:\n\texpected: %t\n\t actual: %t",
rt.expected,
(err == nil),
)
}
}
}
func TestValidateValidateTokenUsages(t *testing.T) {
var tests = []struct {
u []string
f *field.Path
expected bool
}{
{[]string{}, nil, true}, // supported (no usages)
{[]string{"signing", "authentication"}, nil, true}, // supported
{[]string{"something else"}, nil, false}, // usage not supported
}
for _, rt := range tests {
actual := ValidateTokenUsages(rt.u, rt.f)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateTokenUsages:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateTokenGroups(t *testing.T) {
var tests = []struct {
u []string
g []string
f *field.Path
expected bool
}{
{[]string{"some usage"}, []string{"some group"}, nil, false}, // groups doesn't makes sense if usage authentication
{[]string{"authentication"}, []string{"some group"}, nil, false}, // group not supported
{[]string{"authentication"}, []string{"system:bootstrappers:anygroup"}, nil, true}, // supported
}
for _, rt := range tests {
actual := ValidateTokenGroups(rt.u, rt.g, rt.f)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateTokenGroups:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateNodeRegistrationOptions(t *testing.T) {
var tests = []struct {
nodeName string
criSocket string
expectedErrors bool
}{
{"", "/some/path", true}, // node name can't be empty
{"INVALID-NODENAME", "/some/path", true}, // Upper cases is invalid
{"invalid-nodename-", "/some/path", true}, // Can't have trailing dashes
{"invalid-node?name", "/some/path", true}, // Unsupported characters
{"valid-nodename", "/some/path", false}, // supported
{"valid-nodename-with-numbers01234", "/some/path/with/numbers/01234/", false}, // supported, with numbers as well
{"valid-nodename", kubeadmapiv1beta1.DefaultUrlScheme + "://" + "/some/path", false}, // supported, with socket url
{"valid-nodename", "bla:///some/path", true}, // unsupported url scheme
{"valid-nodename", ":::", true}, // unparseable url
}
for _, rt := range tests {
nro := kubeadm.NodeRegistrationOptions{Name: rt.nodeName, CRISocket: rt.criSocket}
actual := ValidateNodeRegistrationOptions(&nro, field.NewPath("nodeRegistration"))
actualErrors := len(actual) > 0
if actualErrors != rt.expectedErrors {
t.Errorf(
"failed ValidateNodeRegistrationOptions: value: %v\n\texpected: %t\n\t actual: %t",
nro,
rt.expectedErrors,
actualErrors,
)
}
}
}
func TestValidateCertSANs(t *testing.T) {
var tests = []struct {
sans []string
expected bool
}{
{[]string{}, true}, // ok if not provided
{[]string{"1,2,,3"}, false}, // not a DNS label or IP
{[]string{"my-hostname", "???&?.garbage"}, false}, // not valid
{[]string{"my-hostname", "my.subdomain", "1.2.3.4"}, true}, // supported
{[]string{"my-hostname2", "my.other.subdomain", "10.0.0.10"}, true}, // supported
{[]string{"my-hostname", "my.subdomain", "2001:db8::4"}, true}, // supported
{[]string{"my-hostname2", "my.other.subdomain", "2001:db8::10"}, true}, // supported
}
for _, rt := range tests {
actual := ValidateCertSANs(rt.sans, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateCertSANs:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateIPFromString(t *testing.T) {
var tests = []struct {
name string
ip string
expected bool
}{
{"invalid missing address", "", false},
{"invalid missing decimal points in IPv4 address", "1234", false},
{"invalid incomplete IPv4 address", "1.2", false},
{"invalid IPv4 CIDR provided instead of IPv4 address", "1.2.3.4/16", false},
{"valid IPv4 address", "1.2.3.4", true},
{"valid IPv6 address", "2001:db8::1", true},
{"invalid IPv6 CIDR provided instead of IPv6 address", "2001:db8::1/64", false},
{"invalid hex character in IPv6 address", "2001:xb8::", false},
{"invalid use of colons in IPv6 address", "2001::db8::", false},
}
for _, rt := range tests {
actual := ValidateIPFromString(rt.ip, 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 TestValidateIPNetFromString(t *testing.T) {
var tests = []struct {
name string
subnet string
minaddrs int64
expected bool
}{
{"invalid missing CIDR", "", 0, false},
{"invalid CIDR missing decimal points in IPv4 address and / mask", "1234", 0, false},
{"invalid CIDR use of letters instead of numbers and / mask", "abc", 0, false},
{"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4", 0, false},
{"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1", 0, false},
{"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29", 10, false},
{"valid, but IPv6 CIDR too small. At least 10 addresses needed", "2001:db8::/125", 10, false},
{"valid IPv4 CIDR", "10.0.0.16/12", 10, true},
{"valid IPv6 CIDR", "2001:db8::/98", 10, true},
}
for _, rt := range tests {
actual := ValidateIPNetFromString(rt.subnet, rt.minaddrs, 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 TestValidateHostPort(t *testing.T) {
var tests = []struct {
name string
s string
expected bool
}{
{
name: "Valid DNS address / port",
s: "cp.k8s.io:8081",
expected: true,
},
{
name: "Valid DNS address",
s: "cp.k8s.io",
expected: true,
},
{
name: "Valid IPv4 address / port",
s: "1.2.3.4:8081",
expected: true,
},
{
name: "Valid IPv4 address",
s: "1.2.3.4",
expected: true,
},
{
name: "Valid IPv6 address / port",
s: "[2001:db7::1]:8081",
expected: true,
},
{
name: "Valid IPv6 address",
s: "2001:db7::1",
expected: true,
},
{
name: "Invalid IPv4 address, but valid DNS",
s: "1.2.34",
expected: true,
},
{
name: "Invalid DNS",
s: "a.B.c.d.e",
expected: false,
},
{
name: "Invalid IPv6 address",
s: "2001:db7:1",
expected: false,
},
{
name: "Invalid BindPort",
s: "1.2.3.4:0",
expected: false,
},
}
for _, rt := range tests {
actual := ValidateHostPort(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 TestValidateAPIEndpoint(t *testing.T) {
var tests = []struct {
name string
s *kubeadm.APIEndpoint
expected bool
}{
{
name: "Valid IPv4 address / port",
s: &kubeadm.APIEndpoint{
AdvertiseAddress: "4.5.6.7",
BindPort: 6443,
},
expected: true,
},
{
name: "Valid IPv6 address / port",
s: &kubeadm.APIEndpoint{
AdvertiseAddress: "2001:db7::2",
BindPort: 6443,
},
expected: true,
},
{
name: "Invalid IPv4 address",
s: &kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.34",
BindPort: 6443,
},
expected: false,
},
{
name: "Invalid IPv6 address",
s: &kubeadm.APIEndpoint{
AdvertiseAddress: "2001:db7:1",
BindPort: 6443,
},
expected: false,
},
{
name: "Invalid BindPort",
s: &kubeadm.APIEndpoint{
AdvertiseAddress: "4.5.6.7",
BindPort: 0,
},
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),
)
}
}
}
//TODO: Create a separated test for ValidateClusterConfiguration
func TestValidateInitConfiguration(t *testing.T) {
nodename := "valid-nodename"
var tests = []struct {
name string
s *kubeadm.InitConfiguration
expected bool
}{
{"invalid missing master configuration",
&kubeadm.InitConfiguration{}, false},
{"invalid missing token with IPv4 service subnet",
&kubeadm.InitConfiguration{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
ClusterConfiguration: kubeadm.ClusterConfiguration{
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/cert/dir",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
}, false},
{"invalid missing token with IPv6 service subnet",
&kubeadm.InitConfiguration{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
ClusterConfiguration: kubeadm.ClusterConfiguration{
Networking: kubeadm.Networking{
ServiceSubnet: "2001:db8::1/98",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/cert/dir",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
}, false},
{"invalid missing node name",
&kubeadm.InitConfiguration{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
ClusterConfiguration: kubeadm.ClusterConfiguration{
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/other/cert/dir",
},
}, false},
{"valid master configuration with incorrect IPv4 pod subnet",
&kubeadm.InitConfiguration{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
ClusterConfiguration: kubeadm.ClusterConfiguration{
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
DNSDomain: "cluster.local",
PodSubnet: "10.0.1.15",
},
CertificatesDir: "/some/other/cert/dir",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
}, false},
{"valid master configuration with IPv4 service subnet",
&kubeadm.InitConfiguration{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
ClusterConfiguration: kubeadm.ClusterConfiguration{
Etcd: kubeadm.Etcd{
Local: &kubeadm.LocalEtcd{
DataDir: "/some/path",
},
},
ComponentConfigs: kubeadm.ComponentConfigs{
KubeProxy: &kubeproxyconfig.KubeProxyConfiguration{
BindAddress: "192.168.59.103",
HealthzBindAddress: "0.0.0.0:10256",
MetricsBindAddress: "127.0.0.1:10249",
ClusterCIDR: "192.168.59.0/24",
UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second},
ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
MasqueradeAll: true,
SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
},
IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{
SyncPeriod: metav1.Duration{Duration: 10 * time.Second},
MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second},
},
Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
Max: utilpointer.Int32Ptr(2),
MaxPerCore: utilpointer.Int32Ptr(1),
Min: utilpointer.Int32Ptr(1),
TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
},
},
},
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
DNSDomain: "cluster.local",
PodSubnet: "10.0.1.15/16",
},
CertificatesDir: "/some/other/cert/dir",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
}, true},
{"valid master configuration using IPv6 service subnet",
&kubeadm.InitConfiguration{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1:2:3::4",
BindPort: 3446,
},
ClusterConfiguration: kubeadm.ClusterConfiguration{
Etcd: kubeadm.Etcd{
Local: &kubeadm.LocalEtcd{
DataDir: "/some/path",
},
},
ComponentConfigs: kubeadm.ComponentConfigs{
KubeProxy: &kubeproxyconfig.KubeProxyConfiguration{
BindAddress: "192.168.59.103",
HealthzBindAddress: "0.0.0.0:10256",
MetricsBindAddress: "127.0.0.1:10249",
ClusterCIDR: "192.168.59.0/24",
UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second},
ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
MasqueradeAll: true,
SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
},
IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{
SyncPeriod: metav1.Duration{Duration: 10 * time.Second},
MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second},
},
Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
Max: utilpointer.Int32Ptr(2),
MaxPerCore: utilpointer.Int32Ptr(1),
Min: utilpointer.Int32Ptr(1),
TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
},
},
},
Networking: kubeadm.Networking{
ServiceSubnet: "2001:db8::1/98",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/other/cert/dir",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
}, true},
}
for _, rt := range tests {
actual := ValidateInitConfiguration(rt.s)
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 TestValidateJoinConfiguration(t *testing.T) {
var tests = []struct {
s *kubeadm.JoinConfiguration
expected bool
}{
{&kubeadm.JoinConfiguration{}, false},
{&kubeadm.JoinConfiguration{
CACertPath: "/some/cert.crt",
Discovery: kubeadm.Discovery{
BootstrapToken: &kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456@foobar",
},
File: &kubeadm.FileDiscovery{
KubeConfigPath: "foo",
},
},
}, false},
{&kubeadm.JoinConfiguration{ // Pass without JoinControlPlane
CACertPath: "/some/cert.crt",
Discovery: kubeadm.Discovery{
BootstrapToken: &kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
APIServerEndpoint: "1.2.3.4:6443",
CACertHashes: []string{"aaaa"},
},
TLSBootstrapToken: "abcdef.1234567890123456",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{
Name: "aaa",
CRISocket: "/var/run/dockershim.sock",
},
}, true},
{&kubeadm.JoinConfiguration{ // Pass with JoinControlPlane
CACertPath: "/some/cert.crt",
Discovery: kubeadm.Discovery{
BootstrapToken: &kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
APIServerEndpoint: "1.2.3.4:6443",
CACertHashes: []string{"aaaa"},
},
TLSBootstrapToken: "abcdef.1234567890123456",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{
Name: "aaa",
CRISocket: "/var/run/dockershim.sock",
},
ControlPlane: &kubeadm.JoinControlPlane{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
BindPort: 1234,
},
},
}, true},
{&kubeadm.JoinConfiguration{ // Fail JoinControlPlane.AdvertiseAddress validation
CACertPath: "/some/cert.crt",
Discovery: kubeadm.Discovery{
BootstrapToken: &kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
APIServerEndpoint: "1.2.3.4:6443",
CACertHashes: []string{"aaaa"},
},
TLSBootstrapToken: "abcdef.1234567890123456",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{
Name: "aaa",
CRISocket: "/var/run/dockershim.sock",
},
ControlPlane: &kubeadm.JoinControlPlane{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "aaa",
BindPort: 1234,
},
},
}, false},
{&kubeadm.JoinConfiguration{ // Fail JoinControlPlane.BindPort validation
CACertPath: "/some/cert.crt",
Discovery: kubeadm.Discovery{
BootstrapToken: &kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
APIServerEndpoint: "1.2.3.4:6443",
CACertHashes: []string{"aaaa"},
},
TLSBootstrapToken: "abcdef.1234567890123456",
},
NodeRegistration: kubeadm.NodeRegistrationOptions{
Name: "aaa",
CRISocket: "/var/run/dockershim.sock",
},
ControlPlane: &kubeadm.JoinControlPlane{
LocalAPIEndpoint: kubeadm.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
BindPort: -1,
},
},
}, false},
}
for _, rt := range tests {
actual := ValidateJoinConfiguration(rt.s)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateJoinConfiguration:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateMixedArguments(t *testing.T) {
var tests = []struct {
args []string
expected bool
}{
// Expected to succeed, --config is mixed with skip-* flags only or no other flags
{[]string{"--foo=bar"}, true},
{[]string{"--config=hello"}, true},
{[]string{"--config=hello", "--ignore-preflight-errors=all"}, true},
{[]string{"--config=hello", "--skip-token-print=true"}, true},
{[]string{"--config=hello", "--ignore-preflight-errors=baz", "--skip-token-print"}, true},
// Expected to fail, --config is mixed with the --foo flag
{[]string{"--config=hello", "--ignore-preflight-errors=baz", "--foo=bar"}, false},
{[]string{"--config=hello", "--foo=bar"}, false},
}
var cfgPath string
var ignorePreflightErrors []string
for _, rt := range tests {
f := pflag.NewFlagSet("test", pflag.ContinueOnError)
if f.Parsed() {
t.Error("f.Parse() = true before Parse")
}
f.String("foo", "", "flag bound to config object")
f.StringSliceVar(&ignorePreflightErrors, "ignore-preflight-errors", ignorePreflightErrors, "flag not bound to config object")
f.Bool("skip-token-print", false, "flag not bound to config object")
f.StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
if err := f.Parse(rt.args); err != nil {
t.Fatal(err)
}
actual := ValidateMixedArguments(f)
if (actual == nil) != rt.expected {
t.Errorf(
"failed ValidateMixedArguments:\n\texpected: %t\n\t actual: %t testdata: %v",
rt.expected,
(actual == nil),
rt.args,
)
}
}
}
func TestValidateFeatureGates(t *testing.T) {
type featureFlag map[string]bool
var tests = []struct {
featureGates featureFlag
expected bool
}{
{featureFlag{"Unknown": true}, false},
{featureFlag{"Unknown": false}, false},
}
for _, rt := range tests {
actual := ValidateFeatureGates(rt.featureGates, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed featureGates %v:\n\texpected: %t\n\t actual: %t",
rt.featureGates,
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateIgnorePreflightErrors(t *testing.T) {
var tests = []struct {
ignorePreflightErrors []string
expectedLen int
expectedError bool
}{
{[]string{}, 0, false}, // empty list
{[]string{"check1", "check2"}, 2, false}, // non-duplicate
{[]string{"check1", "check2", "check1"}, 2, false}, // duplicates
{[]string{"check1", "check2", "all"}, 3, true}, // non-duplicate, but 'all' present together wth individual checks
{[]string{"all"}, 1, false}, // skip all checks by using new flag
{[]string{"all"}, 1, false}, // skip all checks by using both old and new flags at the same time
}
for _, rt := range tests {
result, err := ValidateIgnorePreflightErrors(rt.ignorePreflightErrors)
switch {
case err != nil && !rt.expectedError:
t.Errorf("ValidateIgnorePreflightErrors: unexpected error for input (%s), error: %v", rt.ignorePreflightErrors, err)
case err == nil && rt.expectedError:
t.Errorf("ValidateIgnorePreflightErrors: expected error for input (%s) but got: %v", rt.ignorePreflightErrors, result)
case result.Len() != rt.expectedLen:
t.Errorf("ValidateIgnorePreflightErrors: expected Len = %d for input (%s) but got: %v, %v", rt.expectedLen, rt.ignorePreflightErrors, result.Len(), result)
}
}
}
func TestValidateDiscovery(t *testing.T) {
var tests = []struct {
name string
d *kubeadm.Discovery
expected bool
}{
{
"invalid: .BootstrapToken and .File cannot both be set",
&kubeadm.Discovery{
BootstrapToken: &kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
},
File: &kubeadm.FileDiscovery{
KubeConfigPath: "https://url/file.conf",
},
},
false,
},
{
"invalid: .BootstrapToken or .File must be set",
&kubeadm.Discovery{},
false,
},
}
for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) {
err := ValidateDiscovery(rt.d, nil).ToAggregate()
if (err == nil) != rt.expected {
t.Errorf(
"test case failed: ValidateDiscovery:\n\texpected: %t\n\t actual: %t",
rt.expected,
(err == nil),
)
}
})
}
}
func TestValidateDiscoveryBootstrapToken(t *testing.T) {
var tests = []struct {
name string
btd *kubeadm.BootstrapTokenDiscovery
expected bool
}{
{
"invalid: .APIServerEndpoint not set",
&kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
},
false,
},
{
"invalid: using token-based discovery without .BootstrapToken.CACertHashes and .BootstrapToken.UnsafeSkipCAVerification",
&kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
APIServerEndpoint: "192.168.122.100:6443",
UnsafeSkipCAVerification: false,
},
false,
},
{
"valid: using token-based discovery with .BootstrapToken.CACertHashes",
&kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
APIServerEndpoint: "192.168.122.100:6443",
CACertHashes: []string{"sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"},
UnsafeSkipCAVerification: false,
},
true,
},
{
"valid: using token-based discovery with .BootstrapToken.CACertHashe but skip ca verification",
&kubeadm.BootstrapTokenDiscovery{
Token: "abcdef.1234567890123456",
APIServerEndpoint: "192.168.122.100:6443",
CACertHashes: []string{"sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"},
UnsafeSkipCAVerification: true,
},
true,
},
}
for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) {
err := ValidateDiscoveryBootstrapToken(rt.btd, nil).ToAggregate()
if (err == nil) != rt.expected {
t.Errorf(
"test case failed: ValidateDiscoveryBootstrapToken:\n\texpected: %t\n\t actual: %t",
rt.expected,
(err == nil),
)
}
})
}
}
func TestValidateDiscoveryTokenAPIServer(t *testing.T) {
var tests = []struct {
apiServerEndpoint string
expected bool
}{
{
"192.168.122.100",
false,
},
{
"192.168.122.100:6443",
true,
},
}
for _, rt := range tests {
actual := ValidateDiscoveryTokenAPIServer(rt.apiServerEndpoint, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateDiscoveryTokenAPIServer:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateDiscoveryKubeConfigPath(t *testing.T) {
tmpfile, err := ioutil.TempFile("/tmp", "test_discovery_file")
if err != nil {
t.Errorf("Error creating temporary file: %v", err)
}
defer os.Remove(tmpfile.Name())
var tests = []struct {
s string
expected bool
}{
{"foo", false},
{"/foo/bar/file_which_i_believe_not_existing.conf", false},
{tmpfile.Name(), true},
{"http://[::1]a", false},
{"http://url/file.conf", false},
{"https://u r l/file.conf", false},
{"https://url/file.conf", true},
}
for i, rt := range tests {
actual := ValidateDiscoveryKubeConfigPath(rt.s, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"%d: failed ValidateDiscoveryKubeConfigPath:\n\texpected: %t\n\t actual: %t",
i,
rt.expected,
(len(actual) == 0),
)
}
}
}