From 7cc3971ee0a8971268e16c3a9dbbda02199c7e27 Mon Sep 17 00:00:00 2001 From: wojtekt Date: Mon, 9 Dec 2019 15:46:20 +0100 Subject: [PATCH] Immutable secrets/configmaps tests --- test/e2e/common/configmap_volume.go | 61 +++++++++++++++++++++++++---- test/e2e/common/secrets_volume.go | 61 +++++++++++++++++++++++++---- 2 files changed, 108 insertions(+), 14 deletions(-) diff --git a/test/e2e/common/configmap_volume.go b/test/e2e/common/configmap_volume.go index 3c68add2558..dec31ddcc61 100644 --- a/test/e2e/common/configmap_volume.go +++ b/test/e2e/common/configmap_volume.go @@ -23,6 +23,7 @@ import ( "github.com/onsi/ginkgo" "github.com/onsi/gomega" "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/kubernetes/test/e2e/framework" @@ -549,9 +550,55 @@ var _ = ginkgo.Describe("[sig-storage] ConfigMap", func() { }) - //The pod is in pending during volume creation until the configMap objects are available - //or until mount the configMap volume times out. There is no configMap object defined for the pod, so it should return timout exception unless it is marked optional. - //Slow (~5 mins) + // It should be forbidden to change data for configmaps marked as immutable, but + // allowed to modify its metadata independently of its state. + ginkgo.It("should be immutable if `immutable` field is set [Feature:ImmutableEphemeralVolume]", func() { + name := "immutable" + configMap := newConfigMap(f, name) + + currentConfigMap, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(configMap) + framework.ExpectNoError(err, "Failed to create config map %q in namespace %q", configMap.Name, configMap.Namespace) + + currentConfigMap.Data["data-4"] = "value-4" + currentConfigMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(currentConfigMap) + framework.ExpectNoError(err, "Failed to update config map %q in namespace %q", configMap.Name, configMap.Namespace) + + // Mark config map as immutable. + trueVal := true + currentConfigMap.Immutable = &trueVal + currentConfigMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(currentConfigMap) + framework.ExpectNoError(err, "Failed to mark config map %q in namespace %q as immutable", configMap.Name, configMap.Namespace) + + // Ensure data can't be changed now. + currentConfigMap.Data["data-5"] = "value-5" + _, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(currentConfigMap) + framework.ExpectEqual(apierrors.IsInvalid(err), true) + + // Ensure config map can't be switched from immutable to mutable. + currentConfigMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Get(name, metav1.GetOptions{}) + framework.ExpectNoError(err, "Failed to get config map %q in namespace %q", configMap.Name, configMap.Namespace) + framework.ExpectEqual(*currentConfigMap.Immutable, true) + + falseVal := false + currentConfigMap.Immutable = &falseVal + _, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(currentConfigMap) + framework.ExpectEqual(apierrors.IsInvalid(err), true) + + // Ensure that metadata can be changed. + currentConfigMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Get(name, metav1.GetOptions{}) + framework.ExpectNoError(err, "Failed to get config map %q in namespace %q", configMap.Name, configMap.Namespace) + currentConfigMap.Labels = map[string]string{"label1": "value1"} + _, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(currentConfigMap) + framework.ExpectNoError(err, "Failed to update config map %q in namespace %q", configMap.Name, configMap.Namespace) + + // Ensure that immutable config map can be deleted. + err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Delete(name, &metav1.DeleteOptions{}) + framework.ExpectNoError(err, "Failed to delete config map %q in namespace %q", configMap.Name, configMap.Namespace) + }) + + // The pod is in pending during volume creation until the configMap objects are available + // or until mount the configMap volume times out. There is no configMap object defined for the pod, so it should return timout exception unless it is marked optional. + // Slow (~5 mins) ginkgo.It("Should fail non-optional pod creation due to configMap object does not exist [Slow]", func() { volumeMountPath := "/etc/configmap-volumes" podName := "pod-configmaps-" + string(uuid.NewUUID()) @@ -559,9 +606,9 @@ var _ = ginkgo.Describe("[sig-storage] ConfigMap", func() { framework.ExpectError(err, "created pod %q with non-optional configMap in namespace %q", podName, f.Namespace.Name) }) - //ConfigMap object defined for the pod, If a key is specified which is not present in the ConfigMap, + // ConfigMap object defined for the pod, If a key is specified which is not present in the ConfigMap, // the volume setup will error unless it is marked optional, during the pod creation. - //Slow (~5 mins) + // Slow (~5 mins) ginkgo.It("Should fail non-optional pod creation due to the key in the configMap object does not exist [Slow]", func() { volumeMountPath := "/etc/configmap-volumes" podName := "pod-configmaps-" + string(uuid.NewUUID()) @@ -754,7 +801,7 @@ func createNonOptionalConfigMapPod(f *framework.Framework, volumeMountPath, podN createContainerName := "createcm-volume-test" createVolumeName := "createcm-volume" - //creating a pod without configMap object created, by mentioning the configMap volume source's local reference name + // creating a pod without configMap object created, by mentioning the configMap volume source's local reference name pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, @@ -810,7 +857,7 @@ func createNonOptionalConfigMapPodWithConfig(f *framework.Framework, volumeMount if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(configMap); err != nil { framework.Failf("unable to create test configMap %s: %v", configMap.Name, err) } - //creating a pod with configMap object, but with different key which is not present in configMap object. + // creating a pod with configMap object, but with different key which is not present in configMap object. pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, diff --git a/test/e2e/common/secrets_volume.go b/test/e2e/common/secrets_volume.go index c0318c9d8b3..f2bf26c148e 100644 --- a/test/e2e/common/secrets_volume.go +++ b/test/e2e/common/secrets_volume.go @@ -21,6 +21,7 @@ import ( "path" "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/kubernetes/test/e2e/framework" @@ -368,9 +369,55 @@ var _ = ginkgo.Describe("[sig-storage] Secrets", func() { gomega.Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("Error reading file /etc/secret-volumes/delete/data-1")) }) - //The secret is in pending during volume creation until the secret objects are available - //or until mount the secret volume times out. There is no secret object defined for the pod, so it should return timout exception unless it is marked optional. - //Slow (~5 mins) + // It should be forbidden to change data for secrets marked as immutable, but + // allowed to modify its metadata independently of its state. + ginkgo.It("should be immutable if `immutable` field is set [Feature:ImmutableEphemeralVolume]", func() { + name := "immutable" + secret := secretForTest(f.Namespace.Name, name) + + currentSecret, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(secret) + framework.ExpectNoError(err, "Failed to create secret %q in namespace %q", secret.Name, secret.Namespace) + + currentSecret.Data["data-4"] = []byte("value-4\n") + currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(currentSecret) + framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", secret.Name, secret.Namespace) + + // Mark secret as immutable. + trueVal := true + currentSecret.Immutable = &trueVal + currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(currentSecret) + framework.ExpectNoError(err, "Failed to mark secret %q in namespace %q as immutable", secret.Name, secret.Namespace) + + // Ensure data can't be changed now. + currentSecret.Data["data-5"] = []byte("value-5\n") + _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(currentSecret) + framework.ExpectEqual(apierrors.IsInvalid(err), true) + + // Ensure secret can't be switched from immutable to mutable. + currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(name, metav1.GetOptions{}) + framework.ExpectNoError(err, "Failed to get secret %q in namespace %q", secret.Name, secret.Namespace) + framework.ExpectEqual(*currentSecret.Immutable, true) + + falseVal := false + currentSecret.Immutable = &falseVal + _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(currentSecret) + framework.ExpectEqual(apierrors.IsInvalid(err), true) + + // Ensure that metadata can be changed. + currentSecret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(name, metav1.GetOptions{}) + framework.ExpectNoError(err, "Failed to get secret %q in namespace %q", secret.Name, secret.Namespace) + currentSecret.Labels = map[string]string{"label1": "value1"} + _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Update(currentSecret) + framework.ExpectNoError(err, "Failed to update secret %q in namespace %q", secret.Name, secret.Namespace) + + // Ensure that immutable secret can be deleted. + err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(name, &metav1.DeleteOptions{}) + framework.ExpectNoError(err, "Failed to delete secret %q in namespace %q", secret.Name, secret.Namespace) + }) + + // The secret is in pending during volume creation until the secret objects are available + // or until mount the secret volume times out. There is no secret object defined for the pod, so it should return timout exception unless it is marked optional. + // Slow (~5 mins) ginkgo.It("Should fail non-optional pod creation due to secret object does not exist [Slow]", func() { volumeMountPath := "/etc/secret-volumes" podName := "pod-secrets-" + string(uuid.NewUUID()) @@ -378,9 +425,9 @@ var _ = ginkgo.Describe("[sig-storage] Secrets", func() { framework.ExpectError(err, "created pod %q with non-optional secret in namespace %q", podName, f.Namespace.Name) }) - //Secret object defined for the pod, If a key is specified which is not present in the secret, + // Secret object defined for the pod, If a key is specified which is not present in the secret, // the volume setup will error unless it is marked optional, during the pod creation. - //Slow (~5 mins) + // Slow (~5 mins) ginkgo.It("Should fail non-optional pod creation due to the key in the secret object does not exist [Slow]", func() { volumeMountPath := "/etc/secret-volumes" podName := "pod-secrets-" + string(uuid.NewUUID()) @@ -548,7 +595,7 @@ func createNonOptionalSecretPod(f *framework.Framework, volumeMountPath, podName createContainerName := "creates-volume-test" createVolumeName := "creates-volume" - //creating a pod without secret object created, by mentioning the secret volume source reference name + // creating a pod without secret object created, by mentioning the secret volume source reference name pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, @@ -603,7 +650,7 @@ func createNonOptionalSecretPodWithSecret(f *framework.Framework, volumeMountPat if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(secret); err != nil { framework.Failf("unable to create test secret %s: %v", secret.Name, err) } - //creating a pod with secret object, with the key which is not present in secret object. + // creating a pod with secret object, with the key which is not present in secret object. pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName,