diff --git a/cmd/kubeadm/app/phases/selfhosting/podspec_mutation_test.go b/cmd/kubeadm/app/phases/selfhosting/podspec_mutation_test.go index 9b2ce07b38b..60dacbfab83 100644 --- a/cmd/kubeadm/app/phases/selfhosting/podspec_mutation_test.go +++ b/cmd/kubeadm/app/phases/selfhosting/podspec_mutation_test.go @@ -18,10 +18,10 @@ package selfhosting import ( "reflect" + "sort" "testing" "k8s.io/api/core/v1" - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" ) @@ -72,9 +72,8 @@ func TestMutatePodSpec(t *testing.T) { }, } - cfg := &kubeadmapi.MasterConfiguration{} for _, rt := range tests { - mutatePodSpec(cfg, rt.component, rt.podSpec) + mutatePodSpec(getDefaultMutators(), rt.component, rt.podSpec) if !reflect.DeepEqual(*rt.podSpec, rt.expected) { t.Errorf("failed mutatePodSpec:\nexpected:\n%v\nsaw:\n%v", rt.expected, *rt.podSpec) @@ -110,9 +109,8 @@ func TestAddNodeSelectorToPodSpec(t *testing.T) { }, } - cfg := &kubeadmapi.MasterConfiguration{} for _, rt := range tests { - addNodeSelectorToPodSpec(cfg, rt.podSpec) + addNodeSelectorToPodSpec(rt.podSpec) if !reflect.DeepEqual(*rt.podSpec, rt.expected) { t.Errorf("failed addNodeSelectorToPodSpec:\nexpected:\n%v\nsaw:\n%v", rt.expected, *rt.podSpec) @@ -148,9 +146,8 @@ func TestSetMasterTolerationOnPodSpec(t *testing.T) { }, } - cfg := &kubeadmapi.MasterConfiguration{} for _, rt := range tests { - setMasterTolerationOnPodSpec(cfg, rt.podSpec) + setMasterTolerationOnPodSpec(rt.podSpec) if !reflect.DeepEqual(*rt.podSpec, rt.expected) { t.Errorf("failed setMasterTolerationOnPodSpec:\nexpected:\n%v\nsaw:\n%v", rt.expected, *rt.podSpec) @@ -179,12 +176,281 @@ func TestSetRightDNSPolicyOnPodSpec(t *testing.T) { }, } - cfg := &kubeadmapi.MasterConfiguration{} for _, rt := range tests { - setRightDNSPolicyOnPodSpec(cfg, rt.podSpec) + setRightDNSPolicyOnPodSpec(rt.podSpec) if !reflect.DeepEqual(*rt.podSpec, rt.expected) { t.Errorf("failed setRightDNSPolicyOnPodSpec:\nexpected:\n%v\nsaw:\n%v", rt.expected, *rt.podSpec) } } } + +func TestSetSelfHostedVolumesForAPIServer(t *testing.T) { + var tests = []struct { + podSpec *v1.PodSpec + expected v1.PodSpec + }{ + { + podSpec: &v1.PodSpec{ + Containers: []v1.Container{ + { + VolumeMounts: []v1.VolumeMount{ + { + Name: "ca-certs", + MountPath: "/etc/ssl/certs", + }, + { + Name: "k8s-certs", + MountPath: "/etc/kubernetes/pki", + }, + }, + Command: []string{ + "--foo=bar", + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "ca-certs", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/ssl/certs", + }, + }, + }, + { + Name: "k8s-certs", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/kubernetes/pki", + }, + }, + }, + }, + }, + expected: v1.PodSpec{ + Containers: []v1.Container{ + { + VolumeMounts: []v1.VolumeMount{ + { + Name: "ca-certs", + MountPath: "/etc/ssl/certs", + }, + { + Name: "k8s-certs", + MountPath: "/etc/kubernetes/pki", + }, + }, + Command: []string{ + "--foo=bar", + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "ca-certs", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/ssl/certs", + }, + }, + }, + { + Name: "k8s-certs", + VolumeSource: apiServerCertificatesVolumeSource(), + }, + }, + }, + }, + } + + for _, rt := range tests { + setSelfHostedVolumesForAPIServer(rt.podSpec) + sort.Strings(rt.podSpec.Containers[0].Command) + sort.Strings(rt.expected.Containers[0].Command) + + if !reflect.DeepEqual(*rt.podSpec, rt.expected) { + t.Errorf("failed setSelfHostedVolumesForAPIServer:\nexpected:\n%v\nsaw:\n%v", rt.expected, *rt.podSpec) + } + } +} + +func TestSetSelfHostedVolumesForControllerManager(t *testing.T) { + var tests = []struct { + podSpec *v1.PodSpec + expected v1.PodSpec + }{ + { + podSpec: &v1.PodSpec{ + Containers: []v1.Container{ + { + VolumeMounts: []v1.VolumeMount{ + { + Name: "ca-certs", + MountPath: "/etc/ssl/certs", + }, + { + Name: "k8s-certs", + MountPath: "/etc/kubernetes/pki", + }, + { + Name: "kubeconfig", + MountPath: "/etc/kubernetes/controller-manager.conf", + }, + }, + Command: []string{ + "--kubeconfig=/etc/kubernetes/controller-manager.conf", + "--foo=bar", + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "ca-certs", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/ssl/certs", + }, + }, + }, + { + Name: "k8s-certs", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/kubernetes/pki", + }, + }, + }, + { + Name: "kubeconfig", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/kubernetes/controller-manager.conf", + }, + }, + }, + }, + }, + expected: v1.PodSpec{ + Containers: []v1.Container{ + { + VolumeMounts: []v1.VolumeMount{ + { + Name: "ca-certs", + MountPath: "/etc/ssl/certs", + }, + { + Name: "k8s-certs", + MountPath: "/etc/kubernetes/pki", + }, + { + Name: "kubeconfig", + MountPath: "/etc/kubernetes/kubeconfig", + }, + }, + Command: []string{ + "--kubeconfig=/etc/kubernetes/kubeconfig/controller-manager.conf", + "--foo=bar", + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "ca-certs", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/ssl/certs", + }, + }, + }, + { + Name: "k8s-certs", + VolumeSource: controllerManagerCertificatesVolumeSource(), + }, + { + Name: "kubeconfig", + VolumeSource: kubeConfigVolumeSource(kubeadmconstants.ControllerManagerKubeConfigFileName), + }, + }, + }, + }, + } + + for _, rt := range tests { + setSelfHostedVolumesForControllerManager(rt.podSpec) + sort.Strings(rt.podSpec.Containers[0].Command) + sort.Strings(rt.expected.Containers[0].Command) + + if !reflect.DeepEqual(*rt.podSpec, rt.expected) { + t.Errorf("failed setSelfHostedVolumesForControllerManager:\nexpected:\n%v\nsaw:\n%v", rt.expected, *rt.podSpec) + } + } +} + +func TestSetSelfHostedVolumesForScheduler(t *testing.T) { + var tests = []struct { + podSpec *v1.PodSpec + expected v1.PodSpec + }{ + { + podSpec: &v1.PodSpec{ + Containers: []v1.Container{ + { + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubeconfig", + MountPath: "/etc/kubernetes/scheduler.conf", + }, + }, + Command: []string{ + "--kubeconfig=/etc/kubernetes/scheduler.conf", + "--foo=bar", + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubeconfig", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/etc/kubernetes/scheduler.conf", + }, + }, + }, + }, + }, + expected: v1.PodSpec{ + Containers: []v1.Container{ + { + VolumeMounts: []v1.VolumeMount{ + { + Name: "kubeconfig", + MountPath: "/etc/kubernetes/kubeconfig", + }, + }, + Command: []string{ + "--kubeconfig=/etc/kubernetes/kubeconfig/scheduler.conf", + "--foo=bar", + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "kubeconfig", + VolumeSource: kubeConfigVolumeSource(kubeadmconstants.SchedulerKubeConfigFileName), + }, + }, + }, + }, + } + + for _, rt := range tests { + setSelfHostedVolumesForScheduler(rt.podSpec) + sort.Strings(rt.podSpec.Containers[0].Command) + sort.Strings(rt.expected.Containers[0].Command) + + if !reflect.DeepEqual(*rt.podSpec, rt.expected) { + t.Errorf("failed setSelfHostedVolumesForScheduler:\nexpected:\n%v\nsaw:\n%v", rt.expected, *rt.podSpec) + } + } +} diff --git a/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go b/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go index 0a1afc69b7f..aa73f012731 100644 --- a/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go +++ b/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go @@ -21,88 +21,13 @@ import ( "fmt" "io/ioutil" "os" - "strings" "testing" "github.com/ghodss/yaml" - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - "k8s.io/kubernetes/cmd/kubeadm/app/cmd/features" - kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/constants" ) const ( - apiProjectedSecret = `- name: k8s - projected: - sources: - - secret: - items: - - key: tls.crt - path: ca.crt - - key: tls.key - path: ca.key - name: ca - - secret: - items: - - key: tls.crt - path: apiserver.crt - - key: tls.key - path: apiserver.key - name: apiserver - - secret: - items: - - key: tls.crt - path: apiserver-kubelet-client.crt - - key: tls.key - path: apiserver-kubelet-client.key - name: apiserver-kubelet-client - - secret: - items: - - key: tls.crt - path: sa.pub - - key: tls.key - path: sa.key - name: sa - - secret: - items: - - key: tls.crt - path: front-proxy-ca.crt - name: front-proxy-ca - - secret: - items: - - key: tls.crt - path: front-proxy-client.crt - - key: tls.key - path: front-proxy-client.key - name: front-proxy-client` - - controllerManagerProjectedSecret = `- name: k8s - projected: - sources: - - secret: - name: controller-manager.conf - - secret: - items: - - key: tls.crt - path: ca.crt - - key: tls.key - path: ca.key - name: ca - - secret: - items: - - key: tls.key - path: sa.key - name: sa` - - schedulerProjectedSecret = `- name: k8s - projected: - sources: - - secret: - name: scheduler.conf` - - hostPathVol = `- hostPath: - path: /etc/kubernetes - name: k8s` - testAPIServerPod = ` apiVersion: v1 kind: Pod @@ -110,39 +35,36 @@ metadata: annotations: scheduler.alpha.kubernetes.io/critical-pod: "" creationTimestamp: null - labels: - component: kube-apiserver - tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - - --client-ca-file=/etc/kubernetes/pki/ca.crt - - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key - - --allow-privileged=true - - --service-cluster-ip-range=10.96.0.0/12 - --service-account-key-file=/etc/kubernetes/pki/sa.pub + - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key + - --secure-port=6443 + - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt + - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname + - --requestheader-group-headers=X-Remote-Group + - --service-cluster-ip-range=10.96.0.0/12 - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - - --secure-port=6443 - - --insecure-port=0 - - --admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota - - --requestheader-extra-headers-prefix=X-Remote-Extra- - - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt - - --experimental-bootstrap-token-auth=true - - --requestheader-group-headers=X-Remote-Group - - --requestheader-allowed-names=front-proxy-client - - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key + - --advertise-address=192.168.1.115 - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname + - --insecure-port=0 + - --experimental-bootstrap-token-auth=true - --requestheader-username-headers=X-Remote-User + - --requestheader-extra-headers-prefix=X-Remote-Extra- + - --requestheader-allowed-names=front-proxy-client + - --admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota + - --allow-privileged=true + - --client-ca-file=/etc/kubernetes/pki/ca.crt + - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key + - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - --authorization-mode=Node,RBAC - - --advertise-address=192.168.200.101 - --etcd-servers=http://127.0.0.1:2379 - image: gcr.io/google_containers/kube-apiserver-amd64:v1.7.0 + image: gcr.io/google_containers/kube-apiserver-amd64:v1.7.4 livenessProbe: failureThreshold: 8 httpGet: @@ -157,22 +79,26 @@ spec: requests: cpu: 250m volumeMounts: - - mountPath: /etc/kubernetes - name: k8s + - mountPath: /etc/kubernetes/pki + name: k8s-certs readOnly: true - mountPath: /etc/ssl/certs - name: certs + name: ca-certs + readOnly: true - mountPath: /etc/pki - name: pki + name: ca-certs-etc-pki + readOnly: true hostNetwork: true volumes: - %s + - hostPath: + path: /etc/kubernetes/pki + name: k8s-certs - hostPath: path: /etc/ssl/certs - name: certs + name: ca-certs - hostPath: path: /etc/pki - name: pki + name: ca-certs-etc-pki status: {} ` @@ -192,30 +118,30 @@ spec: containers: - command: - kube-apiserver - - --client-ca-file=/etc/kubernetes/pki/ca.crt - - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key - - --allow-privileged=true - - --service-cluster-ip-range=10.96.0.0/12 - --service-account-key-file=/etc/kubernetes/pki/sa.pub + - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key + - --secure-port=6443 + - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt + - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname + - --requestheader-group-headers=X-Remote-Group + - --service-cluster-ip-range=10.96.0.0/12 - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - - --secure-port=6443 - - --insecure-port=0 - - --admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota - - --requestheader-extra-headers-prefix=X-Remote-Extra- - - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt - - --experimental-bootstrap-token-auth=true - - --requestheader-group-headers=X-Remote-Group - - --requestheader-allowed-names=front-proxy-client - - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key + - --advertise-address=192.168.1.115 - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname + - --insecure-port=0 + - --experimental-bootstrap-token-auth=true - --requestheader-username-headers=X-Remote-User + - --requestheader-extra-headers-prefix=X-Remote-Extra- + - --requestheader-allowed-names=front-proxy-client + - --admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota + - --allow-privileged=true + - --client-ca-file=/etc/kubernetes/pki/ca.crt + - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key + - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - --authorization-mode=Node,RBAC - - --advertise-address=192.168.200.101 - --etcd-servers=http://127.0.0.1:2379 - image: gcr.io/google_containers/kube-apiserver-amd64:v1.7.0 + image: gcr.io/google_containers/kube-apiserver-amd64:v1.7.4 livenessProbe: failureThreshold: 8 httpGet: @@ -230,13 +156,15 @@ spec: requests: cpu: 250m volumeMounts: - - mountPath: /etc/kubernetes - name: k8s + - mountPath: /etc/kubernetes/pki + name: k8s-certs readOnly: true - mountPath: /etc/ssl/certs - name: certs + name: ca-certs + readOnly: true - mountPath: /etc/pki - name: pki + name: ca-certs-etc-pki + readOnly: true dnsPolicy: ClusterFirstWithHostNet hostNetwork: true nodeSelector: @@ -245,13 +173,15 @@ spec: - effect: NoSchedule key: node-role.kubernetes.io/master volumes: - %s + - hostPath: + path: /etc/kubernetes/pki + name: k8s-certs - hostPath: path: /etc/ssl/certs - name: certs + name: ca-certs - hostPath: path: /etc/pki - name: pki + name: ca-certs-etc-pki updateStrategy: {} status: currentNumberScheduled: 0 @@ -267,25 +197,22 @@ metadata: annotations: scheduler.alpha.kubernetes.io/critical-pod: "" creationTimestamp: null - labels: - component: kube-controller-manager - tier: control-plane name: kube-controller-manager namespace: kube-system spec: containers: - command: - kube-controller-manager + - --leader-elect=true + - --controllers=*,bootstrapsigner,tokencleaner + - --kubeconfig=/etc/kubernetes/controller-manager.conf + - --root-ca-file=/etc/kubernetes/pki/ca.crt - --service-account-private-key-file=/etc/kubernetes/pki/sa.key - --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt - --cluster-signing-key-file=/etc/kubernetes/pki/ca.key - - --leader-elect=true - - --kubeconfig=/etc/kubernetes/controller-manager.conf - - --controllers=*,bootstrapsigner,tokencleaner - - --root-ca-file=/etc/kubernetes/pki/ca.crt - --address=127.0.0.1 - --use-service-account-credentials=true - image: gcr.io/google_containers/kube-controller-manager-amd64:v1.7.0 + image: gcr.io/google_containers/kube-controller-manager-amd64:v1.7.4 livenessProbe: failureThreshold: 8 httpGet: @@ -300,22 +227,32 @@ spec: requests: cpu: 200m volumeMounts: - - mountPath: /etc/kubernetes - name: k8s + - mountPath: /etc/kubernetes/pki + name: k8s-certs readOnly: true - mountPath: /etc/ssl/certs - name: certs + name: ca-certs + readOnly: true + - mountPath: /etc/kubernetes/controller-manager.conf + name: kubeconfig + readOnly: true - mountPath: /etc/pki - name: pki + name: ca-certs-etc-pki + readOnly: true hostNetwork: true volumes: - %s + - hostPath: + path: /etc/kubernetes/pki + name: k8s-certs - hostPath: path: /etc/ssl/certs - name: certs + name: ca-certs + - hostPath: + path: /etc/kubernetes/controller-manager.conf + name: kubeconfig - hostPath: path: /etc/pki - name: pki + name: ca-certs-etc-pki status: {} ` @@ -335,16 +272,16 @@ spec: containers: - command: - kube-controller-manager + - --leader-elect=true + - --controllers=*,bootstrapsigner,tokencleaner + - --kubeconfig=/etc/kubernetes/controller-manager.conf + - --root-ca-file=/etc/kubernetes/pki/ca.crt - --service-account-private-key-file=/etc/kubernetes/pki/sa.key - --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt - --cluster-signing-key-file=/etc/kubernetes/pki/ca.key - - --leader-elect=true - - --kubeconfig=/etc/kubernetes/controller-manager.conf - - --controllers=*,bootstrapsigner,tokencleaner - - --root-ca-file=/etc/kubernetes/pki/ca.crt - --address=127.0.0.1 - --use-service-account-credentials=true - image: gcr.io/google_containers/kube-controller-manager-amd64:v1.7.0 + image: gcr.io/google_containers/kube-controller-manager-amd64:v1.7.4 livenessProbe: failureThreshold: 8 httpGet: @@ -359,13 +296,18 @@ spec: requests: cpu: 200m volumeMounts: - - mountPath: /etc/kubernetes - name: k8s + - mountPath: /etc/kubernetes/pki + name: k8s-certs readOnly: true - mountPath: /etc/ssl/certs - name: certs + name: ca-certs + readOnly: true + - mountPath: /etc/kubernetes/controller-manager.conf + name: kubeconfig + readOnly: true - mountPath: /etc/pki - name: pki + name: ca-certs-etc-pki + readOnly: true dnsPolicy: ClusterFirstWithHostNet hostNetwork: true nodeSelector: @@ -374,13 +316,18 @@ spec: - effect: NoSchedule key: node-role.kubernetes.io/master volumes: - %s + - hostPath: + path: /etc/kubernetes/pki + name: k8s-certs - hostPath: path: /etc/ssl/certs - name: certs + name: ca-certs + - hostPath: + path: /etc/kubernetes/controller-manager.conf + name: kubeconfig - hostPath: path: /etc/pki - name: pki + name: ca-certs-etc-pki updateStrategy: {} status: currentNumberScheduled: 0 @@ -396,19 +343,16 @@ metadata: annotations: scheduler.alpha.kubernetes.io/critical-pod: "" creationTimestamp: null - labels: - component: kube-scheduler - tier: control-plane name: kube-scheduler namespace: kube-system spec: containers: - command: - kube-scheduler - - --address=127.0.0.1 - --leader-elect=true - --kubeconfig=/etc/kubernetes/scheduler.conf - image: gcr.io/google_containers/kube-scheduler-amd64:v1.7.0 + - --address=127.0.0.1 + image: gcr.io/google_containers/kube-scheduler-amd64:v1.7.4 livenessProbe: failureThreshold: 8 httpGet: @@ -423,12 +367,14 @@ spec: requests: cpu: 100m volumeMounts: - - mountPath: /etc/kubernetes - name: k8s + - mountPath: /etc/kubernetes/scheduler.conf + name: kubeconfig readOnly: true hostNetwork: true volumes: - %s + - hostPath: + path: /etc/kubernetes/scheduler.conf + name: kubeconfig status: {} ` @@ -448,10 +394,10 @@ spec: containers: - command: - kube-scheduler - - --address=127.0.0.1 - --leader-elect=true - --kubeconfig=/etc/kubernetes/scheduler.conf - image: gcr.io/google_containers/kube-scheduler-amd64:v1.7.0 + - --address=127.0.0.1 + image: gcr.io/google_containers/kube-scheduler-amd64:v1.7.4 livenessProbe: failureThreshold: 8 httpGet: @@ -466,8 +412,8 @@ spec: requests: cpu: 100m volumeMounts: - - mountPath: /etc/kubernetes - name: k8s + - mountPath: /etc/kubernetes/scheduler.conf + name: kubeconfig readOnly: true dnsPolicy: ClusterFirstWithHostNet hostNetwork: true @@ -477,7 +423,9 @@ spec: - effect: NoSchedule key: node-role.kubernetes.io/master volumes: - %s + - hostPath: + path: /etc/kubernetes/scheduler.conf + name: kubeconfig updateStrategy: {} status: currentNumberScheduled: 0 @@ -487,67 +435,26 @@ status: ` ) -var ( - testAPIServerSecretsPod = fmt.Sprintf(testAPIServerPod, apiProjectedSecret) - testAPIServerSecretsDS = fmt.Sprintf(testAPIServerDaemonSet, indentString(apiProjectedSecret, 4)) - testAPIServerHostPathPod = fmt.Sprintf(testAPIServerPod, hostPathVol) - testAPIServerHostPathDS = fmt.Sprintf(testAPIServerDaemonSet, indentString(hostPathVol, 4)) - - testSchedulerSecretsPod = fmt.Sprintf(testSchedulerPod, schedulerProjectedSecret) - testSchedulerSecretsDS = fmt.Sprintf(testSchedulerDaemonSet, indentString(schedulerProjectedSecret, 4)) - testSchedulerHostPathPod = fmt.Sprintf(testSchedulerPod, hostPathVol) - testSchedulerHostPathDS = fmt.Sprintf(testSchedulerDaemonSet, indentString(hostPathVol, 4)) - - testControllerManagerSecretsPod = fmt.Sprintf(testControllerManagerPod, controllerManagerProjectedSecret) - testControllerManagerSecretsDS = fmt.Sprintf(testControllerManagerDaemonSet, indentString(controllerManagerProjectedSecret, 4)) - testControllerManagerHostPathPod = fmt.Sprintf(testControllerManagerPod, hostPathVol) - testControllerManagerHostPathDS = fmt.Sprintf(testControllerManagerDaemonSet, indentString(hostPathVol, 4)) -) - func TestBuildDaemonSet(t *testing.T) { var tests = []struct { - component string - podBytes []byte - dsBytes []byte - selfHostedSecrets bool + component string + podBytes []byte + dsBytes []byte }{ - // vols as secrets { - component: kubeadmconstants.KubeAPIServer, - podBytes: []byte(testAPIServerSecretsPod), - dsBytes: []byte(testAPIServerSecretsDS), - selfHostedSecrets: true, + component: constants.KubeAPIServer, + podBytes: []byte(testAPIServerPod), + dsBytes: []byte(testAPIServerDaemonSet), }, { - component: kubeadmconstants.KubeControllerManager, - podBytes: []byte(testControllerManagerSecretsPod), - dsBytes: []byte(testControllerManagerSecretsDS), - selfHostedSecrets: true, + component: constants.KubeControllerManager, + podBytes: []byte(testControllerManagerPod), + dsBytes: []byte(testControllerManagerDaemonSet), }, { - component: kubeadmconstants.KubeScheduler, - podBytes: []byte(testSchedulerSecretsPod), - dsBytes: []byte(testSchedulerSecretsDS), - selfHostedSecrets: true, - }, - // hostPath vols - { - component: kubeadmconstants.KubeAPIServer, - podBytes: []byte(testAPIServerHostPathPod), - dsBytes: []byte(testAPIServerHostPathDS), - selfHostedSecrets: false, - }, - { - component: kubeadmconstants.KubeControllerManager, - podBytes: []byte(testControllerManagerHostPathPod), - dsBytes: []byte(testControllerManagerHostPathDS), - selfHostedSecrets: false, - }, - { - component: kubeadmconstants.KubeScheduler, - podBytes: []byte(testSchedulerHostPathPod), - dsBytes: []byte(testSchedulerHostPathDS), - selfHostedSecrets: false, + component: constants.KubeScheduler, + podBytes: []byte(testSchedulerPod), + dsBytes: []byte(testSchedulerDaemonSet), }, } @@ -557,21 +464,17 @@ func TestBuildDaemonSet(t *testing.T) { podSpec, err := loadPodSpecFromFile(tempFile) if err != nil { - t.Fatalf("couldn't load the specified Pod: %v", err) + t.Fatalf("couldn't load the specified Pod") } - cfg := &kubeadmapi.MasterConfiguration{ - FeatureFlags: map[string]bool{string(features.StoreCertsInSecrets): rt.selfHostedSecrets}, - } - - ds := buildDaemonSet(cfg, rt.component, podSpec) + ds := buildDaemonSet(rt.component, podSpec, getDefaultMutators()) dsBytes, err := yaml.Marshal(ds) if err != nil { t.Fatalf("failed to marshal daemonset to YAML: %v", err) } if !bytes.Equal(dsBytes, rt.dsBytes) { - t.Errorf("failed TestBuildDaemonSet for name=%s (secrets=%t):\nexpected:\n%s\nsaw:\n%s", rt.component, rt.selfHostedSecrets, rt.dsBytes, dsBytes) + t.Errorf("failed TestBuildDaemonSet:\nexpected:\n%s\nsaw:\n%s", rt.dsBytes, dsBytes) } } } @@ -651,18 +554,3 @@ func createTempFileWithContent(content []byte) (string, error) { } return tempFile.Name(), nil } - -func indentString(input string, count int) string { - output := "" - lines := strings.Split(input, "\n") - for i, line := range lines { - if i > 0 { - output += strings.Repeat(" ", count) - } - output += line - if i < len(lines)-1 { - output += "\n" - } - } - return output -} diff --git a/cmd/kubeadm/app/util/arguments_test.go b/cmd/kubeadm/app/util/arguments_test.go new file mode 100644 index 00000000000..f2c6373bf81 --- /dev/null +++ b/cmd/kubeadm/app/util/arguments_test.go @@ -0,0 +1,341 @@ +/* +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 ( + "reflect" + "sort" + "testing" +) + +func TestBuildArgumentListFromMap(t *testing.T) { + var tests = []struct { + base map[string]string + overrides map[string]string + expected []string + }{ + { // override an argument from the base + base: map[string]string{ + "admission-control": "NamespaceLifecycle", + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + }, + overrides: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + }, + expected: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + }, + { // add an argument that is not in base + base: map[string]string{ + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + }, + overrides: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + }, + expected: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + }, + { // allow empty strings in base + base: map[string]string{ + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + "something-that-allows-empty-string": "", + }, + overrides: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + }, + expected: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + "--something-that-allows-empty-string=", + }, + }, + { // allow empty strings in overrides + base: map[string]string{ + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + "something-that-allows-empty-string": "foo", + }, + overrides: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + "something-that-allows-empty-string": "", + }, + expected: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + "--something-that-allows-empty-string=", + }, + }, + } + + for _, rt := range tests { + actual := BuildArgumentListFromMap(rt.base, rt.overrides) + sort.Strings(actual) + sort.Strings(rt.expected) + if !reflect.DeepEqual(actual, rt.expected) { + t.Errorf("failed BuildArgumentListFromMap:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual) + } + } +} + +func TestParseArgumentListToMap(t *testing.T) { + var tests = []struct { + args []string + expectedMap map[string]string + }{ + { + // normal case + args: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + expectedMap: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + }, + }, + { + // test that feature-gates is working + args: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + "--feature-gates=EnableFoo=true,EnableBar=false", + }, + expectedMap: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + "feature-gates": "EnableFoo=true,EnableBar=false", + }, + }, + { + // test that a binary can be the first arg + args: []string{ + "kube-apiserver", + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + "--feature-gates=EnableFoo=true,EnableBar=false", + }, + expectedMap: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + "feature-gates": "EnableFoo=true,EnableBar=false", + }, + }, + } + + for _, rt := range tests { + actualMap := ParseArgumentListToMap(rt.args) + if !reflect.DeepEqual(actualMap, rt.expectedMap) { + t.Errorf("failed ParseArgumentListToMap:\nexpected:\n%v\nsaw:\n%v", rt.expectedMap, actualMap) + } + } +} + +func TestReplaceArgument(t *testing.T) { + var tests = []struct { + args []string + mutateFunc func(map[string]string) map[string]string + expectedArgs []string + }{ + { + // normal case + args: []string{ + "kube-apiserver", + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + mutateFunc: func(argMap map[string]string) map[string]string { + argMap["admission-control"] = "NamespaceLifecycle,LimitRanger,ResourceQuota" + return argMap + }, + expectedArgs: []string{ + "kube-apiserver", + "--admission-control=NamespaceLifecycle,LimitRanger,ResourceQuota", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + }, + { + // normal case + args: []string{ + "kube-apiserver", + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + mutateFunc: func(argMap map[string]string) map[string]string { + argMap["new-arg-here"] = "foo" + return argMap + }, + expectedArgs: []string{ + "kube-apiserver", + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + "--new-arg-here=foo", + }, + }, + } + + for _, rt := range tests { + actualArgs := ReplaceArgument(rt.args, rt.mutateFunc) + sort.Strings(actualArgs) + sort.Strings(rt.expectedArgs) + if !reflect.DeepEqual(actualArgs, rt.expectedArgs) { + t.Errorf("failed ReplaceArgument:\nexpected:\n%v\nsaw:\n%v", rt.expectedArgs, actualArgs) + } + } +} + +func TestRoundtrip(t *testing.T) { + var tests = []struct { + args []string + }{ + { + // normal case + args: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + }, + { + // test that feature-gates is working + args: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + "--feature-gates=EnableFoo=true,EnableBar=false", + }, + }, + } + + for _, rt := range tests { + // These two methods should be each other's opposite functions, test that by chaining the methods and see if you get the same result back + actual := BuildArgumentListFromMap(ParseArgumentListToMap(rt.args), map[string]string{}) + sort.Strings(actual) + sort.Strings(rt.args) + + if !reflect.DeepEqual(actual, rt.args) { + t.Errorf("failed TestRoundtrip:\nexpected:\n%v\nsaw:\n%v", rt.args, actual) + } + } +} + +func TestParseArgument(t *testing.T) { + var tests = []struct { + arg string + expectedKey string + expectedVal string + expectedErr bool + }{ + { + // cannot be empty + arg: "", + expectedErr: true, + }, + { + // must contain -- and = + arg: "a", + expectedErr: true, + }, + { + // must contain -- and = + arg: "a-z", + expectedErr: true, + }, + { + // must contain -- + arg: "a=b", + expectedErr: true, + }, + { + // must contain a key + arg: "--=b", + expectedErr: true, + }, + { + // can contain key but no value + arg: "--a=", + expectedKey: "a", + expectedVal: "", + expectedErr: false, + }, + { + // simple case + arg: "--a=b", + expectedKey: "a", + expectedVal: "b", + expectedErr: false, + }, + { + // keys/values with '-' should be supported + arg: "--very-long-flag-name=some-value", + expectedKey: "very-long-flag-name", + expectedVal: "some-value", + expectedErr: false, + }, + { + // numbers should be handled correctly + arg: "--some-number=0.2", + expectedKey: "some-number", + expectedVal: "0.2", + expectedErr: false, + }, + { + // lists should be handled correctly + arg: "--admission-control=foo,bar,baz", + expectedKey: "admission-control", + expectedVal: "foo,bar,baz", + expectedErr: false, + }, + { + // more than one '=' should be allowed + arg: "--feature-gates=EnableFoo=true,EnableBar=false", + expectedKey: "feature-gates", + expectedVal: "EnableFoo=true,EnableBar=false", + expectedErr: false, + }, + } + + for _, rt := range tests { + key, val, actual := parseArgument(rt.arg) + if (actual != nil) != rt.expectedErr { + t.Errorf("failed parseArgument:\nexpected error:\n%t\nsaw error:\n%v", rt.expectedErr, actual) + } + if (key != rt.expectedKey) || (val != rt.expectedVal) { + t.Errorf("failed parseArgument:\nexpected key: %s\nsaw key: %s\nexpected value: %s\nsaw value: %s", rt.expectedKey, key, rt.expectedVal, val) + } + } +}