123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- // +build linux
- /*
- Copyright 2018 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package kuberuntime
- import (
- "reflect"
- "testing"
- "github.com/google/go-cmp/cmp"
- cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
- "github.com/stretchr/testify/assert"
- v1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/resource"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- utilfeature "k8s.io/apiserver/pkg/util/feature"
- featuregatetesting "k8s.io/component-base/featuregate/testing"
- runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
- "k8s.io/kubernetes/pkg/features"
- kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
- )
- func makeExpectedConfig(m *kubeGenericRuntimeManager, pod *v1.Pod, containerIndex int) *runtimeapi.ContainerConfig {
- container := &pod.Spec.Containers[containerIndex]
- podIP := ""
- restartCount := 0
- opts, _, _ := m.runtimeHelper.GenerateRunContainerOptions(pod, container, podIP, []string{podIP})
- containerLogsPath := buildContainerLogsPath(container.Name, restartCount)
- restartCountUint32 := uint32(restartCount)
- envs := make([]*runtimeapi.KeyValue, len(opts.Envs))
- expectedConfig := &runtimeapi.ContainerConfig{
- Metadata: &runtimeapi.ContainerMetadata{
- Name: container.Name,
- Attempt: restartCountUint32,
- },
- Image: &runtimeapi.ImageSpec{Image: container.Image},
- Command: container.Command,
- Args: []string(nil),
- WorkingDir: container.WorkingDir,
- Labels: newContainerLabels(container, pod),
- Annotations: newContainerAnnotations(container, pod, restartCount, opts),
- Devices: makeDevices(opts),
- Mounts: m.makeMounts(opts, container),
- LogPath: containerLogsPath,
- Stdin: container.Stdin,
- StdinOnce: container.StdinOnce,
- Tty: container.TTY,
- Linux: m.generateLinuxContainerConfig(container, pod, new(int64), "", nil),
- Envs: envs,
- }
- return expectedConfig
- }
- func TestGenerateContainerConfig(t *testing.T) {
- _, imageService, m, err := createTestRuntimeManager()
- assert.NoError(t, err)
- runAsUser := int64(1000)
- runAsGroup := int64(2000)
- pod := &v1.Pod{
- ObjectMeta: metav1.ObjectMeta{
- UID: "12345678",
- Name: "bar",
- Namespace: "new",
- },
- Spec: v1.PodSpec{
- Containers: []v1.Container{
- {
- Name: "foo",
- Image: "busybox",
- ImagePullPolicy: v1.PullIfNotPresent,
- Command: []string{"testCommand"},
- WorkingDir: "testWorkingDir",
- SecurityContext: &v1.SecurityContext{
- RunAsUser: &runAsUser,
- RunAsGroup: &runAsGroup,
- },
- },
- },
- },
- }
- expectedConfig := makeExpectedConfig(m, pod, 0)
- containerConfig, _, err := m.generateContainerConfig(&pod.Spec.Containers[0], pod, 0, "", pod.Spec.Containers[0].Image, []string{}, nil)
- assert.NoError(t, err)
- assert.Equal(t, expectedConfig, containerConfig, "generate container config for kubelet runtime v1.")
- assert.Equal(t, runAsUser, containerConfig.GetLinux().GetSecurityContext().GetRunAsUser().GetValue(), "RunAsUser should be set")
- assert.Equal(t, runAsGroup, containerConfig.GetLinux().GetSecurityContext().GetRunAsGroup().GetValue(), "RunAsGroup should be set")
- runAsRoot := int64(0)
- runAsNonRootTrue := true
- podWithContainerSecurityContext := &v1.Pod{
- ObjectMeta: metav1.ObjectMeta{
- UID: "12345678",
- Name: "bar",
- Namespace: "new",
- },
- Spec: v1.PodSpec{
- Containers: []v1.Container{
- {
- Name: "foo",
- Image: "busybox",
- ImagePullPolicy: v1.PullIfNotPresent,
- Command: []string{"testCommand"},
- WorkingDir: "testWorkingDir",
- SecurityContext: &v1.SecurityContext{
- RunAsNonRoot: &runAsNonRootTrue,
- RunAsUser: &runAsRoot,
- },
- },
- },
- },
- }
- _, _, err = m.generateContainerConfig(&podWithContainerSecurityContext.Spec.Containers[0], podWithContainerSecurityContext, 0, "", podWithContainerSecurityContext.Spec.Containers[0].Image, []string{}, nil)
- assert.Error(t, err)
- imageID, _ := imageService.PullImage(&runtimeapi.ImageSpec{Image: "busybox"}, nil, nil)
- image, _ := imageService.ImageStatus(&runtimeapi.ImageSpec{Image: imageID})
- image.Uid = nil
- image.Username = "test"
- podWithContainerSecurityContext.Spec.Containers[0].SecurityContext.RunAsUser = nil
- podWithContainerSecurityContext.Spec.Containers[0].SecurityContext.RunAsNonRoot = &runAsNonRootTrue
- _, _, err = m.generateContainerConfig(&podWithContainerSecurityContext.Spec.Containers[0], podWithContainerSecurityContext, 0, "", podWithContainerSecurityContext.Spec.Containers[0].Image, []string{}, nil)
- assert.Error(t, err, "RunAsNonRoot should fail for non-numeric username")
- }
- func TestGetHugepageLimitsFromResources(t *testing.T) {
- var baseHugepage []*runtimeapi.HugepageLimit
- // For each page size, limit to 0.
- for _, pageSize := range cgroupfs.HugePageSizes {
- baseHugepage = append(baseHugepage, &runtimeapi.HugepageLimit{
- PageSize: pageSize,
- Limit: uint64(0),
- })
- }
- tests := []struct {
- name string
- resources v1.ResourceRequirements
- expected []*runtimeapi.HugepageLimit
- }{
- {
- name: "Success2MB",
- resources: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- "hugepages-2Mi": resource.MustParse("2Mi"),
- },
- },
- expected: []*runtimeapi.HugepageLimit{
- {
- PageSize: "2MB",
- Limit: 2097152,
- },
- },
- },
- {
- name: "Success1GB",
- resources: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- "hugepages-1Gi": resource.MustParse("2Gi"),
- },
- },
- expected: []*runtimeapi.HugepageLimit{
- {
- PageSize: "1GB",
- Limit: 2147483648,
- },
- },
- },
- {
- name: "Skip2MB",
- resources: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- "hugepages-2MB": resource.MustParse("2Mi"),
- },
- },
- expected: []*runtimeapi.HugepageLimit{
- {
- PageSize: "2MB",
- Limit: 0,
- },
- },
- },
- {
- name: "Skip1GB",
- resources: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- "hugepages-1GB": resource.MustParse("2Gi"),
- },
- },
- expected: []*runtimeapi.HugepageLimit{
- {
- PageSize: "1GB",
- Limit: 0,
- },
- },
- },
- {
- name: "Success2MBand1GB",
- resources: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("0"),
- "hugepages-2Mi": resource.MustParse("2Mi"),
- "hugepages-1Gi": resource.MustParse("2Gi"),
- },
- },
- expected: []*runtimeapi.HugepageLimit{
- {
- PageSize: "2MB",
- Limit: 2097152,
- },
- {
- PageSize: "1GB",
- Limit: 2147483648,
- },
- },
- },
- {
- name: "Skip2MBand1GB",
- resources: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("0"),
- "hugepages-2MB": resource.MustParse("2Mi"),
- "hugepages-1GB": resource.MustParse("2Gi"),
- },
- },
- expected: []*runtimeapi.HugepageLimit{
- {
- PageSize: "2MB",
- Limit: 0,
- },
- {
- PageSize: "1GB",
- Limit: 0,
- },
- },
- },
- }
- for _, test := range tests {
- // Validate if machine supports hugepage size that used in test case.
- machineHugepageSupport := true
- for _, hugepageLimit := range test.expected {
- hugepageSupport := false
- for _, pageSize := range cgroupfs.HugePageSizes {
- if pageSize == hugepageLimit.PageSize {
- hugepageSupport = true
- break
- }
- }
- if !hugepageSupport {
- machineHugepageSupport = false
- break
- }
- }
- // Case of machine can't support hugepage size
- if !machineHugepageSupport {
- continue
- }
- expectedHugepages := baseHugepage
- for _, hugepage := range test.expected {
- for _, expectedHugepage := range expectedHugepages {
- if expectedHugepage.PageSize == hugepage.PageSize {
- expectedHugepage.Limit = hugepage.Limit
- }
- }
- }
- results := GetHugepageLimitsFromResources(test.resources)
- if !reflect.DeepEqual(expectedHugepages, results) {
- t.Errorf("%s test failed. Expected %v but got %v", test.name, expectedHugepages, results)
- }
- for _, hugepage := range baseHugepage {
- hugepage.Limit = uint64(0)
- }
- }
- }
- func TestGenerateLinuxContainerConfigNamespaces(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)()
- _, _, m, err := createTestRuntimeManager()
- if err != nil {
- t.Fatalf("error creating test RuntimeManager: %v", err)
- }
- for _, tc := range []struct {
- name string
- pod *v1.Pod
- target *kubecontainer.ContainerID
- want *runtimeapi.NamespaceOption
- }{
- {
- "Default namespaces",
- &v1.Pod{
- Spec: v1.PodSpec{
- Containers: []v1.Container{
- {Name: "test"},
- },
- },
- },
- nil,
- &runtimeapi.NamespaceOption{
- Pid: runtimeapi.NamespaceMode_CONTAINER,
- },
- },
- {
- "PID Namespace POD",
- &v1.Pod{
- Spec: v1.PodSpec{
- Containers: []v1.Container{
- {Name: "test"},
- },
- ShareProcessNamespace: &[]bool{true}[0],
- },
- },
- nil,
- &runtimeapi.NamespaceOption{
- Pid: runtimeapi.NamespaceMode_POD,
- },
- },
- {
- "PID Namespace TARGET",
- &v1.Pod{
- Spec: v1.PodSpec{
- Containers: []v1.Container{
- {Name: "test"},
- },
- },
- },
- &kubecontainer.ContainerID{Type: "docker", ID: "really-long-id-string"},
- &runtimeapi.NamespaceOption{
- Pid: runtimeapi.NamespaceMode_TARGET,
- TargetId: "really-long-id-string",
- },
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- got := m.generateLinuxContainerConfig(&tc.pod.Spec.Containers[0], tc.pod, nil, "", tc.target)
- if diff := cmp.Diff(tc.want, got.SecurityContext.NamespaceOptions); diff != "" {
- t.Errorf("%v: diff (-want +got):\n%v", t.Name(), diff)
- }
- })
- }
- }
|