| /* |
| Copyright 2018 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 etcd |
| |
| import ( |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "testing" |
| |
| kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" |
| testutil "k8s.io/kubernetes/cmd/kubeadm/test" |
| ) |
| |
| const ( |
| secureEtcdPod = `# generated by kubeadm v1.10.0 |
| apiVersion: v1 |
| kind: Pod |
| metadata: |
| annotations: |
| scheduler.alpha.kubernetes.io/critical-pod: "" |
| creationTimestamp: null |
| labels: |
| component: etcd |
| tier: control-plane |
| name: etcd |
| namespace: kube-system |
| spec: |
| containers: |
| - command: |
| - etcd |
| - --advertise-client-urls=https://127.0.0.1:2379 |
| - --data-dir=/var/lib/etcd |
| - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key |
| - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt |
| - --listen-client-urls=https://127.0.0.1:2379 |
| - --peer-client-cert-auth=true |
| - --cert-file=/etc/kubernetes/pki/etcd/server.crt |
| - --key-file=/etc/kubernetes/pki/etcd/server.key |
| - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt |
| - --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt |
| - --client-cert-auth=true |
| image: k8s.gcr.io/etcd-amd64:3.1.12 |
| livenessProbe: |
| exec: |
| command: |
| - /bin/sh |
| - -ec |
| - ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt |
| --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key |
| get foo |
| failureThreshold: 8 |
| initialDelaySeconds: 15 |
| timeoutSeconds: 15 |
| name: etcd |
| resources: {} |
| volumeMounts: |
| - mountPath: /var/lib/etcd |
| name: etcd-data |
| - mountPath: /etc/kubernetes/pki/etcd |
| name: etcd-certs |
| hostNetwork: true |
| volumes: |
| - hostPath: |
| path: /var/lib/etcd |
| type: DirectoryOrCreate |
| name: etcd-data |
| - hostPath: |
| path: /etc/kubernetes/pki/etcd |
| type: DirectoryOrCreate |
| name: etcd-certs |
| status: {} |
| ` |
| secureExposedEtcdPod = ` |
| apiVersion: v1 |
| kind: Pod |
| metadata: |
| annotations: |
| scheduler.alpha.kubernetes.io/critical-pod: "" |
| creationTimestamp: null |
| labels: |
| component: etcd |
| tier: control-plane |
| name: etcd |
| namespace: kube-system |
| spec: |
| containers: |
| - command: |
| - etcd |
| - --advertise-client-urls=https://10.0.5.5:2379 |
| - --data-dir=/var/lib/etcd |
| - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key |
| - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt |
| - --listen-client-urls=https://[::0:0]:2379 |
| - --peer-client-cert-auth=true |
| - --cert-file=/etc/kubernetes/pki/etcd/server.crt |
| - --key-file=/etc/kubernetes/pki/etcd/server.key |
| - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt |
| - --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt |
| - --client-cert-auth=true |
| image: k8s.gcr.io/etcd-amd64:3.1.12 |
| livenessProbe: |
| exec: |
| command: |
| - /bin/sh |
| - -ec |
| - ETCDCTL_API=3 etcdctl --endpoints=https://[::1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt |
| --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key |
| get foo |
| failureThreshold: 8 |
| initialDelaySeconds: 15 |
| timeoutSeconds: 15 |
| name: etcd |
| resources: {} |
| volumeMounts: |
| - mountPath: /var/lib/etcd |
| name: etcd-data |
| - mountPath: /etc/kubernetes/pki/etcd |
| name: etcd-certs |
| hostNetwork: true |
| volumes: |
| - hostPath: |
| path: /var/lib/etcd |
| type: DirectoryOrCreate |
| name: etcd-data |
| - hostPath: |
| path: /etc/kubernetes/pki/etcd |
| type: DirectoryOrCreate |
| name: etcd-certs |
| status: {} |
| ` |
| insecureEtcdPod = `# generated by kubeadm v1.9.6 |
| apiVersion: v1 |
| kind: Pod |
| metadata: |
| annotations: |
| scheduler.alpha.kubernetes.io/critical-pod: "" |
| creationTimestamp: null |
| labels: |
| component: etcd |
| tier: control-plane |
| name: etcd |
| namespace: kube-system |
| spec: |
| containers: |
| - command: |
| - etcd |
| - --listen-client-urls=http://127.0.0.1:2379 |
| - --advertise-client-urls=http://127.0.0.1:2379 |
| - --data-dir=/var/lib/etcd |
| image: gcr.io/google_containers/etcd-amd64:3.1.11 |
| livenessProbe: |
| failureThreshold: 8 |
| httpGet: |
| host: 127.0.0.1 |
| path: /health |
| port: 2379 |
| scheme: HTTP |
| initialDelaySeconds: 15 |
| timeoutSeconds: 15 |
| name: etcd |
| resources: {} |
| volumeMounts: |
| - mountPath: /var/lib/etcd |
| name: etcd |
| hostNetwork: true |
| volumes: |
| - hostPath: |
| path: /var/lib/etcd |
| type: DirectoryOrCreate |
| name: etcd |
| status: {} |
| ` |
| invalidPod = `---{ broken yaml @@@` |
| ) |
| |
| func TestPodManifestHasTLS(t *testing.T) { |
| tests := []struct { |
| description string |
| podYaml string |
| hasTLS bool |
| expectErr bool |
| writeManifest bool |
| }{ |
| { |
| description: "secure etcd returns true", |
| podYaml: secureEtcdPod, |
| hasTLS: true, |
| writeManifest: true, |
| expectErr: false, |
| }, |
| { |
| description: "secure exposed etcd returns true", |
| podYaml: secureExposedEtcdPod, |
| hasTLS: true, |
| writeManifest: true, |
| expectErr: false, |
| }, |
| { |
| description: "insecure etcd returns false", |
| podYaml: insecureEtcdPod, |
| hasTLS: false, |
| writeManifest: true, |
| expectErr: false, |
| }, |
| { |
| description: "invalid pod fails to unmarshal", |
| podYaml: invalidPod, |
| hasTLS: false, |
| writeManifest: true, |
| expectErr: true, |
| }, |
| { |
| description: "non-existent file returns error", |
| podYaml: ``, |
| hasTLS: false, |
| writeManifest: false, |
| expectErr: true, |
| }, |
| } |
| |
| for _, rt := range tests { |
| tmpdir := testutil.SetupTempDir(t) |
| defer os.RemoveAll(tmpdir) |
| |
| manifestPath := filepath.Join(tmpdir, "etcd.yaml") |
| if rt.writeManifest { |
| err := ioutil.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) |
| } |
| } |
| |
| hasTLS, actualErr := PodManifestsHaveTLS(tmpdir) |
| if (actualErr != nil) != rt.expectErr { |
| t.Errorf( |
| "PodManifestHasTLS failed\n%s\n\texpected error: %t\n\tgot: %t\n\tactual error: %v", |
| rt.description, |
| rt.expectErr, |
| (actualErr != nil), |
| actualErr, |
| ) |
| } |
| |
| if hasTLS != rt.hasTLS { |
| t.Errorf("PodManifestHasTLS failed\n%s\n\texpected hasTLS: %t\n\tgot: %t", rt.description, rt.hasTLS, hasTLS) |
| } |
| } |
| } |
| |
| func TestCheckConfigurationIsHA(t *testing.T) { |
| var tests = []struct { |
| name string |
| cfg *kubeadmapi.Etcd |
| expected bool |
| }{ |
| { |
| name: "HA etcd", |
| cfg: &kubeadmapi.Etcd{ |
| External: &kubeadmapi.ExternalEtcd{ |
| Endpoints: []string{"10.100.0.1:2379", "10.100.0.2:2379", "10.100.0.3:2379"}, |
| }, |
| }, |
| expected: true, |
| }, |
| { |
| name: "single External etcd", |
| cfg: &kubeadmapi.Etcd{ |
| External: &kubeadmapi.ExternalEtcd{ |
| Endpoints: []string{"10.100.0.1:2379"}, |
| }, |
| }, |
| expected: false, |
| }, |
| { |
| name: "local etcd", |
| cfg: &kubeadmapi.Etcd{ |
| Local: &kubeadmapi.LocalEtcd{}, |
| }, |
| expected: false, |
| }, |
| { |
| name: "empty etcd struct", |
| cfg: &kubeadmapi.Etcd{}, |
| expected: false, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| if isHA := CheckConfigurationIsHA(test.cfg); isHA != test.expected { |
| t.Errorf("expected isHA to be %v, got %v", test.expected, isHA) |
| } |
| }) |
| } |
| } |