1
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
31
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
105
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
172
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
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
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