mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-30 17:58:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			839 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			839 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| 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 staticpod
 | |
| 
 | |
| import (
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"reflect"
 | |
| 	"sort"
 | |
| 	"strconv"
 | |
| 	"testing"
 | |
| 
 | |
| 	v1 "k8s.io/api/core/v1"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 
 | |
| 	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
 | |
| 	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
 | |
| 	testutil "k8s.io/kubernetes/cmd/kubeadm/test"
 | |
| )
 | |
| 
 | |
| func TestComponentResources(t *testing.T) {
 | |
| 	a := ComponentResources("250m")
 | |
| 	if a.Requests == nil {
 | |
| 		t.Errorf(
 | |
| 			"failed componentResources, return value was nil",
 | |
| 		)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetAPIServerProbeAddress(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		desc     string
 | |
| 		endpoint *kubeadmapi.APIEndpoint
 | |
| 		expected string
 | |
| 	}{
 | |
| 		{
 | |
| 			desc:     "nil endpoint returns 127.0.0.1",
 | |
| 			expected: "127.0.0.1",
 | |
| 		},
 | |
| 		{
 | |
| 			desc:     "empty AdvertiseAddress endpoint returns 127.0.0.1",
 | |
| 			endpoint: &kubeadmapi.APIEndpoint{},
 | |
| 			expected: "127.0.0.1",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "filled in AdvertiseAddress endpoint returns it",
 | |
| 			endpoint: &kubeadmapi.APIEndpoint{
 | |
| 				AdvertiseAddress: "10.10.10.10",
 | |
| 			},
 | |
| 			expected: "10.10.10.10",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "filled in ipv6 AdvertiseAddress endpoint returns it",
 | |
| 			endpoint: &kubeadmapi.APIEndpoint{
 | |
| 				AdvertiseAddress: "2001:abcd:bcda::1",
 | |
| 			},
 | |
| 			expected: "2001:abcd:bcda::1",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "filled in 0.0.0.0 AdvertiseAddress endpoint returns empty",
 | |
| 			endpoint: &kubeadmapi.APIEndpoint{
 | |
| 				AdvertiseAddress: "0.0.0.0",
 | |
| 			},
 | |
| 			expected: "",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "filled in :: AdvertiseAddress endpoint returns empty",
 | |
| 			endpoint: &kubeadmapi.APIEndpoint{
 | |
| 				AdvertiseAddress: "::",
 | |
| 			},
 | |
| 			expected: "",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.desc, func(t *testing.T) {
 | |
| 			actual := GetAPIServerProbeAddress(test.endpoint)
 | |
| 			if actual != test.expected {
 | |
| 				t.Errorf("Unexpected result from GetAPIServerProbeAddress:\n\texpected: %s\n\tactual: %s", test.expected, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetControllerManagerProbeAddress(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		desc     string
 | |
| 		cfg      *kubeadmapi.ClusterConfiguration
 | |
| 		expected string
 | |
| 	}{
 | |
| 		{
 | |
| 			desc: "no controller manager extra args leads to 127.0.0.1 being used",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				ControllerManager: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "127.0.0.1",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting controller manager extra address arg to something acknowledges it",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				ControllerManager: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeControllerManagerBindAddressArg: "10.10.10.10",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "10.10.10.10",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting controller manager extra ipv6 address arg to something acknowledges it",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				ControllerManager: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeControllerManagerBindAddressArg: "2001:abcd:bcda::1",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "2001:abcd:bcda::1",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting controller manager extra address arg to 0.0.0.0 returns empty",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				ControllerManager: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeControllerManagerBindAddressArg: "0.0.0.0",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting controller manager extra ipv6 address arg to :: returns empty",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				ControllerManager: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeControllerManagerBindAddressArg: "::",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.desc, func(t *testing.T) {
 | |
| 			actual := GetControllerManagerProbeAddress(test.cfg)
 | |
| 			if actual != test.expected {
 | |
| 				t.Errorf("Unexpected result from GetControllerManagerProbeAddress:\n\texpected: %s\n\tactual: %s", test.expected, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetSchedulerProbeAddress(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		desc     string
 | |
| 		cfg      *kubeadmapi.ClusterConfiguration
 | |
| 		expected string
 | |
| 	}{
 | |
| 		{
 | |
| 			desc: "no scheduler extra args leads to 127.0.0.1 being used",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				Scheduler: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "127.0.0.1",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting scheduler extra address arg to something acknowledges it",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				Scheduler: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeSchedulerBindAddressArg: "10.10.10.10",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "10.10.10.10",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting scheduler extra ipv6 address arg to something acknowledges it",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				Scheduler: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeSchedulerBindAddressArg: "2001:abcd:bcda::1",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "2001:abcd:bcda::1",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting scheduler extra ipv6 address arg to 0.0.0.0 returns empty",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				Scheduler: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeSchedulerBindAddressArg: "0.0.0.0",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "setting scheduler extra ipv6 address arg to :: returns empty",
 | |
| 			cfg: &kubeadmapi.ClusterConfiguration{
 | |
| 				Scheduler: kubeadmapi.ControlPlaneComponent{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						kubeSchedulerBindAddressArg: "::",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			expected: "",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		t.Run(test.desc, func(t *testing.T) {
 | |
| 			actual := GetSchedulerProbeAddress(test.cfg)
 | |
| 			if actual != test.expected {
 | |
| 				t.Errorf("Unexpected result from GetSchedulerProbeAddress:\n\texpected: %s\n\tactual: %s", test.expected, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| func TestGetEtcdProbeEndpoint(t *testing.T) {
 | |
| 	var tests = []struct {
 | |
| 		name             string
 | |
| 		cfg              *kubeadmapi.Etcd
 | |
| 		isIPv6           bool
 | |
| 		expectedHostname string
 | |
| 		expectedPort     int
 | |
| 		expectedScheme   v1.URIScheme
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "etcd probe URL from two URLs",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "https://1.2.3.4:1234,https://4.3.2.1:2381"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           false,
 | |
| 			expectedHostname: "1.2.3.4",
 | |
| 			expectedPort:     1234,
 | |
| 			expectedScheme:   v1.URISchemeHTTPS,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe URL with HTTP scheme",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "http://1.2.3.4:1234"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           false,
 | |
| 			expectedHostname: "1.2.3.4",
 | |
| 			expectedPort:     1234,
 | |
| 			expectedScheme:   v1.URISchemeHTTP,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe URL without scheme should result in defaults",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "1.2.3.4"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           false,
 | |
| 			expectedHostname: "127.0.0.1",
 | |
| 			expectedPort:     kubeadmconstants.EtcdMetricsPort,
 | |
| 			expectedScheme:   v1.URISchemeHTTP,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe URL without port",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "https://1.2.3.4"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           false,
 | |
| 			expectedHostname: "1.2.3.4",
 | |
| 			expectedPort:     kubeadmconstants.EtcdMetricsPort,
 | |
| 			expectedScheme:   v1.URISchemeHTTPS,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe URL from two IPv6 URLs",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "https://[2001:abcd:bcda::1]:1234,https://[2001:abcd:bcda::2]:2381"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           true,
 | |
| 			expectedHostname: "2001:abcd:bcda::1",
 | |
| 			expectedPort:     1234,
 | |
| 			expectedScheme:   v1.URISchemeHTTPS,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe localhost IPv6 URL with HTTP scheme",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "http://[::1]:1234"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           true,
 | |
| 			expectedHostname: "::1",
 | |
| 			expectedPort:     1234,
 | |
| 			expectedScheme:   v1.URISchemeHTTP,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe IPv6 URL with HTTP scheme",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "http://[2001:abcd:bcda::1]:1234"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           true,
 | |
| 			expectedHostname: "2001:abcd:bcda::1",
 | |
| 			expectedPort:     1234,
 | |
| 			expectedScheme:   v1.URISchemeHTTP,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe IPv6 URL without port",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{
 | |
| 					ExtraArgs: map[string]string{
 | |
| 						"listen-metrics-urls": "https://[2001:abcd:bcda::1]"},
 | |
| 				},
 | |
| 			},
 | |
| 			isIPv6:           true,
 | |
| 			expectedHostname: "2001:abcd:bcda::1",
 | |
| 			expectedPort:     kubeadmconstants.EtcdMetricsPort,
 | |
| 			expectedScheme:   v1.URISchemeHTTPS,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe URL from defaults",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{},
 | |
| 			},
 | |
| 			isIPv6:           false,
 | |
| 			expectedHostname: "127.0.0.1",
 | |
| 			expectedPort:     kubeadmconstants.EtcdMetricsPort,
 | |
| 			expectedScheme:   v1.URISchemeHTTP,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "etcd probe URL from defaults if IPv6",
 | |
| 			cfg: &kubeadmapi.Etcd{
 | |
| 				Local: &kubeadmapi.LocalEtcd{},
 | |
| 			},
 | |
| 			isIPv6:           true,
 | |
| 			expectedHostname: "::1",
 | |
| 			expectedPort:     kubeadmconstants.EtcdMetricsPort,
 | |
| 			expectedScheme:   v1.URISchemeHTTP,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, rt := range tests {
 | |
| 		t.Run(rt.name, func(t *testing.T) {
 | |
| 			hostname, port, scheme := GetEtcdProbeEndpoint(rt.cfg, rt.isIPv6)
 | |
| 			if hostname != rt.expectedHostname {
 | |
| 				t.Errorf("%q test case failed:\n\texpected hostname: %s\n\tgot: %s",
 | |
| 					rt.name, rt.expectedHostname, hostname)
 | |
| 			}
 | |
| 			if port != rt.expectedPort {
 | |
| 				t.Errorf("%q test case failed:\n\texpected port: %d\n\tgot: %d",
 | |
| 					rt.name, rt.expectedPort, port)
 | |
| 			}
 | |
| 			if scheme != rt.expectedScheme {
 | |
| 				t.Errorf("%q test case failed:\n\texpected scheme: %v\n\tgot: %v",
 | |
| 					rt.name, rt.expectedScheme, scheme)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestComponentPod(t *testing.T) {
 | |
| 	var tests = []struct {
 | |
| 		name     string
 | |
| 		expected v1.Pod
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "foo",
 | |
| 			expected: v1.Pod{
 | |
| 				TypeMeta: metav1.TypeMeta{
 | |
| 					APIVersion: "v1",
 | |
| 					Kind:       "Pod",
 | |
| 				},
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Name:      "foo",
 | |
| 					Namespace: "kube-system",
 | |
| 					Labels:    map[string]string{"component": "foo", "tier": "control-plane"},
 | |
| 				},
 | |
| 				Spec: v1.PodSpec{
 | |
| 					SecurityContext: &v1.PodSecurityContext{
 | |
| 						SeccompProfile: &v1.SeccompProfile{
 | |
| 							Type: v1.SeccompProfileTypeRuntimeDefault,
 | |
| 						},
 | |
| 					},
 | |
| 					Containers: []v1.Container{
 | |
| 						{
 | |
| 							Name: "foo",
 | |
| 						},
 | |
| 					},
 | |
| 					PriorityClassName: "system-node-critical",
 | |
| 					HostNetwork:       true,
 | |
| 					Volumes:           []v1.Volume{},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, rt := range tests {
 | |
| 		t.Run(rt.name, func(t *testing.T) {
 | |
| 			c := v1.Container{Name: rt.name}
 | |
| 			actual := ComponentPod(c, map[string]v1.Volume{}, nil)
 | |
| 			if !reflect.DeepEqual(rt.expected, actual) {
 | |
| 				t.Errorf(
 | |
| 					"failed componentPod:\n\texpected: %v\n\t  actual: %v",
 | |
| 					rt.expected,
 | |
| 					actual,
 | |
| 				)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestNewVolume(t *testing.T) {
 | |
| 	hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
 | |
| 	var tests = []struct {
 | |
| 		name     string
 | |
| 		path     string
 | |
| 		expected v1.Volume
 | |
| 		pathType *v1.HostPathType
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "foo",
 | |
| 			path: "/etc/foo",
 | |
| 			expected: v1.Volume{
 | |
| 				Name: "foo",
 | |
| 				VolumeSource: v1.VolumeSource{
 | |
| 					HostPath: &v1.HostPathVolumeSource{
 | |
| 						Path: "/etc/foo",
 | |
| 						Type: &hostPathDirectoryOrCreate,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 			pathType: &hostPathDirectoryOrCreate,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, rt := range tests {
 | |
| 		t.Run(rt.name, func(t *testing.T) {
 | |
| 			actual := NewVolume(rt.name, rt.path, rt.pathType)
 | |
| 			if !reflect.DeepEqual(actual, rt.expected) {
 | |
| 				t.Errorf(
 | |
| 					"failed newVolume:\n\texpected: %v\n\t  actual: %v",
 | |
| 					rt.expected,
 | |
| 					actual,
 | |
| 				)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestNewVolumeMount(t *testing.T) {
 | |
| 	var tests = []struct {
 | |
| 		name     string
 | |
| 		path     string
 | |
| 		ro       bool
 | |
| 		expected v1.VolumeMount
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "foo",
 | |
| 			path: "/etc/foo",
 | |
| 			ro:   false,
 | |
| 			expected: v1.VolumeMount{
 | |
| 				Name:      "foo",
 | |
| 				MountPath: "/etc/foo",
 | |
| 				ReadOnly:  false,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "bar",
 | |
| 			path: "/etc/foo/bar",
 | |
| 			ro:   true,
 | |
| 			expected: v1.VolumeMount{
 | |
| 				Name:      "bar",
 | |
| 				MountPath: "/etc/foo/bar",
 | |
| 				ReadOnly:  true,
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, rt := range tests {
 | |
| 		t.Run(rt.name, func(t *testing.T) {
 | |
| 			actual := NewVolumeMount(rt.name, rt.path, rt.ro)
 | |
| 			if !reflect.DeepEqual(actual, rt.expected) {
 | |
| 				t.Errorf(
 | |
| 					"failed newVolumeMount:\n\texpected: %v\n\t  actual: %v",
 | |
| 					rt.expected,
 | |
| 					actual,
 | |
| 				)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| func TestVolumeMapToSlice(t *testing.T) {
 | |
| 	testVolumes := map[string]v1.Volume{
 | |
| 		"foo": {
 | |
| 			Name: "foo",
 | |
| 		},
 | |
| 		"bar": {
 | |
| 			Name: "bar",
 | |
| 		},
 | |
| 	}
 | |
| 	volumeSlice := VolumeMapToSlice(testVolumes)
 | |
| 	if len(volumeSlice) != 2 {
 | |
| 		t.Errorf("Expected slice length of 1, got %d", len(volumeSlice))
 | |
| 	}
 | |
| 	if volumeSlice[0].Name != "bar" {
 | |
| 		t.Errorf("Expected first volume name \"bar\", got %s", volumeSlice[0].Name)
 | |
| 	}
 | |
| 	if volumeSlice[1].Name != "foo" {
 | |
| 		t.Errorf("Expected second volume name \"foo\", got %s", volumeSlice[1].Name)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestVolumeMountMapToSlice(t *testing.T) {
 | |
| 	testVolumeMounts := map[string]v1.VolumeMount{
 | |
| 		"foo": {
 | |
| 			Name: "foo",
 | |
| 		},
 | |
| 		"bar": {
 | |
| 			Name: "bar",
 | |
| 		},
 | |
| 	}
 | |
| 	volumeMountSlice := VolumeMountMapToSlice(testVolumeMounts)
 | |
| 	if len(volumeMountSlice) != 2 {
 | |
| 		t.Errorf("Expected slice length of 1, got %d", len(volumeMountSlice))
 | |
| 	}
 | |
| 	if volumeMountSlice[0].Name != "bar" {
 | |
| 		t.Errorf("Expected first volume mount name \"bar\", got %s", volumeMountSlice[0].Name)
 | |
| 	}
 | |
| 	if volumeMountSlice[1].Name != "foo" {
 | |
| 		t.Errorf("Expected second volume name \"foo\", got %s", volumeMountSlice[1].Name)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetExtraParameters(t *testing.T) {
 | |
| 	var tests = []struct {
 | |
| 		name      string
 | |
| 		overrides map[string]string
 | |
| 		defaults  map[string]string
 | |
| 		expected  []string
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "with admission-control default NamespaceLifecycle",
 | |
| 			overrides: map[string]string{
 | |
| 				"admission-control": "NamespaceLifecycle,LimitRanger",
 | |
| 			},
 | |
| 			defaults: map[string]string{
 | |
| 				"admission-control": "NamespaceLifecycle",
 | |
| 				"allow-privileged":  "true",
 | |
| 			},
 | |
| 			expected: []string{
 | |
| 				"--admission-control=NamespaceLifecycle,LimitRanger",
 | |
| 				"--allow-privileged=true",
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "without admission-control default",
 | |
| 			overrides: map[string]string{
 | |
| 				"admission-control": "NamespaceLifecycle,LimitRanger",
 | |
| 			},
 | |
| 			defaults: map[string]string{
 | |
| 				"allow-privileged": "true",
 | |
| 			},
 | |
| 			expected: []string{
 | |
| 				"--admission-control=NamespaceLifecycle,LimitRanger",
 | |
| 				"--allow-privileged=true",
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, rt := range tests {
 | |
| 		t.Run(rt.name, func(t *testing.T) {
 | |
| 			actual := GetExtraParameters(rt.overrides, rt.defaults)
 | |
| 			sort.Strings(actual)
 | |
| 			sort.Strings(rt.expected)
 | |
| 			if !reflect.DeepEqual(actual, rt.expected) {
 | |
| 				t.Errorf("failed getExtraParameters:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	validPod = `
 | |
| apiVersion: v1
 | |
| kind: Pod
 | |
| metadata:
 | |
|   labels:
 | |
|     component: etcd
 | |
|     tier: control-plane
 | |
|   name: etcd
 | |
|   namespace: kube-system
 | |
| spec:
 | |
|   containers:
 | |
|   - image: gcr.io/google_containers/etcd-amd64:3.1.11
 | |
| status: {}
 | |
| `
 | |
| 	invalidPod = `---{ broken yaml @@@`
 | |
| )
 | |
| 
 | |
| func TestReadStaticPodFromDisk(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		description   string
 | |
| 		podYaml       string
 | |
| 		expectErr     bool
 | |
| 		writeManifest bool
 | |
| 	}{
 | |
| 		{
 | |
| 			description:   "valid pod is marshaled",
 | |
| 			podYaml:       validPod,
 | |
| 			writeManifest: true,
 | |
| 			expectErr:     false,
 | |
| 		},
 | |
| 		{
 | |
| 			description:   "invalid pod fails to unmarshal",
 | |
| 			podYaml:       invalidPod,
 | |
| 			writeManifest: true,
 | |
| 			expectErr:     true,
 | |
| 		},
 | |
| 		{
 | |
| 			description:   "non-existent file returns error",
 | |
| 			podYaml:       ``,
 | |
| 			writeManifest: false,
 | |
| 			expectErr:     true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, rt := range tests {
 | |
| 		t.Run(rt.description, func(t *testing.T) {
 | |
| 			tmpdir := testutil.SetupTempDir(t)
 | |
| 			defer os.RemoveAll(tmpdir)
 | |
| 
 | |
| 			manifestPath := filepath.Join(tmpdir, "pod.yaml")
 | |
| 			if rt.writeManifest {
 | |
| 				err := os.WriteFile(manifestPath, []byte(rt.podYaml), 0644)
 | |
| 				if err != nil {
 | |
| 					t.Fatalf("Failed to write pod manifest\n%s\n\tfatal error: %v", rt.description, err)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			_, actualErr := ReadStaticPodFromDisk(manifestPath)
 | |
| 			if (actualErr != nil) != rt.expectErr {
 | |
| 				t.Errorf(
 | |
| 					"ReadStaticPodFromDisk failed\n%s\n\texpected error: %t\n\tgot: %t\n\tactual error: %v",
 | |
| 					rt.description,
 | |
| 					rt.expectErr,
 | |
| 					(actualErr != nil),
 | |
| 					actualErr,
 | |
| 				)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestManifestFilesAreEqual(t *testing.T) {
 | |
| 	var tests = []struct {
 | |
| 		description    string
 | |
| 		podYamls       []string
 | |
| 		expectedResult bool
 | |
| 		expectErr      bool
 | |
| 	}{
 | |
| 		{
 | |
| 			description:    "manifests are equal",
 | |
| 			podYamls:       []string{validPod, validPod},
 | |
| 			expectedResult: true,
 | |
| 			expectErr:      false,
 | |
| 		},
 | |
| 		{
 | |
| 			description:    "manifests are not equal",
 | |
| 			podYamls:       []string{validPod, validPod + "\n"},
 | |
| 			expectedResult: false,
 | |
| 			expectErr:      false,
 | |
| 		},
 | |
| 		{
 | |
| 			description:    "first manifest doesn't exist",
 | |
| 			podYamls:       []string{validPod, ""},
 | |
| 			expectedResult: false,
 | |
| 			expectErr:      true,
 | |
| 		},
 | |
| 		{
 | |
| 			description:    "second manifest doesn't exist",
 | |
| 			podYamls:       []string{"", validPod},
 | |
| 			expectedResult: false,
 | |
| 			expectErr:      true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, rt := range tests {
 | |
| 		t.Run(rt.description, func(t *testing.T) {
 | |
| 			tmpdir := testutil.SetupTempDir(t)
 | |
| 			defer os.RemoveAll(tmpdir)
 | |
| 
 | |
| 			// write 2 manifests
 | |
| 			for i := 0; i < 2; i++ {
 | |
| 				if rt.podYamls[i] != "" {
 | |
| 					manifestPath := filepath.Join(tmpdir, strconv.Itoa(i)+".yaml")
 | |
| 					err := os.WriteFile(manifestPath, []byte(rt.podYamls[i]), 0644)
 | |
| 					if err != nil {
 | |
| 						t.Fatalf("Failed to write manifest file\n%s\n\tfatal error: %v", rt.description, err)
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// compare them
 | |
| 			result, actualErr := ManifestFilesAreEqual(filepath.Join(tmpdir, "0.yaml"), filepath.Join(tmpdir, "1.yaml"))
 | |
| 			if result != rt.expectedResult {
 | |
| 				t.Errorf(
 | |
| 					"ManifestFilesAreEqual failed\n%s\nexpected result: %t\nactual result: %t",
 | |
| 					rt.description,
 | |
| 					rt.expectedResult,
 | |
| 					result,
 | |
| 				)
 | |
| 			}
 | |
| 			if (actualErr != nil) != rt.expectErr {
 | |
| 				t.Errorf(
 | |
| 					"ManifestFilesAreEqual failed\n%s\n\texpected error: %t\n\tgot: %t\n\tactual error: %v",
 | |
| 					rt.description,
 | |
| 					rt.expectErr,
 | |
| 					(actualErr != nil),
 | |
| 					actualErr,
 | |
| 				)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestPatchStaticPod(t *testing.T) {
 | |
| 	type file struct {
 | |
| 		name string
 | |
| 		data string
 | |
| 	}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		name          string
 | |
| 		files         []*file
 | |
| 		pod           *v1.Pod
 | |
| 		expectedPod   *v1.Pod
 | |
| 		expectedError bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "valid: patch a kube-apiserver target using a couple of ordered patches",
 | |
| 			pod: &v1.Pod{
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Name:      "kube-apiserver",
 | |
| 					Namespace: "foo",
 | |
| 				},
 | |
| 			},
 | |
| 			expectedPod: &v1.Pod{
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Name:      "kube-apiserver",
 | |
| 					Namespace: "bar2",
 | |
| 				},
 | |
| 			},
 | |
| 			files: []*file{
 | |
| 				{
 | |
| 					name: "kube-apiserver1+merge.json",
 | |
| 					data: `{"metadata":{"namespace":"bar2"}}`,
 | |
| 				},
 | |
| 				{
 | |
| 					name: "kube-apiserver0+json.json",
 | |
| 					data: `[{"op": "replace", "path": "/metadata/namespace", "value": "bar1"}]`,
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "invalid: unknown patch target name",
 | |
| 			pod: &v1.Pod{
 | |
| 				ObjectMeta: metav1.ObjectMeta{
 | |
| 					Name:      "foo",
 | |
| 					Namespace: "bar",
 | |
| 				},
 | |
| 			},
 | |
| 			expectedError: true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tc := range tests {
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			tempDir, err := os.MkdirTemp("", "patch-files")
 | |
| 			if err != nil {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 			defer os.RemoveAll(tempDir)
 | |
| 
 | |
| 			for _, file := range tc.files {
 | |
| 				filePath := filepath.Join(tempDir, file.name)
 | |
| 				err := os.WriteFile(filePath, []byte(file.data), 0644)
 | |
| 				if err != nil {
 | |
| 					t.Fatalf("could not write temporary file %q", filePath)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			pod, err := PatchStaticPod(tc.pod, tempDir, io.Discard)
 | |
| 			if (err != nil) != tc.expectedError {
 | |
| 				t.Fatalf("expected error: %v, got: %v, error: %v", tc.expectedError, (err != nil), err)
 | |
| 			}
 | |
| 			if err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			if tc.expectedPod.String() != pod.String() {
 | |
| 				t.Fatalf("expected object:\n%s\ngot:\n%s", tc.expectedPod.String(), pod.String())
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
