1
16
17 package node
18
19 import (
20 "context"
21 "encoding/base64"
22 "encoding/json"
23 "fmt"
24
25 v1 "k8s.io/api/core/v1"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/types"
28 "k8s.io/apimachinery/pkg/util/uuid"
29 "k8s.io/kubernetes/test/e2e/feature"
30 "k8s.io/kubernetes/test/e2e/framework"
31 e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
32 imageutils "k8s.io/kubernetes/test/utils/image"
33 admissionapi "k8s.io/pod-security-admission/api"
34
35 "github.com/onsi/ginkgo/v2"
36 "github.com/onsi/gomega"
37 )
38
39 var _ = SIGDescribe("Secrets", func() {
40 f := framework.NewDefaultFramework("secrets")
41 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
42
43
48 framework.ConformanceIt("should be consumable from pods in env vars", f.WithNodeConformance(), func(ctx context.Context) {
49 name := "secret-test-" + string(uuid.NewUUID())
50 secret := secretForTest(f.Namespace.Name, name)
51
52 ginkgo.By(fmt.Sprintf("Creating secret with name %s", secret.Name))
53 var err error
54 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil {
55 framework.Failf("unable to create test secret %s: %v", secret.Name, err)
56 }
57
58 pod := &v1.Pod{
59 ObjectMeta: metav1.ObjectMeta{
60 Name: "pod-secrets-" + string(uuid.NewUUID()),
61 },
62 Spec: v1.PodSpec{
63 Containers: []v1.Container{
64 {
65 Name: "secret-env-test",
66 Image: imageutils.GetE2EImage(imageutils.BusyBox),
67 Command: []string{"sh", "-c", "env"},
68 Env: []v1.EnvVar{
69 {
70 Name: "SECRET_DATA",
71 ValueFrom: &v1.EnvVarSource{
72 SecretKeyRef: &v1.SecretKeySelector{
73 LocalObjectReference: v1.LocalObjectReference{
74 Name: name,
75 },
76 Key: "data-1",
77 },
78 },
79 },
80 },
81 },
82 },
83 RestartPolicy: v1.RestartPolicyNever,
84 },
85 }
86
87 e2epodoutput.TestContainerOutput(ctx, f, "consume secrets", pod, 0, []string{
88 "SECRET_DATA=value-1",
89 })
90 })
91
92
97 framework.ConformanceIt("should be consumable via the environment", f.WithNodeConformance(), func(ctx context.Context) {
98 name := "secret-test-" + string(uuid.NewUUID())
99 secret := secretForTest(f.Namespace.Name, name)
100 ginkgo.By(fmt.Sprintf("creating secret %v/%v", f.Namespace.Name, secret.Name))
101 var err error
102 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil {
103 framework.Failf("unable to create test secret %s: %v", secret.Name, err)
104 }
105
106 pod := &v1.Pod{
107 ObjectMeta: metav1.ObjectMeta{
108 Name: "pod-configmaps-" + string(uuid.NewUUID()),
109 },
110 Spec: v1.PodSpec{
111 Containers: []v1.Container{
112 {
113 Name: "env-test",
114 Image: imageutils.GetE2EImage(imageutils.BusyBox),
115 Command: []string{"sh", "-c", "env"},
116 EnvFrom: []v1.EnvFromSource{
117 {
118 SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
119 },
120 {
121 Prefix: "p-",
122 SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
123 },
124 },
125 },
126 },
127 RestartPolicy: v1.RestartPolicyNever,
128 },
129 }
130
131 e2epodoutput.TestContainerOutput(ctx, f, "consume secrets", pod, 0, []string{
132 "data-1=value-1", "data-2=value-2", "data-3=value-3",
133 "p-data-1=value-1", "p-data-2=value-2", "p-data-3=value-3",
134 })
135 })
136
137
142 framework.ConformanceIt("should fail to create secret due to empty secret key", func(ctx context.Context) {
143 secret, err := createEmptyKeySecretForTest(ctx, f)
144 gomega.Expect(err).To(gomega.HaveOccurred(), "created secret %q with empty key in namespace %q", secret.Name, f.Namespace.Name)
145 })
146
147
156 framework.ConformanceIt("should patch a secret", func(ctx context.Context) {
157 ginkgo.By("creating a secret")
158
159 secretTestName := "test-secret-" + string(uuid.NewUUID())
160
161
162 _, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, &v1.Secret{
163 ObjectMeta: metav1.ObjectMeta{
164 Name: secretTestName,
165 Labels: map[string]string{
166 "testsecret-constant": "true",
167 },
168 },
169 Data: map[string][]byte{
170 "key": []byte("value"),
171 },
172 Type: "Opaque",
173 }, metav1.CreateOptions{})
174 framework.ExpectNoError(err, "failed to create secret")
175
176 ginkgo.By("listing secrets in all namespaces to ensure that there are more than zero")
177
178 secretsList, err := f.ClientSet.CoreV1().Secrets("").List(ctx, metav1.ListOptions{
179 LabelSelector: "testsecret-constant=true",
180 })
181 framework.ExpectNoError(err, "failed to list secrets")
182 gomega.Expect(secretsList.Items).ToNot(gomega.BeEmpty(), "no secrets found")
183
184 foundCreatedSecret := false
185 var secretCreatedName string
186 for _, val := range secretsList.Items {
187 if val.ObjectMeta.Name == secretTestName && val.ObjectMeta.Namespace == f.Namespace.Name {
188 foundCreatedSecret = true
189 secretCreatedName = val.ObjectMeta.Name
190 break
191 }
192 }
193 if !foundCreatedSecret {
194 framework.Failf("unable to find secret %s/%s by name", f.Namespace.Name, secretTestName)
195 }
196
197 ginkgo.By("patching the secret")
198
199 secretPatchNewData := base64.StdEncoding.EncodeToString([]byte("value1"))
200 secretPatch, err := json.Marshal(map[string]interface{}{
201 "metadata": map[string]interface{}{
202 "labels": map[string]string{"testsecret": "true"},
203 },
204 "data": map[string][]byte{"key": []byte(secretPatchNewData)},
205 })
206 framework.ExpectNoError(err, "failed to marshal JSON")
207 _, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Patch(ctx, secretCreatedName, types.StrategicMergePatchType, []byte(secretPatch), metav1.PatchOptions{})
208 framework.ExpectNoError(err, "failed to patch secret")
209
210 secret, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(ctx, secretCreatedName, metav1.GetOptions{})
211 framework.ExpectNoError(err, "failed to get secret")
212
213 secretDecodedstring, err := base64.StdEncoding.DecodeString(string(secret.Data["key"]))
214 framework.ExpectNoError(err, "failed to decode secret from Base64")
215
216 gomega.Expect(string(secretDecodedstring)).To(gomega.Equal("value1"), "found secret, but the data wasn't updated from the patch")
217
218 ginkgo.By("deleting the secret using a LabelSelector")
219 err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{
220 LabelSelector: "testsecret=true",
221 })
222 framework.ExpectNoError(err, "failed to delete patched secret")
223
224 ginkgo.By("listing secrets in all namespaces, searching for label name and value in patch")
225
226 secretsList, err = f.ClientSet.CoreV1().Secrets("").List(ctx, metav1.ListOptions{
227 LabelSelector: "testsecret-constant=true",
228 })
229 framework.ExpectNoError(err, "failed to list secrets")
230
231 foundCreatedSecret = false
232 for _, val := range secretsList.Items {
233 if val.ObjectMeta.Name == secretTestName && val.ObjectMeta.Namespace == f.Namespace.Name {
234 foundCreatedSecret = true
235 break
236 }
237 }
238 if foundCreatedSecret {
239 framework.Failf("secret %s/%s was not deleted successfully", f.Namespace.Name, secretTestName)
240 }
241 })
242
243
249 framework.It("should be consumable as environment variable names when secret keys start with a digit", feature.RelaxedEnvironmentVariableValidation, func(ctx context.Context) {
250 name := "secret-test-" + string(uuid.NewUUID())
251 secret := secretForTest(f.Namespace.Name, name)
252
253 ginkgo.By(fmt.Sprintf("creating secret %v/%v", f.Namespace.Name, secret.Name))
254 var err error
255 if secret, err = f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{}); err != nil {
256 framework.Failf("unable to create test secret %s: %v", secret.Name, err)
257 }
258
259 pod := &v1.Pod{
260 ObjectMeta: metav1.ObjectMeta{
261 Name: "pod-configmaps-" + string(uuid.NewUUID()),
262 },
263 Spec: v1.PodSpec{
264 Containers: []v1.Container{
265 {
266 Name: "env-test",
267 Image: imageutils.GetE2EImage(imageutils.BusyBox),
268 Command: []string{"sh", "-c", "env"},
269 EnvFrom: []v1.EnvFromSource{
270 {
271 SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
272 },
273 {
274
275 Prefix: "1-",
276 SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
277 },
278 },
279 },
280 },
281 RestartPolicy: v1.RestartPolicyNever,
282 },
283 }
284
285 e2epodoutput.TestContainerOutput(ctx, f, "consume secrets", pod, 0, []string{
286 "data-1=value-1", "data-2=value-2", "data-3=value-3",
287 "1-data-1=value-1", "1-data-2=value-2", "1-data-3=value-3",
288 })
289 })
290 })
291
292 func secretForTest(namespace, name string) *v1.Secret {
293 return &v1.Secret{
294 ObjectMeta: metav1.ObjectMeta{
295 Namespace: namespace,
296 Name: name,
297 },
298 Data: map[string][]byte{
299 "data-1": []byte("value-1\n"),
300 "data-2": []byte("value-2\n"),
301 "data-3": []byte("value-3\n"),
302 },
303 }
304 }
305
306 func createEmptyKeySecretForTest(ctx context.Context, f *framework.Framework) (*v1.Secret, error) {
307 secretName := "secret-emptykey-test-" + string(uuid.NewUUID())
308 secret := &v1.Secret{
309 ObjectMeta: metav1.ObjectMeta{
310 Namespace: f.Namespace.Name,
311 Name: secretName,
312 },
313 Data: map[string][]byte{
314 "": []byte("value-1\n"),
315 },
316 }
317 ginkgo.By(fmt.Sprintf("Creating projection with secret that has name %s", secret.Name))
318 return f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Create(ctx, secret, metav1.CreateOptions{})
319 }
320
View as plain text