...

Source file src/k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_sandbox_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/kuberuntime

     1  /*
     2  Copyright 2016 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 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  // TestCreatePodSandbox tests creating sandbox and its corresponding pod log directory.
    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  		// Check pod logs root directory is created.
    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  // TestCreatePodSandbox_RuntimeClass tests creating sandbox with RuntimeClasses enabled.
   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