From 9a6d50cb2af151527c89cda865e6f7949f7ab827 Mon Sep 17 00:00:00 2001 From: Lee Verberne Date: Fri, 1 Nov 2019 15:09:43 +0000 Subject: [PATCH] Add namespace targeting to the kubelet --- .../kuberuntime/kuberuntime_container.go | 56 +++++++++++- .../kuberuntime_container_linux.go | 12 ++- .../kuberuntime_container_linux_test.go | 85 +++++++++++++++++-- .../kuberuntime/kuberuntime_container_test.go | 78 ++++++++++++++++- .../kuberuntime_container_unsupported.go | 3 +- .../kuberuntime_container_windows.go | 4 +- .../kuberuntime/kuberuntime_manager.go | 19 ++--- .../kuberuntime/kuberuntime_manager_test.go | 4 +- .../pkg/apis/runtime/v1alpha2/api.proto | 13 ++- 9 files changed, 243 insertions(+), 31 deletions(-) diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container.go b/pkg/kubelet/kuberuntime/kuberuntime_container.go index 20a35899f28..89be5366b4f 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container.go @@ -41,7 +41,9 @@ import ( kubetypes "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/sets" + utilfeature "k8s.io/apiserver/pkg/util/feature" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" + "k8s.io/kubernetes/pkg/features" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/events" "k8s.io/kubernetes/pkg/kubelet/types" @@ -86,13 +88,52 @@ func (m *kubeGenericRuntimeManager) recordContainerEvent(pod *v1.Pod, container m.recorder.Event(ref, eventType, reason, eventMessage) } +// startSpec wraps the spec required to start a container, either a regular/init container +// or an ephemeral container. Ephemeral containers contain all the fields of regular/init +// containers, plus some additional fields. In both cases startSpec.container will be set. +type startSpec struct { + container *v1.Container + ephemeralContainer *v1.EphemeralContainer +} + +func containerStartSpec(c *v1.Container) *startSpec { + return &startSpec{container: c} +} + +func ephemeralContainerStartSpec(ec *v1.EphemeralContainer) *startSpec { + return &startSpec{ + container: (*v1.Container)(&ec.EphemeralContainerCommon), + ephemeralContainer: ec, + } +} + +// getTargetID returns the kubecontainer.ContainerID for ephemeral container namespace +// targeting. The target is stored as EphemeralContainer.TargetContainerName, which must be +// resolved to a ContainerID using podStatus. The target container must already exist, which +// usually isn't a problem since ephemeral containers aren't allowed at pod creation time. +// This always returns nil when the EphemeralContainers feature is disabled. +func (s *startSpec) getTargetID(podStatus *kubecontainer.PodStatus) (*kubecontainer.ContainerID, error) { + if s.ephemeralContainer == nil || s.ephemeralContainer.TargetContainerName == "" || !utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) { + return nil, nil + } + + targetStatus := podStatus.FindContainerStatusByName(s.ephemeralContainer.TargetContainerName) + if targetStatus == nil { + return nil, fmt.Errorf("unable to find target container %v", s.ephemeralContainer.TargetContainerName) + } + + return &targetStatus.ID, nil +} + // startContainer starts a container and returns a message indicates why it is failed on error. // It starts the container through the following steps: // * pull the image // * create the container // * start the container // * run the post start lifecycle hooks (if applicable) -func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandboxConfig *runtimeapi.PodSandboxConfig, container *v1.Container, pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, podIP string, podIPs []string) (string, error) { +func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandboxConfig *runtimeapi.PodSandboxConfig, spec *startSpec, pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, podIP string, podIPs []string) (string, error) { + container := spec.container + // Step 1: pull the image. imageRef, msg, err := m.imagePuller.EnsureImageExists(pod, container, pullSecrets, podSandboxConfig) if err != nil { @@ -115,7 +156,14 @@ func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandb restartCount = containerStatus.RestartCount + 1 } - containerConfig, cleanupAction, err := m.generateContainerConfig(container, pod, restartCount, podIP, imageRef, podIPs) + target, err := spec.getTargetID(podStatus) + if err != nil { + s, _ := grpcstatus.FromError(err) + m.recordContainerEvent(pod, container, "", v1.EventTypeWarning, events.FailedToCreateContainer, "Error: %v", s.Message()) + return s.Message(), ErrCreateContainerConfig + } + + containerConfig, cleanupAction, err := m.generateContainerConfig(container, pod, restartCount, podIP, imageRef, podIPs, target) if cleanupAction != nil { defer cleanupAction() } @@ -195,7 +243,7 @@ func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandb } // generateContainerConfig generates container config for kubelet runtime v1. -func (m *kubeGenericRuntimeManager) generateContainerConfig(container *v1.Container, pod *v1.Pod, restartCount int, podIP, imageRef string, podIPs []string) (*runtimeapi.ContainerConfig, func(), error) { +func (m *kubeGenericRuntimeManager) generateContainerConfig(container *v1.Container, pod *v1.Pod, restartCount int, podIP, imageRef string, podIPs []string, nsTarget *kubecontainer.ContainerID) (*runtimeapi.ContainerConfig, func(), error) { opts, cleanupAction, err := m.runtimeHelper.GenerateRunContainerOptions(pod, container, podIP, podIPs) if err != nil { return nil, nil, err @@ -239,7 +287,7 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(container *v1.Contai } // set platform specific configurations. - if err := m.applyPlatformSpecificContainerConfig(config, container, pod, uid, username); err != nil { + if err := m.applyPlatformSpecificContainerConfig(config, container, pod, uid, username, nsTarget); err != nil { return nil, cleanupAction, err } diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go index 483d335b9ee..5da5735b559 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go @@ -28,22 +28,28 @@ import ( "k8s.io/klog" v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" kubefeatures "k8s.io/kubernetes/pkg/features" + kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/qos" ) // applyPlatformSpecificContainerConfig applies platform specific configurations to runtimeapi.ContainerConfig. -func (m *kubeGenericRuntimeManager) applyPlatformSpecificContainerConfig(config *runtimeapi.ContainerConfig, container *v1.Container, pod *v1.Pod, uid *int64, username string) error { - config.Linux = m.generateLinuxContainerConfig(container, pod, uid, username) +func (m *kubeGenericRuntimeManager) applyPlatformSpecificContainerConfig(config *runtimeapi.ContainerConfig, container *v1.Container, pod *v1.Pod, uid *int64, username string, nsTarget *kubecontainer.ContainerID) error { + config.Linux = m.generateLinuxContainerConfig(container, pod, uid, username, nsTarget) return nil } // generateLinuxContainerConfig generates linux container config for kubelet runtime v1. -func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.Container, pod *v1.Pod, uid *int64, username string) *runtimeapi.LinuxContainerConfig { +func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.Container, pod *v1.Pod, uid *int64, username string, nsTarget *kubecontainer.ContainerID) *runtimeapi.LinuxContainerConfig { lc := &runtimeapi.LinuxContainerConfig{ Resources: &runtimeapi.LinuxContainerResources{}, SecurityContext: m.determineEffectiveSecurityContext(pod, container, uid, username), } + if nsTarget != nil && lc.SecurityContext.NamespaceOptions.Pid == runtimeapi.NamespaceMode_CONTAINER { + lc.SecurityContext.NamespaceOptions.Pid = runtimeapi.NamespaceMode_TARGET + lc.SecurityContext.NamespaceOptions.TargetId = nsTarget.ID + } + // set linux container resources var cpuShares int64 cpuRequest := container.Resources.Requests.Cpu() diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go index 6319477ccde..98203fef23e 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go @@ -22,13 +22,17 @@ 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" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" - "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 { @@ -57,7 +61,7 @@ func makeExpectedConfig(m *kubeGenericRuntimeManager, pod *v1.Pod, containerInde Stdin: container.Stdin, StdinOnce: container.StdinOnce, Tty: container.TTY, - Linux: m.generateLinuxContainerConfig(container, pod, new(int64), ""), + Linux: m.generateLinuxContainerConfig(container, pod, new(int64), "", nil), Envs: envs, } return expectedConfig @@ -93,7 +97,7 @@ func TestGenerateContainerConfig(t *testing.T) { } expectedConfig := makeExpectedConfig(m, pod, 0) - containerConfig, _, err := m.generateContainerConfig(&pod.Spec.Containers[0], pod, 0, "", pod.Spec.Containers[0].Image, []string{}) + 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") @@ -124,7 +128,7 @@ func TestGenerateContainerConfig(t *testing.T) { }, } - _, _, err = m.generateContainerConfig(&podWithContainerSecurityContext.Spec.Containers[0], podWithContainerSecurityContext, 0, "", podWithContainerSecurityContext.Spec.Containers[0].Image, []string{}) + _, _, 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) @@ -136,7 +140,7 @@ func TestGenerateContainerConfig(t *testing.T) { 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{}) + _, _, 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") } @@ -296,3 +300,70 @@ func TestGetHugepageLimitsFromResources(t *testing.T) { } } } + +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) + } + }) + } +} diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_test.go b/pkg/kubelet/kuberuntime/kuberuntime_container_test.go index a30ab2f1e6f..70ff0272d17 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_test.go @@ -17,17 +17,22 @@ limitations under the License. package kuberuntime import ( + "fmt" "path/filepath" "strings" "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/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" containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" "k8s.io/kubernetes/pkg/kubelet/lifecycle" @@ -325,7 +330,7 @@ func TestLifeCycleHook(t *testing.T) { } // Now try to create a container, which should in turn invoke PostStart Hook - _, err := m.startContainer(fakeSandBox.Id, fakeSandBoxConfig, testContainer, testPod, fakePodStatus, nil, "", []string{}) + _, err := m.startContainer(fakeSandBox.Id, fakeSandBoxConfig, containerStartSpec(testContainer), testPod, fakePodStatus, nil, "", []string{}) if err != nil { t.Errorf("startContainer error =%v", err) } @@ -334,3 +339,72 @@ func TestLifeCycleHook(t *testing.T) { } }) } + +func TestStartSpec(t *testing.T) { + podStatus := &kubecontainer.PodStatus{ + ContainerStatuses: []*kubecontainer.ContainerStatus{ + { + ID: kubecontainer.ContainerID{ + Type: "docker", + ID: "docker-something-something", + }, + Name: "target", + }, + }, + } + + for _, tc := range []struct { + name string + spec *startSpec + want *kubecontainer.ContainerID + }{ + { + "Regular Container", + containerStartSpec(&v1.Container{ + Name: "test", + }), + nil, + }, + { + "Ephemeral Container w/o Target", + ephemeralContainerStartSpec(&v1.EphemeralContainer{ + EphemeralContainerCommon: v1.EphemeralContainerCommon{ + Name: "test", + }, + }), + nil, + }, + { + "Ephemeral Container w/ Target", + ephemeralContainerStartSpec(&v1.EphemeralContainer{ + EphemeralContainerCommon: v1.EphemeralContainerCommon{ + Name: "test", + }, + TargetContainerName: "target", + }), + &kubecontainer.ContainerID{ + Type: "docker", + ID: "docker-something-something", + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, true)() + if got, err := tc.spec.getTargetID(podStatus); err != nil { + t.Fatalf("%v: getTargetID got unexpected error: %v", t.Name(), err) + } else if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("%v: getTargetID got unexpected result. diff:\n%v", t.Name(), diff) + } + }) + + // Test with feature disabled in self-contained section which can be removed when feature flag is removed. + t.Run(fmt.Sprintf("%s (disabled)", tc.name), func(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EphemeralContainers, false)() + if got, err := tc.spec.getTargetID(podStatus); err != nil { + t.Fatalf("%v: getTargetID got unexpected error: %v", t.Name(), err) + } else if got != nil { + t.Errorf("%v: getTargetID got: %v, wanted nil", t.Name(), got) + } + }) + } +} diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_unsupported.go b/pkg/kubelet/kuberuntime/kuberuntime_container_unsupported.go index caedaa82935..72e234c9143 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_unsupported.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_unsupported.go @@ -21,9 +21,10 @@ package kuberuntime import ( "k8s.io/api/core/v1" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" + kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" ) // applyPlatformSpecificContainerConfig applies platform specific configurations to runtimeapi.ContainerConfig. -func (m *kubeGenericRuntimeManager) applyPlatformSpecificContainerConfig(config *runtimeapi.ContainerConfig, container *v1.Container, pod *v1.Pod, uid *int64, username string) error { +func (m *kubeGenericRuntimeManager) applyPlatformSpecificContainerConfig(config *runtimeapi.ContainerConfig, container *v1.Container, pod *v1.Pod, uid *int64, username string, nsTarget *kubecontainer.ContainerID) error { return nil } diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_windows.go b/pkg/kubelet/kuberuntime/kuberuntime_container_windows.go index 6f858b7482e..7b3f064532e 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_windows.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_windows.go @@ -20,6 +20,7 @@ package kuberuntime import ( "fmt" + "github.com/docker/docker/pkg/sysinfo" "k8s.io/api/core/v1" @@ -27,11 +28,12 @@ import ( runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" kubefeatures "k8s.io/kubernetes/pkg/features" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" + kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/securitycontext" ) // applyPlatformSpecificContainerConfig applies platform specific configurations to runtimeapi.ContainerConfig. -func (m *kubeGenericRuntimeManager) applyPlatformSpecificContainerConfig(config *runtimeapi.ContainerConfig, container *v1.Container, pod *v1.Pod, uid *int64, username string) error { +func (m *kubeGenericRuntimeManager) applyPlatformSpecificContainerConfig(config *runtimeapi.ContainerConfig, container *v1.Container, pod *v1.Pod, uid *int64, username string, _ *kubecontainer.ContainerID) error { windowsConfig, err := m.generateWindowsContainerConfig(container, pod, uid, username) if err != nil { return err diff --git a/pkg/kubelet/kuberuntime/kuberuntime_manager.go b/pkg/kubelet/kuberuntime/kuberuntime_manager.go index 3d0eb561836..9035f6a9be0 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_manager.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_manager.go @@ -777,20 +777,20 @@ func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontaine // Helper containing boilerplate common to starting all types of containers. // typeName is a label used to describe this type of container in log messages, // currently: "container", "init container" or "ephemeral container" - start := func(typeName string, container *v1.Container) error { - startContainerResult := kubecontainer.NewSyncResult(kubecontainer.StartContainer, container.Name) + start := func(typeName string, spec *startSpec) error { + startContainerResult := kubecontainer.NewSyncResult(kubecontainer.StartContainer, spec.container.Name) result.AddSyncResult(startContainerResult) - isInBackOff, msg, err := m.doBackOff(pod, container, podStatus, backOff) + isInBackOff, msg, err := m.doBackOff(pod, spec.container, podStatus, backOff) if isInBackOff { startContainerResult.Fail(err, msg) - klog.V(4).Infof("Backing Off restarting %v %+v in pod %v", typeName, container, format.Pod(pod)) + klog.V(4).Infof("Backing Off restarting %v %+v in pod %v", typeName, spec.container, format.Pod(pod)) return err } - klog.V(4).Infof("Creating %v %+v in pod %v", typeName, container, format.Pod(pod)) + klog.V(4).Infof("Creating %v %+v in pod %v", typeName, spec.container, format.Pod(pod)) // NOTE (aramase) podIPs are populated for single stack and dual stack clusters. Send only podIPs. - if msg, err := m.startContainer(podSandboxID, podSandboxConfig, container, pod, podStatus, pullSecrets, podIP, podIPs); err != nil { + if msg, err := m.startContainer(podSandboxID, podSandboxConfig, spec, pod, podStatus, pullSecrets, podIP, podIPs); err != nil { startContainerResult.Fail(err, msg) // known errors that are logged in other places are logged at higher levels here to avoid // repetitive log spam @@ -812,15 +812,14 @@ func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontaine // containers cannot be specified on pod creation. if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) { for _, idx := range podContainerChanges.EphemeralContainersToStart { - c := (*v1.Container)(&pod.Spec.EphemeralContainers[idx].EphemeralContainerCommon) - start("ephemeral container", c) + start("ephemeral container", ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx])) } } // Step 6: start the init container. if container := podContainerChanges.NextInitContainerToStart; container != nil { // Start the next init container. - if err := start("init container", container); err != nil { + if err := start("init container", containerStartSpec(container)); err != nil { return } @@ -830,7 +829,7 @@ func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontaine // Step 7: start containers in podContainerChanges.ContainersToStart. for _, idx := range podContainerChanges.ContainersToStart { - start("container", &pod.Spec.Containers[idx]) + start("container", containerStartSpec(&pod.Spec.Containers[idx])) } return diff --git a/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go b/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go index f75ffcbffdb..16a4cc467b2 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_manager_test.go @@ -27,7 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" @@ -157,7 +157,7 @@ func makeFakeContainer(t *testing.T, m *kubeGenericRuntimeManager, template cont sandboxConfig, err := m.generatePodSandboxConfig(template.pod, template.sandboxAttempt) assert.NoError(t, err, "generatePodSandboxConfig for container template %+v", template) - containerConfig, _, err := m.generateContainerConfig(template.container, template.pod, template.attempt, "", template.container.Image, []string{}) + containerConfig, _, err := m.generateContainerConfig(template.container, template.pod, template.attempt, "", template.container.Image, []string{}, nil) assert.NoError(t, err, "generateContainerConfig for container template %+v", template) podSandboxID := apitest.BuildSandboxName(sandboxConfig.Metadata) diff --git a/staging/src/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.proto b/staging/src/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.proto index ce5a63d56b6..f9430299471 100644 --- a/staging/src/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.proto +++ b/staging/src/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.proto @@ -216,6 +216,13 @@ enum NamespaceMode { // For example, a container with a PID namespace of NODE expects to view // all of the processes on the host running the kubelet. NODE = 2; + // TARGET targets the namespace of another container. When this is specified, + // a target_id must be specified in NamespaceOption and refer to a container + // previously created with NamespaceMode CONTAINER. This containers namespace + // will be made to match that of container target_id. + // For example, a container with a PID namespace of TARGET expects to view + // all of the processes that container target_id can view. + TARGET = 3; } // NamespaceOption provides options for Linux namespaces. @@ -227,12 +234,16 @@ message NamespaceOption { // PID namespace for this container/sandbox. // Note: The CRI default is POD, but the v1.PodSpec default is CONTAINER. // The kubelet's runtime manager will set this to CONTAINER explicitly for v1 pods. - // Namespaces currently set by the kubelet: POD, CONTAINER, NODE + // Namespaces currently set by the kubelet: POD, CONTAINER, NODE, TARGET NamespaceMode pid = 2; // IPC namespace for this container/sandbox. // Note: There is currently no way to set CONTAINER scoped IPC in the Kubernetes API. // Namespaces currently set by the kubelet: POD, NODE NamespaceMode ipc = 3; + // Target Container ID for NamespaceMode of TARGET. This container must have been + // previously created in the same pod. It is not possible to specify different targets + // for each namespace. + string target_id = 4; } // Int64Value is the wrapper of int64.