k8s.io/kubernetes@v1.29.3/test/e2e/common/node/secrets.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package node 18 19 import ( 20 "context" 21 "encoding/base64" 22 "encoding/json" 23 "fmt" 24 25 "github.com/onsi/ginkgo/v2" 26 "github.com/onsi/gomega" 27 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/types" 31 "k8s.io/apimachinery/pkg/util/uuid" 32 "k8s.io/kubernetes/test/e2e/framework" 33 e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" 34 imageutils "k8s.io/kubernetes/test/utils/image" 35 admissionapi "k8s.io/pod-security-admission/api" 36 ) 37 38 var _ = SIGDescribe("Secrets", func() { 39 f := framework.NewDefaultFramework("secrets") 40 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 41 42 /* 43 Release: v1.9 44 Testname: Secrets, pod environment field 45 Description: Create a secret. Create a Pod with Container that declares a environment variable which references the secret created to extract a key value from the secret. Pod MUST have the environment variable that contains proper value for the key to the secret. 46 */ 47 framework.ConformanceIt("should be consumable from pods in env vars", f.WithNodeConformance(), func(ctx context.Context) { 48 name := "secret-test-" + string(uuid.NewUUID()) 49 secret := secretForTest(f.Namespace.Name, name) 50 51 ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name)) 52 var err error 53 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil { 54 framework.Failf("unable to create test secret %s: %v", secret.Name, err) 55 } 56 57 pod := &v1.Pod{ 58 ObjectMeta: metav1.ObjectMeta{ 59 Name: "pod-secrets-" + string(uuid.NewUUID()), 60 }, 61 Spec: v1.PodSpec{ 62 Containers: []v1.Container{ 63 { 64 Name: "secret-env-test", 65 Image: imageutils.GetE2EImage(imageutils.BusyBox), 66 Command: []string{"sh", "-c", "env"}, 67 Env: []v1.EnvVar{ 68 { 69 Name: "SECRET_DATA", 70 ValueFrom: &v1.EnvVarSource{ 71 SecretKeyRef: &v1.SecretKeySelector{ 72 LocalObjectReference: v1.LocalObjectReference{ 73 Name: name, 74 }, 75 Key: "data-1", 76 }, 77 }, 78 }, 79 }, 80 }, 81 }, 82 RestartPolicy: v1.RestartPolicyNever, 83 }, 84 } 85 86 e2epodoutput.TestContainerOutput(ctx, f, "consume secrets", pod, 0, []string{ 87 "SECRET_DATA=value-1", 88 }) 89 }) 90 91 /* 92 Release: v1.9 93 Testname: Secrets, pod environment from source 94 Description: Create a secret. Create a Pod with Container that declares a environment variable using 'EnvFrom' which references the secret created to extract a key value from the secret. Pod MUST have the environment variable that contains proper value for the key to the secret. 95 */ 96 framework.ConformanceIt("should be consumable via the environment", f.WithNodeConformance(), func(ctx context.Context) { 97 name := "secret-test-" + string(uuid.NewUUID()) 98 secret := secretForTest(f.Namespace.Name, name) 99 ginkgo.By(fmt.Sprintf("creating secret %v/%v", f.Namespace.Name, secret.Name)) 100 var err error 101 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil { 102 framework.Failf("unable to create test secret %s: %v", secret.Name, err) 103 } 104 105 pod := &v1.Pod{ 106 ObjectMeta: metav1.ObjectMeta{ 107 Name: "pod-configmaps-" + string(uuid.NewUUID()), 108 }, 109 Spec: v1.PodSpec{ 110 Containers: []v1.Container{ 111 { 112 Name: "env-test", 113 Image: imageutils.GetE2EImage(imageutils.BusyBox), 114 Command: []string{"sh", "-c", "env"}, 115 EnvFrom: []v1.EnvFromSource{ 116 { 117 SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, 118 }, 119 { 120 Prefix: "p-", 121 SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}}, 122 }, 123 }, 124 }, 125 }, 126 RestartPolicy: v1.RestartPolicyNever, 127 }, 128 } 129 130 e2epodoutput.TestContainerOutput(ctx, f, "consume secrets", pod, 0, []string{ 131 "data-1=value-1", "data-2=value-2", "data-3=value-3", 132 "p-data-1=value-1", "p-data-2=value-2", "p-data-3=value-3", 133 }) 134 }) 135 136 /* 137 Release: v1.15 138 Testname: Secrets, with empty-key 139 Description: Attempt to create a Secret with an empty key. The creation MUST fail. 140 */ 141 framework.ConformanceIt("should fail to create secret due to empty secret key", func(ctx context.Context) { 142 secret, err := createEmptyKeySecretForTest(ctx, f) 143 gomega.Expect(err).To(gomega.HaveOccurred(), "created secret %q with empty key in namespace %q", secret.Name, f.Namespace.Name) 144 }) 145 146 /* 147 Release: v1.18 148 Testname: Secret patching 149 Description: A Secret is created. 150 Listing all Secrets MUST return an empty list. 151 Given the patching and fetching of the Secret, the fields MUST equal the new values. 152 The Secret is deleted by it's static Label. 153 Secrets are listed finally, the list MUST NOT include the originally created Secret. 154 */ 155 framework.ConformanceIt("should patch a secret", func(ctx context.Context) { 156 ginkgo.By("creating a secret") 157 158 secretTestName := "test-secret-" + string(uuid.NewUUID()) 159 160 // create a secret in the test namespace 161 _, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, &v1.Secret{ 162 ObjectMeta: metav1.ObjectMeta{ 163 Name: secretTestName, 164 Labels: map[string]string{ 165 "testsecret-constant": "true", 166 }, 167 }, 168 Data: map[string][]byte{ 169 "key": []byte("value"), 170 }, 171 Type: "Opaque", 172 }, metav1.CreateOptions{}) 173 framework.ExpectNoError(err, "failed to create secret") 174 175 ginkgo.By("listing secrets in all namespaces to ensure that there are more than zero") 176 // list all secrets in all namespaces to ensure endpoint coverage 177 secretsList, err := f.ClientSet.CoreV1().Secrets("").List(ctx, metav1.ListOptions{ 178 LabelSelector: "testsecret-constant=true", 179 }) 180 framework.ExpectNoError(err, "failed to list secrets") 181 gomega.Expect(secretsList.Items).ToNot(gomega.BeEmpty(), "no secrets found") 182 183 foundCreatedSecret := false 184 var secretCreatedName string 185 for _, val := range secretsList.Items { 186 if val.ObjectMeta.Name == secretTestName && val.ObjectMeta.Namespace == f.Namespace.Name { 187 foundCreatedSecret = true 188 secretCreatedName = val.ObjectMeta.Name 189 break 190 } 191 } 192 if !foundCreatedSecret { 193 framework.Failf("unable to find secret %s/%s by name", f.Namespace.Name, secretTestName) 194 } 195 196 ginkgo.By("patching the secret") 197 // patch the secret in the test namespace 198 secretPatchNewData := base64.StdEncoding.EncodeToString([]byte("value1")) 199 secretPatch, err := json.Marshal(map[string]interface{}{ 200 "metadata": map[string]interface{}{ 201 "labels": map[string]string{"testsecret": "true"}, 202 }, 203 "data": map[string][]byte{"key": []byte(secretPatchNewData)}, 204 }) 205 framework.ExpectNoError(err, "failed to marshal JSON") 206 _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Patch(ctx, secretCreatedName, types.StrategicMergePatchType, []byte(secretPatch), metav1.PatchOptions{}) 207 framework.ExpectNoError(err, "failed to patch secret") 208 209 secret, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(ctx, secretCreatedName, metav1.GetOptions{}) 210 framework.ExpectNoError(err, "failed to get secret") 211 212 secretDecodedstring, err := base64.StdEncoding.DecodeString(string(secret.Data["key"])) 213 framework.ExpectNoError(err, "failed to decode secret from Base64") 214 215 gomega.Expect(string(secretDecodedstring)).To(gomega.Equal("value1"), "found secret, but the data wasn't updated from the patch") 216 217 ginkgo.By("deleting the secret using a LabelSelector") 218 err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{ 219 LabelSelector: "testsecret=true", 220 }) 221 framework.ExpectNoError(err, "failed to delete patched secret") 222 223 ginkgo.By("listing secrets in all namespaces, searching for label name and value in patch") 224 // list all secrets in all namespaces 225 secretsList, err = f.ClientSet.CoreV1().Secrets("").List(ctx, metav1.ListOptions{ 226 LabelSelector: "testsecret-constant=true", 227 }) 228 framework.ExpectNoError(err, "failed to list secrets") 229 230 foundCreatedSecret = false 231 for _, val := range secretsList.Items { 232 if val.ObjectMeta.Name == secretTestName && val.ObjectMeta.Namespace == f.Namespace.Name { 233 foundCreatedSecret = true 234 break 235 } 236 } 237 if foundCreatedSecret { 238 framework.Failf("secret %s/%s was not deleted successfully", f.Namespace.Name, secretTestName) 239 } 240 }) 241 }) 242 243 func secretForTest(namespace, name string) *v1.Secret { 244 return &v1.Secret{ 245 ObjectMeta: metav1.ObjectMeta{ 246 Namespace: namespace, 247 Name: name, 248 }, 249 Data: map[string][]byte{ 250 "data-1": []byte("value-1\n"), 251 "data-2": []byte("value-2\n"), 252 "data-3": []byte("value-3\n"), 253 }, 254 } 255 } 256 257 func createEmptyKeySecretForTest(ctx context.Context, f *framework.Framework) (*v1.Secret, error) { 258 secretName := "secret-emptykey-test-" + string(uuid.NewUUID()) 259 secret := &v1.Secret{ 260 ObjectMeta: metav1.ObjectMeta{ 261 Namespace: f.Namespace.Name, 262 Name: secretName, 263 }, 264 Data: map[string][]byte{ 265 "": []byte("value-1\n"), 266 }, 267 } 268 ginkgo.By(fmt.Sprintf("Creating projection with secret that has name %s", secret.Name)) 269 return f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}) 270 }