...

Source file src/k8s.io/kubernetes/test/e2e/common/node/secrets.go

Documentation: k8s.io/kubernetes/test/e2e/common/node

     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  	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  	/*
    44  		Release: v1.9
    45  		Testname: Secrets, pod environment field
    46  		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.
    47  	*/
    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  	/*
    93  		Release: v1.9
    94  		Testname: Secrets, pod environment from source
    95  		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.
    96  	*/
    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  	/*
   138  	   Release: v1.15
   139  	   Testname: Secrets, with empty-key
   140  	   Description: Attempt to create a Secret with an empty key. The creation MUST fail.
   141  	*/
   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  	/*
   148  			   Release: v1.18
   149  			   Testname: Secret patching
   150  			   Description: A Secret is created.
   151  		           Listing all Secrets MUST return an empty list.
   152  		           Given the patching and fetching of the Secret, the fields MUST equal the new values.
   153  		           The Secret is deleted by it's static Label.
   154  		           Secrets are listed finally, the list MUST NOT include the originally created Secret.
   155  	*/
   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  		// create a secret in the test namespace
   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  		// list all secrets in all namespaces to ensure endpoint coverage
   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  		// patch the secret in the test namespace
   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  		// list all secrets in all namespaces
   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  	/*
   244  		Release: v1.30
   245  		Testname: Secrets, pod environment from source
   246  		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.
   247  		Allows users to use envFrom to set prefix starting with a digit as environment variable names.
   248  	*/
   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  								// prefix start with a digit can be consumed as environment variables.
   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