1
16
17 package kuberuntime
18
19 import (
20 "context"
21 "fmt"
22 "os"
23 "path/filepath"
24 "testing"
25
26 "github.com/stretchr/testify/assert"
27 "github.com/stretchr/testify/require"
28 v1 "k8s.io/api/core/v1"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 utilfeature "k8s.io/apiserver/pkg/util/feature"
31 featuregatetesting "k8s.io/component-base/featuregate/testing"
32 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
33 "k8s.io/kubernetes/pkg/features"
34 containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
35 "k8s.io/kubernetes/pkg/kubelet/runtimeclass"
36 rctest "k8s.io/kubernetes/pkg/kubelet/runtimeclass/testing"
37 "k8s.io/utils/pointer"
38 )
39
40 const testPodLogsDirectory = "/var/log/pods"
41
42 func TestGeneratePodSandboxConfig(t *testing.T) {
43 _, _, m, err := createTestRuntimeManager()
44 require.NoError(t, err)
45 pod := newTestPod()
46
47 expectedLogDirectory := filepath.Join(testPodLogsDirectory, pod.Namespace+"_"+pod.Name+"_12345678")
48 expectedLabels := map[string]string{
49 "io.kubernetes.pod.name": pod.Name,
50 "io.kubernetes.pod.namespace": pod.Namespace,
51 "io.kubernetes.pod.uid": string(pod.UID),
52 }
53 expectedMetadata := &runtimeapi.PodSandboxMetadata{
54 Name: pod.Name,
55 Namespace: pod.Namespace,
56 Uid: string(pod.UID),
57 Attempt: uint32(1),
58 }
59 expectedPortMappings := []*runtimeapi.PortMapping{
60 {
61 HostPort: 8080,
62 },
63 }
64
65 podSandboxConfig, err := m.generatePodSandboxConfig(pod, 1)
66 assert.NoError(t, err)
67 assert.Equal(t, expectedLabels, podSandboxConfig.Labels)
68 assert.Equal(t, expectedLogDirectory, podSandboxConfig.LogDirectory)
69 assert.Equal(t, expectedMetadata, podSandboxConfig.Metadata)
70 assert.Equal(t, expectedPortMappings, podSandboxConfig.PortMappings)
71 }
72
73
74 func TestCreatePodSandbox(t *testing.T) {
75 ctx := context.Background()
76 fakeRuntime, _, m, err := createTestRuntimeManager()
77 require.NoError(t, err)
78 pod := newTestPod()
79
80 fakeOS := m.osInterface.(*containertest.FakeOS)
81 fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error {
82
83 assert.Equal(t, filepath.Join(testPodLogsDirectory, pod.Namespace+"_"+pod.Name+"_12345678"), path)
84 assert.Equal(t, os.FileMode(0755), perm)
85 return nil
86 }
87 id, _, err := m.createPodSandbox(ctx, pod, 1)
88 assert.NoError(t, err)
89 assert.Contains(t, fakeRuntime.Called, "RunPodSandbox")
90 sandboxes, err := fakeRuntime.ListPodSandbox(ctx, &runtimeapi.PodSandboxFilter{Id: id})
91 assert.NoError(t, err)
92 assert.Equal(t, len(sandboxes), 1)
93 assert.Equal(t, sandboxes[0].Id, fmt.Sprintf("%s_%s_%s_1", pod.Name, pod.Namespace, pod.UID))
94 assert.Equal(t, sandboxes[0].State, runtimeapi.PodSandboxState_SANDBOX_READY)
95 }
96
97 func TestGeneratePodSandboxLinuxConfigSeccomp(t *testing.T) {
98 _, _, m, err := createTestRuntimeManager()
99 require.NoError(t, err)
100
101 tests := []struct {
102 description string
103 pod *v1.Pod
104 expectedProfile v1.SeccompProfileType
105 }{
106 {
107 description: "no seccomp defined at pod level should return runtime/default",
108 pod: newSeccompPod(nil, nil, "", "runtime/default"),
109 expectedProfile: v1.SeccompProfileTypeRuntimeDefault,
110 },
111 {
112 description: "seccomp field defined at pod level should not be honoured",
113 pod: newSeccompPod(&v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}, nil, "", ""),
114 expectedProfile: v1.SeccompProfileTypeRuntimeDefault,
115 },
116 {
117 description: "seccomp field defined at container level should not be honoured",
118 pod: newSeccompPod(nil, &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}, "", ""),
119 expectedProfile: v1.SeccompProfileTypeRuntimeDefault,
120 },
121 {
122 description: "seccomp annotation defined at pod level should not be honoured",
123 pod: newSeccompPod(nil, nil, "unconfined", ""),
124 expectedProfile: v1.SeccompProfileTypeRuntimeDefault,
125 },
126 {
127 description: "seccomp annotation defined at container level should not be honoured",
128 pod: newSeccompPod(nil, nil, "", "unconfined"),
129 expectedProfile: v1.SeccompProfileTypeRuntimeDefault,
130 },
131 }
132
133 for i, test := range tests {
134 config, _ := m.generatePodSandboxLinuxConfig(test.pod)
135 actualProfile := config.SecurityContext.Seccomp.ProfileType.String()
136 assert.EqualValues(t, test.expectedProfile, actualProfile, "TestCase[%d]: %s", i, test.description)
137 }
138 }
139
140
141 func TestCreatePodSandbox_RuntimeClass(t *testing.T) {
142 ctx := context.Background()
143 rcm := runtimeclass.NewManager(rctest.NewPopulatedClient())
144 defer rctest.StartManagerSync(rcm)()
145
146 fakeRuntime, _, m, err := createTestRuntimeManager()
147 require.NoError(t, err)
148 m.runtimeClassManager = rcm
149
150 tests := map[string]struct {
151 rcn *string
152 expectedHandler string
153 expectError bool
154 }{
155 "unspecified RuntimeClass": {rcn: nil, expectedHandler: ""},
156 "valid RuntimeClass": {rcn: pointer.String(rctest.SandboxRuntimeClass), expectedHandler: rctest.SandboxRuntimeHandler},
157 "missing RuntimeClass": {rcn: pointer.String("phantom"), expectError: true},
158 }
159 for name, test := range tests {
160 t.Run(name, func(t *testing.T) {
161 fakeRuntime.Called = []string{}
162 pod := newTestPod()
163 pod.Spec.RuntimeClassName = test.rcn
164
165 id, _, err := m.createPodSandbox(ctx, pod, 1)
166 if test.expectError {
167 assert.Error(t, err)
168 } else {
169 assert.NoError(t, err)
170 assert.Contains(t, fakeRuntime.Called, "RunPodSandbox")
171 assert.Equal(t, test.expectedHandler, fakeRuntime.Sandboxes[id].RuntimeHandler)
172 }
173 })
174 }
175 }
176
177 func newTestPod() *v1.Pod {
178 return &v1.Pod{
179 ObjectMeta: metav1.ObjectMeta{
180 UID: "12345678",
181 Name: "bar",
182 Namespace: "new",
183 },
184 Spec: v1.PodSpec{
185 Containers: []v1.Container{
186 {
187 Name: "foo",
188 Image: "busybox",
189 ImagePullPolicy: v1.PullIfNotPresent,
190 Ports: []v1.ContainerPort{
191 {
192 HostPort: 8080,
193 },
194 },
195 },
196 },
197 },
198 }
199 }
200
201 func newSeccompPod(podFieldProfile, containerFieldProfile *v1.SeccompProfile, podAnnotationProfile, containerAnnotationProfile string) *v1.Pod {
202 pod := newTestPod()
203 if podFieldProfile != nil {
204 pod.Spec.SecurityContext = &v1.PodSecurityContext{
205 SeccompProfile: podFieldProfile,
206 }
207 }
208 if containerFieldProfile != nil {
209 pod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{
210 SeccompProfile: containerFieldProfile,
211 }
212 }
213 return pod
214 }
215
216 func TestGeneratePodSandboxWindowsConfig_HostProcess(t *testing.T) {
217 _, _, m, err := createTestRuntimeManager()
218 require.NoError(t, err)
219
220 const containerName = "container"
221 gmsaCreds := "gmsa-creds"
222 userName := "SYSTEM"
223 trueVar := true
224 falseVar := false
225
226 testCases := []struct {
227 name string
228 podSpec *v1.PodSpec
229 expectedWindowsConfig *runtimeapi.WindowsPodSandboxConfig
230 expectedError error
231 }{
232 {
233 name: "Empty PodSecurityContext",
234 podSpec: &v1.PodSpec{
235 Containers: []v1.Container{{
236 Name: containerName,
237 }},
238 },
239 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
240 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{},
241 },
242 expectedError: nil,
243 },
244 {
245 name: "GMSACredentialSpec in PodSecurityContext",
246 podSpec: &v1.PodSpec{
247 SecurityContext: &v1.PodSecurityContext{
248 WindowsOptions: &v1.WindowsSecurityContextOptions{
249 GMSACredentialSpec: &gmsaCreds,
250 },
251 },
252 Containers: []v1.Container{{
253 Name: containerName,
254 }},
255 },
256 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
257 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{
258 CredentialSpec: "gmsa-creds",
259 },
260 },
261 expectedError: nil,
262 },
263 {
264 name: "RunAsUserName in PodSecurityContext",
265 podSpec: &v1.PodSpec{
266 SecurityContext: &v1.PodSecurityContext{
267 WindowsOptions: &v1.WindowsSecurityContextOptions{
268 RunAsUserName: &userName,
269 },
270 },
271 Containers: []v1.Container{{
272 Name: containerName,
273 }},
274 },
275 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
276 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{
277 RunAsUsername: "SYSTEM",
278 },
279 },
280 expectedError: nil,
281 },
282 {
283 name: "Pod with HostProcess containers and non-HostProcess containers",
284 podSpec: &v1.PodSpec{
285 SecurityContext: &v1.PodSecurityContext{
286 WindowsOptions: &v1.WindowsSecurityContextOptions{
287 HostProcess: &trueVar,
288 },
289 },
290 Containers: []v1.Container{{
291 Name: containerName,
292 }, {
293 Name: containerName,
294 SecurityContext: &v1.SecurityContext{
295 WindowsOptions: &v1.WindowsSecurityContextOptions{
296 HostProcess: &falseVar,
297 },
298 },
299 }},
300 },
301 expectedWindowsConfig: nil,
302 expectedError: fmt.Errorf("pod must not contain both HostProcess and non-HostProcess containers"),
303 },
304 {
305 name: "Pod with HostProcess containers and HostNetwork not set",
306 podSpec: &v1.PodSpec{
307 SecurityContext: &v1.PodSecurityContext{
308 WindowsOptions: &v1.WindowsSecurityContextOptions{
309 HostProcess: &trueVar,
310 },
311 },
312 Containers: []v1.Container{{
313 Name: containerName,
314 }},
315 },
316 expectedWindowsConfig: nil,
317 expectedError: fmt.Errorf("hostNetwork is required if Pod contains HostProcess containers"),
318 },
319 {
320 name: "Pod with HostProcess containers and HostNetwork set",
321 podSpec: &v1.PodSpec{
322 HostNetwork: true,
323 SecurityContext: &v1.PodSecurityContext{
324 WindowsOptions: &v1.WindowsSecurityContextOptions{
325 HostProcess: &trueVar,
326 },
327 },
328 Containers: []v1.Container{{
329 Name: containerName,
330 }},
331 },
332 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
333 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{
334 HostProcess: true,
335 },
336 },
337 expectedError: nil,
338 },
339 {
340 name: "Pod's WindowsOptions.HostProcess set to false and pod has HostProcess containers",
341 podSpec: &v1.PodSpec{
342 HostNetwork: true,
343 SecurityContext: &v1.PodSecurityContext{
344 WindowsOptions: &v1.WindowsSecurityContextOptions{
345 HostProcess: &falseVar,
346 },
347 },
348 Containers: []v1.Container{{
349 Name: containerName,
350 SecurityContext: &v1.SecurityContext{
351 WindowsOptions: &v1.WindowsSecurityContextOptions{
352 HostProcess: &trueVar,
353 },
354 },
355 }},
356 },
357 expectedWindowsConfig: nil,
358 expectedError: fmt.Errorf("pod must not contain any HostProcess containers if Pod's WindowsOptions.HostProcess is set to false"),
359 },
360 {
361 name: "Pod's security context doesn't specify HostProcess containers but Container's security context does",
362 podSpec: &v1.PodSpec{
363 HostNetwork: true,
364 Containers: []v1.Container{{
365 Name: containerName,
366 SecurityContext: &v1.SecurityContext{
367 WindowsOptions: &v1.WindowsSecurityContextOptions{
368 HostProcess: &trueVar,
369 },
370 },
371 }},
372 },
373 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
374 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{
375 HostProcess: true,
376 },
377 },
378 expectedError: nil,
379 },
380 }
381
382 for _, testCase := range testCases {
383 t.Run(testCase.name, func(t *testing.T) {
384 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsHostNetwork, false)()
385 pod := &v1.Pod{}
386 pod.Spec = *testCase.podSpec
387
388 wc, err := m.generatePodSandboxWindowsConfig(pod)
389
390 assert.Equal(t, testCase.expectedWindowsConfig, wc)
391 assert.Equal(t, testCase.expectedError, err)
392 })
393 }
394 }
395
396 func TestGeneratePodSandboxWindowsConfig_HostNetwork(t *testing.T) {
397 _, _, m, err := createTestRuntimeManager()
398 require.NoError(t, err)
399
400 const containerName = "container"
401
402 testCases := []struct {
403 name string
404 hostNetworkFeatureEnabled bool
405 podSpec *v1.PodSpec
406 expectedWindowsConfig *runtimeapi.WindowsPodSandboxConfig
407 }{
408 {
409 name: "feature disabled, hostNetwork=false",
410 hostNetworkFeatureEnabled: false,
411 podSpec: &v1.PodSpec{
412 HostNetwork: false,
413 Containers: []v1.Container{{Name: containerName}},
414 },
415 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
416 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{},
417 },
418 },
419 {
420 name: "feature disabled, hostNetwork=true",
421 hostNetworkFeatureEnabled: false,
422 podSpec: &v1.PodSpec{
423 HostNetwork: true,
424 Containers: []v1.Container{{Name: containerName}},
425 },
426 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
427 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{},
428 }},
429 {
430 name: "feature enabled, hostNetwork=false",
431 hostNetworkFeatureEnabled: true,
432 podSpec: &v1.PodSpec{
433 HostNetwork: false,
434 Containers: []v1.Container{{Name: containerName}},
435 },
436 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
437 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{
438 NamespaceOptions: &runtimeapi.WindowsNamespaceOption{
439 Network: runtimeapi.NamespaceMode_POD,
440 },
441 },
442 },
443 },
444 {
445 name: "feature enabled, hostNetwork=true",
446 hostNetworkFeatureEnabled: true,
447 podSpec: &v1.PodSpec{
448 HostNetwork: true,
449 Containers: []v1.Container{{Name: containerName}},
450 },
451 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{
452 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{
453 NamespaceOptions: &runtimeapi.WindowsNamespaceOption{
454 Network: runtimeapi.NamespaceMode_NODE,
455 },
456 },
457 },
458 },
459 }
460
461 for _, testCase := range testCases {
462 t.Run(testCase.name, func(t *testing.T) {
463 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsHostNetwork, testCase.hostNetworkFeatureEnabled)()
464 pod := &v1.Pod{}
465 pod.Spec = *testCase.podSpec
466
467 wc, err := m.generatePodSandboxWindowsConfig(pod)
468
469 assert.Equal(t, testCase.expectedWindowsConfig, wc)
470 assert.Equal(t, nil, err)
471 })
472 }
473 }
474
View as plain text