...

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

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

     1  /*
     2  Copyright 2015 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 alwayspullimages
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apiserver/pkg/admission"
    26  	admissiontesting "k8s.io/apiserver/pkg/admission/testing"
    27  	api "k8s.io/kubernetes/pkg/apis/core"
    28  )
    29  
    30  // TestAdmission verifies all create requests for pods result in every container's image pull policy
    31  // set to Always
    32  func TestAdmission(t *testing.T) {
    33  	namespace := "test"
    34  	handler := admissiontesting.WithReinvocationTesting(t, &AlwaysPullImages{})
    35  	pod := api.Pod{
    36  		ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: namespace},
    37  		Spec: api.PodSpec{
    38  			InitContainers: []api.Container{
    39  				{Name: "init1", Image: "image"},
    40  				{Name: "init2", Image: "image", ImagePullPolicy: api.PullNever},
    41  				{Name: "init3", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
    42  				{Name: "init4", Image: "image", ImagePullPolicy: api.PullAlways},
    43  			},
    44  			Containers: []api.Container{
    45  				{Name: "ctr1", Image: "image"},
    46  				{Name: "ctr2", Image: "image", ImagePullPolicy: api.PullNever},
    47  				{Name: "ctr3", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
    48  				{Name: "ctr4", Image: "image", ImagePullPolicy: api.PullAlways},
    49  			},
    50  		},
    51  	}
    52  	err := handler.Admit(context.TODO(), admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
    53  	if err != nil {
    54  		t.Errorf("Unexpected error returned from admission handler")
    55  	}
    56  	for _, c := range pod.Spec.InitContainers {
    57  		if c.ImagePullPolicy != api.PullAlways {
    58  			t.Errorf("Container %v: expected pull always, got %v", c, c.ImagePullPolicy)
    59  		}
    60  	}
    61  	for _, c := range pod.Spec.Containers {
    62  		if c.ImagePullPolicy != api.PullAlways {
    63  			t.Errorf("Container %v: expected pull always, got %v", c, c.ImagePullPolicy)
    64  		}
    65  	}
    66  }
    67  
    68  func TestValidate(t *testing.T) {
    69  	namespace := "test"
    70  	handler := &AlwaysPullImages{}
    71  	pod := api.Pod{
    72  		ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: namespace},
    73  		Spec: api.PodSpec{
    74  			InitContainers: []api.Container{
    75  				{Name: "init1", Image: "image"},
    76  				{Name: "init2", Image: "image", ImagePullPolicy: api.PullNever},
    77  				{Name: "init3", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
    78  				{Name: "init4", Image: "image", ImagePullPolicy: api.PullAlways},
    79  			},
    80  			Containers: []api.Container{
    81  				{Name: "ctr1", Image: "image"},
    82  				{Name: "ctr2", Image: "image", ImagePullPolicy: api.PullNever},
    83  				{Name: "ctr3", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
    84  				{Name: "ctr4", Image: "image", ImagePullPolicy: api.PullAlways},
    85  			},
    86  		},
    87  	}
    88  	expectedError := `[` +
    89  		`pods "123" is forbidden: spec.initContainers[0].imagePullPolicy: Unsupported value: "": supported values: "Always", ` +
    90  		`pods "123" is forbidden: spec.initContainers[1].imagePullPolicy: Unsupported value: "Never": supported values: "Always", ` +
    91  		`pods "123" is forbidden: spec.initContainers[2].imagePullPolicy: Unsupported value: "IfNotPresent": supported values: "Always", ` +
    92  		`pods "123" is forbidden: spec.containers[0].imagePullPolicy: Unsupported value: "": supported values: "Always", ` +
    93  		`pods "123" is forbidden: spec.containers[1].imagePullPolicy: Unsupported value: "Never": supported values: "Always", ` +
    94  		`pods "123" is forbidden: spec.containers[2].imagePullPolicy: Unsupported value: "IfNotPresent": supported values: "Always"]`
    95  	err := handler.Validate(context.TODO(), admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
    96  	if err == nil {
    97  		t.Fatal("missing expected error")
    98  	}
    99  	if err.Error() != expectedError {
   100  		t.Fatal(err)
   101  	}
   102  }
   103  
   104  // TestOtherResources ensures that this admission controller is a no-op for other resources,
   105  // subresources, and non-pods.
   106  func TestOtherResources(t *testing.T) {
   107  	namespace := "testnamespace"
   108  	name := "testname"
   109  	pod := &api.Pod{
   110  		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
   111  		Spec: api.PodSpec{
   112  			Containers: []api.Container{
   113  				{Name: "ctr2", Image: "image", ImagePullPolicy: api.PullNever},
   114  			},
   115  		},
   116  	}
   117  	tests := []struct {
   118  		name        string
   119  		kind        string
   120  		resource    string
   121  		subresource string
   122  		object      runtime.Object
   123  		expectError bool
   124  	}{
   125  		{
   126  			name:     "non-pod resource",
   127  			kind:     "Foo",
   128  			resource: "foos",
   129  			object:   pod,
   130  		},
   131  		{
   132  			name:        "pod subresource",
   133  			kind:        "Pod",
   134  			resource:    "pods",
   135  			subresource: "exec",
   136  			object:      pod,
   137  		},
   138  		{
   139  			name:        "non-pod object",
   140  			kind:        "Pod",
   141  			resource:    "pods",
   142  			object:      &api.Service{},
   143  			expectError: true,
   144  		},
   145  	}
   146  
   147  	for _, tc := range tests {
   148  		handler := admissiontesting.WithReinvocationTesting(t, &AlwaysPullImages{})
   149  
   150  		err := handler.Admit(context.TODO(), admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, &metav1.CreateOptions{}, false, nil), nil)
   151  
   152  		if tc.expectError {
   153  			if err == nil {
   154  				t.Errorf("%s: unexpected nil error", tc.name)
   155  			}
   156  			continue
   157  		}
   158  
   159  		if err != nil {
   160  			t.Errorf("%s: unexpected error: %v", tc.name, err)
   161  			continue
   162  		}
   163  
   164  		if e, a := api.PullNever, pod.Spec.Containers[0].ImagePullPolicy; e != a {
   165  			t.Errorf("%s: image pull policy was changed to %s", tc.name, a)
   166  		}
   167  	}
   168  
   169  }
   170  
   171  // TestUpdatePod ensures that this admission controller is a no-op for update pod if no
   172  // images were changed in the new pod spec.
   173  func TestUpdatePod(t *testing.T) {
   174  	namespace := "testnamespace"
   175  	name := "testname"
   176  	oldPod := &api.Pod{
   177  		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
   178  		Spec: api.PodSpec{
   179  			Containers: []api.Container{
   180  				{Name: "ctr2", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
   181  			},
   182  		},
   183  	}
   184  	// only add new annotation
   185  	pod := &api.Pod{
   186  		ObjectMeta: metav1.ObjectMeta{
   187  			Name:      name,
   188  			Namespace: namespace,
   189  			Annotations: map[string]string{
   190  				"test": "test",
   191  			},
   192  		},
   193  		Spec: api.PodSpec{
   194  			Containers: []api.Container{
   195  				{Name: "ctr2", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
   196  			},
   197  		},
   198  	}
   199  	// add new label and change image
   200  	podWithNewImage := &api.Pod{
   201  		ObjectMeta: metav1.ObjectMeta{
   202  			Name:      name,
   203  			Namespace: namespace,
   204  			Annotations: map[string]string{
   205  				"test": "test",
   206  			},
   207  		},
   208  		Spec: api.PodSpec{
   209  			Containers: []api.Container{
   210  				{Name: "ctr2", Image: "image2", ImagePullPolicy: api.PullIfNotPresent},
   211  			},
   212  		},
   213  	}
   214  	tests := []struct {
   215  		name         string
   216  		kind         string
   217  		resource     string
   218  		subresource  string
   219  		object       runtime.Object
   220  		oldObject    runtime.Object
   221  		expectError  bool
   222  		expectIgnore bool
   223  	}{
   224  		{
   225  			name:         "update IfNotPresent pod annotations",
   226  			kind:         "Pod",
   227  			resource:     "pods",
   228  			subresource:  "finalizers",
   229  			object:       pod,
   230  			oldObject:    oldPod,
   231  			expectIgnore: true,
   232  		},
   233  		{
   234  			name:        "update IfNotPresent pod image",
   235  			kind:        "Pod",
   236  			resource:    "pods",
   237  			subresource: "finalizers",
   238  			object:      podWithNewImage,
   239  			oldObject:   oldPod,
   240  		},
   241  	}
   242  
   243  	for _, tc := range tests {
   244  		handler := admissiontesting.WithReinvocationTesting(t, &AlwaysPullImages{})
   245  
   246  		err := handler.Admit(context.TODO(), admission.NewAttributesRecord(tc.object, tc.oldObject, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, &metav1.UpdateOptions{}, false, nil), nil)
   247  
   248  		if tc.expectError {
   249  			if err == nil {
   250  				t.Errorf("%s: unexpected nil error", tc.name)
   251  			}
   252  			continue
   253  		}
   254  		if tc.expectIgnore {
   255  			if e, a := api.PullIfNotPresent, pod.Spec.Containers[0].ImagePullPolicy; e != a {
   256  				t.Errorf("%s: image pull policy was changed to %s", tc.name, a)
   257  			}
   258  			continue
   259  		}
   260  
   261  		if err != nil {
   262  			t.Errorf("%s: unexpected error: %v", tc.name, err)
   263  			continue
   264  		}
   265  
   266  	}
   267  
   268  }
   269  

View as plain text