...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/serviceaccount/admission_test.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/serviceaccount

     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 serviceaccount
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	"github.com/stretchr/testify/assert"
    27  	corev1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	"k8s.io/apiserver/pkg/admission"
    31  	admissiontesting "k8s.io/apiserver/pkg/admission/testing"
    32  	"k8s.io/client-go/informers"
    33  	"k8s.io/client-go/kubernetes/fake"
    34  	api "k8s.io/kubernetes/pkg/apis/core"
    35  	v1defaults "k8s.io/kubernetes/pkg/apis/core/v1"
    36  	"k8s.io/kubernetes/pkg/controller"
    37  	kubelet "k8s.io/kubernetes/pkg/kubelet/types"
    38  	utilpointer "k8s.io/utils/pointer"
    39  )
    40  
    41  func TestIgnoresNonCreate(t *testing.T) {
    42  	for _, op := range []admission.Operation{admission.Delete, admission.Connect} {
    43  		handler := NewServiceAccount()
    44  		if handler.Handles(op) {
    45  			t.Errorf("Expected not to handle operation %s", op)
    46  		}
    47  	}
    48  }
    49  
    50  func TestIgnoresNonPodResource(t *testing.T) {
    51  	pod := &api.Pod{}
    52  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("CustomResource").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
    53  	handler := admissiontesting.WithReinvocationTesting(t, NewServiceAccount())
    54  	err := handler.Admit(context.TODO(), attrs, nil)
    55  	if err != nil {
    56  		t.Errorf("Expected non-pod resource allowed, got err: %v", err)
    57  	}
    58  }
    59  
    60  func TestIgnoresNilObject(t *testing.T) {
    61  	attrs := admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
    62  	handler := admissiontesting.WithReinvocationTesting(t, NewServiceAccount())
    63  	err := handler.Admit(context.TODO(), attrs, nil)
    64  	if err != nil {
    65  		t.Errorf("Expected nil object allowed allowed, got err: %v", err)
    66  	}
    67  }
    68  
    69  func TestIgnoresNonPodObject(t *testing.T) {
    70  	obj := &api.Namespace{}
    71  	attrs := admission.NewAttributesRecord(obj, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
    72  	handler := admissiontesting.WithReinvocationTesting(t, NewServiceAccount())
    73  	err := handler.Admit(context.TODO(), attrs, nil)
    74  	if err != nil {
    75  		t.Errorf("Expected non pod object allowed, got err: %v", err)
    76  	}
    77  }
    78  
    79  func TestIgnoresMirrorPod(t *testing.T) {
    80  	pod := &api.Pod{
    81  		ObjectMeta: metav1.ObjectMeta{
    82  			Annotations: map[string]string{
    83  				kubelet.ConfigMirrorAnnotationKey: "true",
    84  			},
    85  		},
    86  		Spec: api.PodSpec{
    87  			Volumes: []api.Volume{
    88  				{VolumeSource: api.VolumeSource{}},
    89  			},
    90  		},
    91  	}
    92  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
    93  	err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
    94  	if err != nil {
    95  		t.Errorf("Expected mirror pod without service account or secrets allowed, got err: %v", err)
    96  	}
    97  }
    98  
    99  func TestRejectsMirrorPodWithServiceAccount(t *testing.T) {
   100  	pod := &api.Pod{
   101  		ObjectMeta: metav1.ObjectMeta{
   102  			Annotations: map[string]string{
   103  				kubelet.ConfigMirrorAnnotationKey: "true",
   104  			},
   105  		},
   106  		Spec: api.PodSpec{
   107  			ServiceAccountName: "default",
   108  		},
   109  	}
   110  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   111  	err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
   112  	if err == nil {
   113  		t.Errorf("Expected a mirror pod to be prevented from referencing a service account")
   114  	}
   115  }
   116  
   117  func TestRejectsMirrorPodWithSecretVolumes(t *testing.T) {
   118  	pod := &api.Pod{
   119  		ObjectMeta: metav1.ObjectMeta{
   120  			Annotations: map[string]string{
   121  				kubelet.ConfigMirrorAnnotationKey: "true",
   122  			},
   123  		},
   124  		Spec: api.PodSpec{
   125  			Volumes: []api.Volume{
   126  				{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "mysecret"}}},
   127  			},
   128  		},
   129  	}
   130  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   131  	err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
   132  	if err == nil {
   133  		t.Errorf("Expected a mirror pod to be prevented from referencing a secret volume")
   134  	}
   135  }
   136  
   137  func TestRejectsMirrorPodWithServiceAccountTokenVolumeProjections(t *testing.T) {
   138  	pod := &api.Pod{
   139  		ObjectMeta: metav1.ObjectMeta{
   140  			Annotations: map[string]string{
   141  				kubelet.ConfigMirrorAnnotationKey: "true",
   142  			},
   143  		},
   144  		Spec: api.PodSpec{
   145  			Volumes: []api.Volume{
   146  				{VolumeSource: api.VolumeSource{
   147  					Projected: &api.ProjectedVolumeSource{
   148  						Sources: []api.VolumeProjection{{ServiceAccountToken: &api.ServiceAccountTokenProjection{}}},
   149  					},
   150  				},
   151  				},
   152  			},
   153  		},
   154  	}
   155  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   156  	err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
   157  	if err == nil {
   158  		t.Errorf("Expected a mirror pod to be prevented from referencing a ServiceAccountToken volume projection")
   159  	}
   160  }
   161  
   162  func TestAssignsDefaultServiceAccountAndBoundTokenWithNoSecretTokens(t *testing.T) {
   163  	ns := "myns"
   164  
   165  	admit := NewServiceAccount()
   166  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   167  	admit.SetExternalKubeInformerFactory(informerFactory)
   168  	admit.MountServiceAccountToken = true
   169  
   170  	// Add the default service account for the ns into the cache
   171  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   172  		ObjectMeta: metav1.ObjectMeta{
   173  			Name:      DefaultServiceAccountName,
   174  			Namespace: ns,
   175  		},
   176  	})
   177  
   178  	v1PodIn := &corev1.Pod{
   179  		Spec: corev1.PodSpec{
   180  			Containers: []corev1.Container{{}},
   181  		},
   182  	}
   183  	v1defaults.SetObjectDefaults_Pod(v1PodIn)
   184  	pod := &api.Pod{}
   185  	if err := v1defaults.Convert_v1_Pod_To_core_Pod(v1PodIn, pod, nil); err != nil {
   186  		t.Fatal(err)
   187  	}
   188  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   189  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   190  	if err != nil {
   191  		t.Fatalf("Expected success, got: %v", err)
   192  	}
   193  
   194  	expectedVolumes := []api.Volume{{
   195  		Name: "cleared",
   196  		VolumeSource: api.VolumeSource{
   197  			Projected: &api.ProjectedVolumeSource{
   198  				Sources: []api.VolumeProjection{
   199  					{ServiceAccountToken: &api.ServiceAccountTokenProjection{ExpirationSeconds: 3607, Path: "token"}},
   200  					{ConfigMap: &api.ConfigMapProjection{LocalObjectReference: api.LocalObjectReference{Name: "kube-root-ca.crt"}, Items: []api.KeyToPath{{Key: "ca.crt", Path: "ca.crt"}}}},
   201  					{DownwardAPI: &api.DownwardAPIProjection{Items: []api.DownwardAPIVolumeFile{{Path: "namespace", FieldRef: &api.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}},
   202  				},
   203  				DefaultMode: utilpointer.Int32(0644),
   204  			},
   205  		},
   206  	}}
   207  	expectedVolumeMounts := []api.VolumeMount{{
   208  		Name:      "cleared",
   209  		ReadOnly:  true,
   210  		MountPath: "/var/run/secrets/kubernetes.io/serviceaccount",
   211  	}}
   212  
   213  	// clear generated volume names
   214  	for i := range pod.Spec.Volumes {
   215  		if len(pod.Spec.Volumes[i].Name) > 0 {
   216  			pod.Spec.Volumes[i].Name = "cleared"
   217  		}
   218  	}
   219  	for i := range pod.Spec.Containers[0].VolumeMounts {
   220  		if len(pod.Spec.Containers[0].VolumeMounts[i].Name) > 0 {
   221  			pod.Spec.Containers[0].VolumeMounts[i].Name = "cleared"
   222  		}
   223  	}
   224  
   225  	if !reflect.DeepEqual(expectedVolumes, pod.Spec.Volumes) {
   226  		t.Errorf("unexpected volumes: %s", cmp.Diff(expectedVolumes, pod.Spec.Volumes))
   227  	}
   228  	if !reflect.DeepEqual(expectedVolumeMounts, pod.Spec.Containers[0].VolumeMounts) {
   229  		t.Errorf("unexpected volumes: %s", cmp.Diff(expectedVolumeMounts, pod.Spec.Containers[0].VolumeMounts))
   230  	}
   231  
   232  	// ensure result converted to v1 matches defaulted object
   233  	v1PodOut := &corev1.Pod{}
   234  	if err := v1defaults.Convert_core_Pod_To_v1_Pod(pod, v1PodOut, nil); err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	v1PodOutDefaulted := v1PodOut.DeepCopy()
   238  	v1defaults.SetObjectDefaults_Pod(v1PodOutDefaulted)
   239  	if !reflect.DeepEqual(v1PodOut, v1PodOutDefaulted) {
   240  		t.Error(cmp.Diff(v1PodOut, v1PodOutDefaulted))
   241  	}
   242  }
   243  
   244  func TestFetchesUncachedServiceAccount(t *testing.T) {
   245  	ns := "myns"
   246  
   247  	// Build a test client that the admission plugin can use to look up the service account missing from its cache
   248  	client := fake.NewSimpleClientset(&corev1.ServiceAccount{
   249  		ObjectMeta: metav1.ObjectMeta{
   250  			Name:      DefaultServiceAccountName,
   251  			Namespace: ns,
   252  		},
   253  	})
   254  
   255  	admit := NewServiceAccount()
   256  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   257  	admit.SetExternalKubeInformerFactory(informerFactory)
   258  	admit.client = client
   259  
   260  	pod := &api.Pod{}
   261  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   262  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   263  	if err != nil {
   264  		t.Errorf("Unexpected error: %v", err)
   265  	}
   266  	if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
   267  		t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
   268  	}
   269  }
   270  
   271  func TestDeniesInvalidServiceAccount(t *testing.T) {
   272  	ns := "myns"
   273  
   274  	// Build a test client that the admission plugin can use to look up the service account missing from its cache
   275  	client := fake.NewSimpleClientset()
   276  
   277  	admit := NewServiceAccount()
   278  	admit.SetExternalKubeClientSet(client)
   279  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   280  	admit.SetExternalKubeInformerFactory(informerFactory)
   281  
   282  	pod := &api.Pod{}
   283  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   284  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   285  	if err == nil {
   286  		t.Errorf("Expected error for missing service account, got none")
   287  	}
   288  }
   289  
   290  func TestAutomountsAPIToken(t *testing.T) {
   291  
   292  	admit := NewServiceAccount()
   293  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   294  	admit.SetExternalKubeInformerFactory(informerFactory)
   295  	admit.generateName = testGenerateName
   296  	admit.MountServiceAccountToken = true
   297  
   298  	ns := "myns"
   299  	serviceAccountName := DefaultServiceAccountName
   300  	serviceAccountUID := "12345"
   301  
   302  	tokenName := generatedVolumeName
   303  
   304  	expectedVolume := api.Volume{
   305  		Name: tokenName,
   306  		VolumeSource: api.VolumeSource{
   307  			Projected: TokenVolumeSource(),
   308  		},
   309  	}
   310  	expectedVolumeMount := api.VolumeMount{
   311  		Name:      tokenName,
   312  		ReadOnly:  true,
   313  		MountPath: DefaultAPITokenMountPath,
   314  	}
   315  	// Add the default service account for the ns with a token into the cache
   316  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   317  		ObjectMeta: metav1.ObjectMeta{
   318  			Name:      serviceAccountName,
   319  			Namespace: ns,
   320  			UID:       types.UID(serviceAccountUID),
   321  		},
   322  	})
   323  
   324  	pod := &api.Pod{
   325  		Spec: api.PodSpec{
   326  			Containers: []api.Container{
   327  				{},
   328  			},
   329  		},
   330  	}
   331  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   332  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   333  	if err != nil {
   334  		t.Errorf("Unexpected error: %v", err)
   335  	}
   336  	if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
   337  		t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
   338  	}
   339  	if len(pod.Spec.Volumes) != 1 {
   340  		t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes))
   341  	}
   342  	if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) {
   343  		t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0])
   344  	}
   345  	if len(pod.Spec.Containers[0].VolumeMounts) != 1 {
   346  		t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts))
   347  	}
   348  	if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) {
   349  		t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0])
   350  	}
   351  
   352  	// testing InitContainers
   353  	pod = &api.Pod{
   354  		Spec: api.PodSpec{
   355  			InitContainers: []api.Container{
   356  				{},
   357  			},
   358  		},
   359  	}
   360  	attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   361  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   362  		t.Errorf("Unexpected error: %v", err)
   363  	}
   364  	if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
   365  		t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
   366  	}
   367  	if len(pod.Spec.Volumes) != 1 {
   368  		t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes))
   369  	}
   370  	if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) {
   371  		t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0])
   372  	}
   373  	if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 {
   374  		t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts))
   375  	}
   376  	if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) {
   377  		t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0])
   378  	}
   379  }
   380  
   381  func TestRespectsExistingMount(t *testing.T) {
   382  	ns := "myns"
   383  	serviceAccountName := DefaultServiceAccountName
   384  	serviceAccountUID := "12345"
   385  
   386  	expectedVolumeMount := api.VolumeMount{
   387  		Name:      "my-custom-mount",
   388  		ReadOnly:  false,
   389  		MountPath: DefaultAPITokenMountPath,
   390  	}
   391  
   392  	admit := NewServiceAccount()
   393  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   394  	admit.SetExternalKubeInformerFactory(informerFactory)
   395  	admit.MountServiceAccountToken = true
   396  
   397  	// Add the default service account for the ns with a token into the cache
   398  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   399  		ObjectMeta: metav1.ObjectMeta{
   400  			Name:      serviceAccountName,
   401  			Namespace: ns,
   402  			UID:       types.UID(serviceAccountUID),
   403  		},
   404  	})
   405  
   406  	// Define a pod with a container that already mounts a volume at the API token path
   407  	// Admission should respect that
   408  	// Additionally, no volume should be created if no container is going to use it
   409  	pod := &api.Pod{
   410  		Spec: api.PodSpec{
   411  			Containers: []api.Container{
   412  				{
   413  					VolumeMounts: []api.VolumeMount{
   414  						expectedVolumeMount,
   415  					},
   416  				},
   417  			},
   418  		},
   419  	}
   420  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   421  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   422  	if err != nil {
   423  		t.Errorf("Unexpected error: %v", err)
   424  	}
   425  	if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
   426  		t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
   427  	}
   428  	if len(pod.Spec.Volumes) != 0 {
   429  		t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes))
   430  	}
   431  	if len(pod.Spec.Containers[0].VolumeMounts) != 1 {
   432  		t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts))
   433  	}
   434  	if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) {
   435  		t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0])
   436  	}
   437  
   438  	// check init containers
   439  	pod = &api.Pod{
   440  		Spec: api.PodSpec{
   441  			InitContainers: []api.Container{
   442  				{
   443  					VolumeMounts: []api.VolumeMount{
   444  						expectedVolumeMount,
   445  					},
   446  				},
   447  			},
   448  		},
   449  	}
   450  	attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   451  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   452  		t.Errorf("Unexpected error: %v", err)
   453  	}
   454  	if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
   455  		t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
   456  	}
   457  	if len(pod.Spec.Volumes) != 0 {
   458  		t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes))
   459  	}
   460  	if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 {
   461  		t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts))
   462  	}
   463  	if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) {
   464  		t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0])
   465  	}
   466  }
   467  
   468  func TestAllowsReferencedSecret(t *testing.T) {
   469  	ns := "myns"
   470  
   471  	admit := NewServiceAccount()
   472  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   473  	admit.SetExternalKubeInformerFactory(informerFactory)
   474  	admit.LimitSecretReferences = true
   475  
   476  	// Add the default service account for the ns with a secret reference into the cache
   477  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   478  		ObjectMeta: metav1.ObjectMeta{
   479  			Name:      DefaultServiceAccountName,
   480  			Namespace: ns,
   481  		},
   482  		Secrets: []corev1.ObjectReference{
   483  			{Name: "foo"},
   484  		},
   485  	})
   486  
   487  	pod1 := &api.Pod{
   488  		Spec: api.PodSpec{
   489  			Volumes: []api.Volume{
   490  				{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}},
   491  			},
   492  		},
   493  	}
   494  	attrs := admission.NewAttributesRecord(pod1, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   495  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   496  		t.Errorf("Unexpected error: %v", err)
   497  	}
   498  
   499  	pod2 := &api.Pod{
   500  		Spec: api.PodSpec{
   501  			Containers: []api.Container{
   502  				{
   503  					Name: "container-1",
   504  					Env: []api.EnvVar{
   505  						{
   506  							Name: "env-1",
   507  							ValueFrom: &api.EnvVarSource{
   508  								SecretKeyRef: &api.SecretKeySelector{
   509  									LocalObjectReference: api.LocalObjectReference{Name: "foo"},
   510  								},
   511  							},
   512  						},
   513  					},
   514  				},
   515  			},
   516  		},
   517  	}
   518  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   519  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   520  		t.Errorf("Unexpected error: %v", err)
   521  	}
   522  
   523  	pod2 = &api.Pod{
   524  		Spec: api.PodSpec{
   525  			Containers: []api.Container{
   526  				{
   527  					Name: "container-1",
   528  					EnvFrom: []api.EnvFromSource{
   529  						{
   530  							SecretRef: &api.SecretEnvSource{
   531  								LocalObjectReference: api.LocalObjectReference{
   532  									Name: "foo"}}}},
   533  				},
   534  			},
   535  		},
   536  	}
   537  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   538  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   539  		t.Errorf("Unexpected error: %v", err)
   540  	}
   541  
   542  	pod2 = &api.Pod{
   543  		Spec: api.PodSpec{
   544  			InitContainers: []api.Container{
   545  				{
   546  					Name: "container-1",
   547  					Env: []api.EnvVar{
   548  						{
   549  							Name: "env-1",
   550  							ValueFrom: &api.EnvVarSource{
   551  								SecretKeyRef: &api.SecretKeySelector{
   552  									LocalObjectReference: api.LocalObjectReference{Name: "foo"},
   553  								},
   554  							},
   555  						},
   556  					},
   557  				},
   558  			},
   559  		},
   560  	}
   561  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   562  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   563  		t.Errorf("Unexpected error: %v", err)
   564  	}
   565  
   566  	pod2 = &api.Pod{
   567  		Spec: api.PodSpec{
   568  			InitContainers: []api.Container{
   569  				{
   570  					Name: "container-1",
   571  					EnvFrom: []api.EnvFromSource{
   572  						{
   573  							SecretRef: &api.SecretEnvSource{
   574  								LocalObjectReference: api.LocalObjectReference{
   575  									Name: "foo"}}}},
   576  				},
   577  			},
   578  		},
   579  	}
   580  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   581  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   582  		t.Errorf("Unexpected error: %v", err)
   583  	}
   584  
   585  	pod2 = &api.Pod{
   586  		Spec: api.PodSpec{
   587  			ServiceAccountName: DefaultServiceAccountName,
   588  			EphemeralContainers: []api.EphemeralContainer{
   589  				{
   590  					EphemeralContainerCommon: api.EphemeralContainerCommon{
   591  						Name: "container-2",
   592  						Env: []api.EnvVar{
   593  							{
   594  								Name: "env-1",
   595  								ValueFrom: &api.EnvVarSource{
   596  									SecretKeyRef: &api.SecretKeySelector{
   597  										LocalObjectReference: api.LocalObjectReference{Name: "foo"},
   598  									},
   599  								},
   600  							},
   601  						},
   602  					},
   603  				},
   604  			},
   605  		},
   606  	}
   607  	// validate enforces restrictions on secret mounts when operation==create and subresource=='' or operation==update and subresource==ephemeralcontainers"
   608  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil)
   609  	if err := admit.Validate(context.TODO(), attrs, nil); err != nil {
   610  		t.Errorf("Unexpected error: %v", err)
   611  	}
   612  
   613  	pod2 = &api.Pod{
   614  		Spec: api.PodSpec{
   615  			ServiceAccountName: DefaultServiceAccountName,
   616  			EphemeralContainers: []api.EphemeralContainer{
   617  				{
   618  					EphemeralContainerCommon: api.EphemeralContainerCommon{
   619  						Name: "container-2",
   620  						EnvFrom: []api.EnvFromSource{{
   621  							SecretRef: &api.SecretEnvSource{
   622  								LocalObjectReference: api.LocalObjectReference{
   623  									Name: "foo"}}}},
   624  					},
   625  				},
   626  			},
   627  		},
   628  	}
   629  	// validate enforces restrictions on secret mounts when operation==update and subresource==ephemeralcontainers"
   630  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil)
   631  	if err := admit.Validate(context.TODO(), attrs, nil); err != nil {
   632  		t.Errorf("Unexpected error: %v", err)
   633  	}
   634  }
   635  
   636  func TestRejectsUnreferencedSecretVolumes(t *testing.T) {
   637  	ns := "myns"
   638  
   639  	admit := NewServiceAccount()
   640  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   641  	admit.SetExternalKubeInformerFactory(informerFactory)
   642  	admit.LimitSecretReferences = true
   643  
   644  	// Add the default service account for the ns into the cache
   645  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   646  		ObjectMeta: metav1.ObjectMeta{
   647  			Name:      DefaultServiceAccountName,
   648  			Namespace: ns,
   649  		},
   650  	})
   651  
   652  	pod1 := &api.Pod{
   653  		Spec: api.PodSpec{
   654  			Volumes: []api.Volume{
   655  				{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}},
   656  			},
   657  		},
   658  	}
   659  	attrs := admission.NewAttributesRecord(pod1, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   660  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil {
   661  		t.Errorf("Expected rejection for using a secret the service account does not reference")
   662  	}
   663  
   664  	pod2 := &api.Pod{
   665  		Spec: api.PodSpec{
   666  			Containers: []api.Container{
   667  				{
   668  					Name: "container-1",
   669  					Env: []api.EnvVar{
   670  						{
   671  							Name: "env-1",
   672  							ValueFrom: &api.EnvVarSource{
   673  								SecretKeyRef: &api.SecretKeySelector{
   674  									LocalObjectReference: api.LocalObjectReference{Name: "foo"},
   675  								},
   676  							},
   677  						},
   678  					},
   679  				},
   680  			},
   681  		},
   682  	}
   683  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   684  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") {
   685  		t.Errorf("Unexpected error: %v", err)
   686  	}
   687  
   688  	pod2 = &api.Pod{
   689  		Spec: api.PodSpec{
   690  			Containers: []api.Container{
   691  				{
   692  					Name: "container-1",
   693  					EnvFrom: []api.EnvFromSource{
   694  						{
   695  							SecretRef: &api.SecretEnvSource{
   696  								LocalObjectReference: api.LocalObjectReference{
   697  									Name: "foo"}}}},
   698  				},
   699  			},
   700  		},
   701  	}
   702  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   703  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envFrom") {
   704  		t.Errorf("Unexpected error: %v", err)
   705  	}
   706  
   707  	pod2 = &api.Pod{
   708  		Spec: api.PodSpec{
   709  			ServiceAccountName: DefaultServiceAccountName,
   710  			InitContainers: []api.Container{
   711  				{
   712  					Name: "container-1",
   713  					Env: []api.EnvVar{
   714  						{
   715  							Name: "env-1",
   716  							ValueFrom: &api.EnvVarSource{
   717  								SecretKeyRef: &api.SecretKeySelector{
   718  									LocalObjectReference: api.LocalObjectReference{Name: "foo"},
   719  								},
   720  							},
   721  						},
   722  					},
   723  				},
   724  			},
   725  		},
   726  	}
   727  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)
   728  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   729  		t.Errorf("admit only enforces restrictions on secret mounts when operation==create. Unexpected error: %v", err)
   730  	}
   731  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   732  	if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") {
   733  		t.Errorf("validate only enforces restrictions on secret mounts when operation==create and subresource==''. Unexpected error: %v", err)
   734  	}
   735  
   736  	pod2 = &api.Pod{
   737  		Spec: api.PodSpec{
   738  			ServiceAccountName: DefaultServiceAccountName,
   739  			InitContainers: []api.Container{
   740  				{
   741  					Name: "container-1",
   742  					EnvFrom: []api.EnvFromSource{
   743  						{
   744  							SecretRef: &api.SecretEnvSource{
   745  								LocalObjectReference: api.LocalObjectReference{
   746  									Name: "foo"}}}},
   747  				},
   748  			},
   749  		},
   750  	}
   751  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)
   752  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   753  		t.Errorf("admit only enforces restrictions on secret mounts when operation==create. Unexpected error: %v", err)
   754  	}
   755  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   756  	if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envFrom") {
   757  		t.Errorf("validate only enforces restrictions on secret mounts when operation==create and subresource==''. Unexpected error: %v", err)
   758  	}
   759  
   760  	pod2 = &api.Pod{
   761  		Spec: api.PodSpec{
   762  			ServiceAccountName: DefaultServiceAccountName,
   763  			EphemeralContainers: []api.EphemeralContainer{
   764  				{
   765  					EphemeralContainerCommon: api.EphemeralContainerCommon{
   766  						Name: "container-2",
   767  						Env: []api.EnvVar{
   768  							{
   769  								Name: "env-1",
   770  								ValueFrom: &api.EnvVarSource{
   771  									SecretKeyRef: &api.SecretKeySelector{
   772  										LocalObjectReference: api.LocalObjectReference{Name: "foo"},
   773  									},
   774  								},
   775  							},
   776  						},
   777  					},
   778  				},
   779  			},
   780  		},
   781  	}
   782  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil)
   783  	if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
   784  		t.Errorf("admit only enforces restrictions on secret mounts when operation==create and subresource==''. Unexpected error: %v", err)
   785  	}
   786  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil)
   787  	if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") {
   788  		t.Errorf("validate enforces restrictions on secret mounts when operation==update and subresource==ephemeralcontainers. Unexpected error: %v", err)
   789  	}
   790  
   791  	pod2 = &api.Pod{
   792  		Spec: api.PodSpec{
   793  			ServiceAccountName: DefaultServiceAccountName,
   794  			EphemeralContainers: []api.EphemeralContainer{
   795  				{
   796  					EphemeralContainerCommon: api.EphemeralContainerCommon{
   797  						Name: "container-2",
   798  						EnvFrom: []api.EnvFromSource{{
   799  							SecretRef: &api.SecretEnvSource{
   800  								LocalObjectReference: api.LocalObjectReference{
   801  									Name: "foo"}}}},
   802  					},
   803  				},
   804  			},
   805  		},
   806  	}
   807  	attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil)
   808  	if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envFrom") {
   809  		t.Errorf("validate enforces restrictions on secret mounts when operation==update and subresource==ephemeralcontainers. Unexpected error: %v", err)
   810  	}
   811  }
   812  
   813  func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) {
   814  	ns := "myns"
   815  
   816  	admit := NewServiceAccount()
   817  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   818  	admit.SetExternalKubeInformerFactory(informerFactory)
   819  	admit.LimitSecretReferences = false
   820  
   821  	// Add the default service account for the ns into the cache
   822  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   823  		ObjectMeta: metav1.ObjectMeta{
   824  			Name:        DefaultServiceAccountName,
   825  			Namespace:   ns,
   826  			Annotations: map[string]string{EnforceMountableSecretsAnnotation: "true"},
   827  		},
   828  	})
   829  
   830  	pod := &api.Pod{
   831  		Spec: api.PodSpec{
   832  			Volumes: []api.Volume{
   833  				{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}},
   834  			},
   835  		},
   836  	}
   837  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   838  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   839  	if err == nil {
   840  		t.Errorf("Expected rejection for using a secret the service account does not reference")
   841  	}
   842  }
   843  
   844  func TestAllowsReferencedImagePullSecrets(t *testing.T) {
   845  	ns := "myns"
   846  
   847  	admit := NewServiceAccount()
   848  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   849  	admit.SetExternalKubeInformerFactory(informerFactory)
   850  	admit.LimitSecretReferences = true
   851  
   852  	// Add the default service account for the ns with a secret reference into the cache
   853  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   854  		ObjectMeta: metav1.ObjectMeta{
   855  			Name:      DefaultServiceAccountName,
   856  			Namespace: ns,
   857  		},
   858  		ImagePullSecrets: []corev1.LocalObjectReference{
   859  			{Name: "foo"},
   860  		},
   861  	})
   862  
   863  	pod := &api.Pod{
   864  		Spec: api.PodSpec{
   865  			ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}},
   866  		},
   867  	}
   868  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   869  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   870  	if err != nil {
   871  		t.Errorf("Unexpected error: %v", err)
   872  	}
   873  }
   874  
   875  func TestRejectsUnreferencedImagePullSecrets(t *testing.T) {
   876  	ns := "myns"
   877  
   878  	admit := NewServiceAccount()
   879  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   880  	admit.SetExternalKubeInformerFactory(informerFactory)
   881  	admit.LimitSecretReferences = true
   882  
   883  	// Add the default service account for the ns into the cache
   884  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   885  		ObjectMeta: metav1.ObjectMeta{
   886  			Name:      DefaultServiceAccountName,
   887  			Namespace: ns,
   888  		},
   889  	})
   890  
   891  	pod := &api.Pod{
   892  		Spec: api.PodSpec{
   893  			ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}},
   894  		},
   895  	}
   896  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   897  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   898  	if err == nil {
   899  		t.Errorf("Expected rejection for using a secret the service account does not reference")
   900  	}
   901  }
   902  
   903  func TestDoNotAddImagePullSecrets(t *testing.T) {
   904  	ns := "myns"
   905  
   906  	admit := NewServiceAccount()
   907  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   908  	admit.SetExternalKubeInformerFactory(informerFactory)
   909  	admit.LimitSecretReferences = true
   910  
   911  	// Add the default service account for the ns with a secret reference into the cache
   912  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
   913  		ObjectMeta: metav1.ObjectMeta{
   914  			Name:      DefaultServiceAccountName,
   915  			Namespace: ns,
   916  		},
   917  		ImagePullSecrets: []corev1.LocalObjectReference{
   918  			{Name: "foo"},
   919  			{Name: "bar"},
   920  		},
   921  	})
   922  
   923  	pod := &api.Pod{
   924  		Spec: api.PodSpec{
   925  			ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}},
   926  		},
   927  	}
   928  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   929  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   930  	if err != nil {
   931  		t.Errorf("Unexpected error: %v", err)
   932  	}
   933  
   934  	if len(pod.Spec.ImagePullSecrets) != 1 || pod.Spec.ImagePullSecrets[0].Name != "foo" {
   935  		t.Errorf("unexpected image pull secrets: %v", pod.Spec.ImagePullSecrets)
   936  	}
   937  }
   938  
   939  func TestAddImagePullSecrets(t *testing.T) {
   940  	ns := "myns"
   941  
   942  	admit := NewServiceAccount()
   943  	informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   944  	admit.SetExternalKubeInformerFactory(informerFactory)
   945  	admit.LimitSecretReferences = true
   946  
   947  	sa := &corev1.ServiceAccount{
   948  		ObjectMeta: metav1.ObjectMeta{
   949  			Name:      DefaultServiceAccountName,
   950  			Namespace: ns,
   951  		},
   952  		ImagePullSecrets: []corev1.LocalObjectReference{
   953  			{Name: "foo"},
   954  			{Name: "bar"},
   955  		},
   956  	}
   957  	originalSA := sa.DeepCopy()
   958  	expected := []api.LocalObjectReference{
   959  		{Name: "foo"},
   960  		{Name: "bar"},
   961  	}
   962  	// Add the default service account for the ns with a secret reference into the cache
   963  	informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(sa)
   964  
   965  	pod := &api.Pod{}
   966  	attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
   967  	err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
   968  	if err != nil {
   969  		t.Errorf("Unexpected error: %v", err)
   970  	}
   971  
   972  	assert.EqualValues(t, expected, pod.Spec.ImagePullSecrets, "expected %v, got %v", expected, pod.Spec.ImagePullSecrets)
   973  
   974  	pod.Spec.ImagePullSecrets[1] = api.LocalObjectReference{Name: "baz"}
   975  	if !reflect.DeepEqual(originalSA, sa) {
   976  		t.Errorf("accidentally mutated the ServiceAccount.ImagePullSecrets: %v", sa.ImagePullSecrets)
   977  	}
   978  }
   979  
   980  func testGenerateName(n string) string {
   981  	return n + "abc123"
   982  }
   983  
   984  var generatedVolumeName = testGenerateName(ServiceAccountVolumeName + "-")
   985  

View as plain text