mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-14 21:53:52 +00:00
Merge pull request #120281 from gjkim42/feature-gate-sidecar-containers-in-kuberuntime
Feature-gate SidecarContainers code in pkg/kubelet/kuberuntime
This commit is contained in:
@@ -849,6 +849,63 @@ func (m *kubeGenericRuntimeManager) purgeInitContainers(ctx context.Context, pod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findNextInitContainerToRun returns the status of the last failed container, the
|
||||||
|
// index of next init container to start, or done if there are no further init containers.
|
||||||
|
// Status is only returned if an init container is failed, in which case next will
|
||||||
|
// point to the current container.
|
||||||
|
func findNextInitContainerToRun(pod *v1.Pod, podStatus *kubecontainer.PodStatus) (status *kubecontainer.Status, next *v1.Container, done bool) {
|
||||||
|
if len(pod.Spec.InitContainers) == 0 {
|
||||||
|
return nil, nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any of the main containers have status and are Running, then all init containers must
|
||||||
|
// have been executed at some point in the past. However, they could have been removed
|
||||||
|
// from the container runtime now, and if we proceed, it would appear as if they
|
||||||
|
// never ran and will re-execute improperly.
|
||||||
|
for i := range pod.Spec.Containers {
|
||||||
|
container := &pod.Spec.Containers[i]
|
||||||
|
status := podStatus.FindContainerStatusByName(container.Name)
|
||||||
|
if status != nil && status.State == kubecontainer.ContainerStateRunning {
|
||||||
|
return nil, nil, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are failed containers, return the status of the last failed one.
|
||||||
|
for i := len(pod.Spec.InitContainers) - 1; i >= 0; i-- {
|
||||||
|
container := &pod.Spec.InitContainers[i]
|
||||||
|
status := podStatus.FindContainerStatusByName(container.Name)
|
||||||
|
if status != nil && isInitContainerFailed(status) {
|
||||||
|
return status, container, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are no failed containers now.
|
||||||
|
for i := len(pod.Spec.InitContainers) - 1; i >= 0; i-- {
|
||||||
|
container := &pod.Spec.InitContainers[i]
|
||||||
|
status := podStatus.FindContainerStatusByName(container.Name)
|
||||||
|
if status == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// container is still running, return not done.
|
||||||
|
if status.State == kubecontainer.ContainerStateRunning {
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.State == kubecontainer.ContainerStateExited {
|
||||||
|
// all init containers successful
|
||||||
|
if i == (len(pod.Spec.InitContainers) - 1) {
|
||||||
|
return nil, nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// all containers up to i successful, go to i+1
|
||||||
|
return nil, &pod.Spec.InitContainers[i+1], false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, &pod.Spec.InitContainers[0], false
|
||||||
|
}
|
||||||
|
|
||||||
// hasAnyRegularContainerCreated returns true if any regular container has been
|
// hasAnyRegularContainerCreated returns true if any regular container has been
|
||||||
// created, which indicates all init containers have been initialized.
|
// created, which indicates all init containers have been initialized.
|
||||||
func hasAnyRegularContainerCreated(pod *v1.Pod, podStatus *kubecontainer.PodStatus) bool {
|
func hasAnyRegularContainerCreated(pod *v1.Pod, podStatus *kubecontainer.PodStatus) bool {
|
||||||
@@ -878,6 +935,10 @@ func hasAnyRegularContainerCreated(pod *v1.Pod, podStatus *kubecontainer.PodStat
|
|||||||
// - Start the first init container that has not been started.
|
// - Start the first init container that has not been started.
|
||||||
// - Restart all restartable init containers that have started but are not running.
|
// - Restart all restartable init containers that have started but are not running.
|
||||||
// - Kill the restartable init containers that are not alive or started.
|
// - Kill the restartable init containers that are not alive or started.
|
||||||
|
//
|
||||||
|
// Note that this is a function for the SidecarContainers feature.
|
||||||
|
// Please sync with the findNextInitContainerToRun function if any changes are
|
||||||
|
// made, as either this or that function will be called.
|
||||||
func (m *kubeGenericRuntimeManager) computeInitContainerActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus, changes *podActions) bool {
|
func (m *kubeGenericRuntimeManager) computeInitContainerActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus, changes *podActions) bool {
|
||||||
if len(pod.Spec.InitContainers) == 0 {
|
if len(pod.Spec.InitContainers) == 0 {
|
||||||
return true
|
return true
|
||||||
|
@@ -492,9 +492,13 @@ type podActions struct {
|
|||||||
// The attempt number of creating sandboxes for the pod.
|
// The attempt number of creating sandboxes for the pod.
|
||||||
Attempt uint32
|
Attempt uint32
|
||||||
|
|
||||||
|
// The next init container to start.
|
||||||
|
NextInitContainerToStart *v1.Container
|
||||||
// InitContainersToStart keeps a list of indexes for the init containers to
|
// InitContainersToStart keeps a list of indexes for the init containers to
|
||||||
// start, where the index is the index of the specific init container in the
|
// start, where the index is the index of the specific init container in the
|
||||||
// pod spec (pod.Spec.InitContainers).
|
// pod spec (pod.Spec.InitContainers).
|
||||||
|
// NOTE: This is a field for SidecarContainers feature. Either this or
|
||||||
|
// NextInitContainerToStart will be set.
|
||||||
InitContainersToStart []int
|
InitContainersToStart []int
|
||||||
// ContainersToStart keeps a list of indexes for the containers to start,
|
// ContainersToStart keeps a list of indexes for the containers to start,
|
||||||
// where the index is the index of the specific container in the pod spec (
|
// where the index is the index of the specific container in the pod spec (
|
||||||
@@ -854,21 +858,34 @@ func (m *kubeGenericRuntimeManager) computePodActions(ctx context.Context, pod *
|
|||||||
containersToStart = append(containersToStart, idx)
|
containersToStart = append(containersToStart, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is any regular container, it means all init containers have
|
|
||||||
// been initialized.
|
|
||||||
hasInitialized := hasAnyRegularContainerCreated(pod, podStatus)
|
|
||||||
// We should not create a sandbox, and just kill the pod if initialization
|
// We should not create a sandbox, and just kill the pod if initialization
|
||||||
// is done and there is no container to start.
|
// is done and there is no container to start.
|
||||||
if hasInitialized && len(containersToStart) == 0 {
|
if len(containersToStart) == 0 {
|
||||||
changes.CreateSandbox = false
|
hasInitialized := false
|
||||||
return changes
|
if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) {
|
||||||
|
_, _, hasInitialized = findNextInitContainerToRun(pod, podStatus)
|
||||||
|
} else {
|
||||||
|
// If there is any regular container, it means all init containers have
|
||||||
|
// been initialized.
|
||||||
|
hasInitialized = hasAnyRegularContainerCreated(pod, podStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasInitialized {
|
||||||
|
changes.CreateSandbox = false
|
||||||
|
return changes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are creating a pod sandbox, we should restart from the initial
|
// If we are creating a pod sandbox, we should restart from the initial
|
||||||
// state.
|
// state.
|
||||||
if len(pod.Spec.InitContainers) != 0 {
|
if len(pod.Spec.InitContainers) != 0 {
|
||||||
// Pod has init containers, return the first one.
|
// Pod has init containers, return the first one.
|
||||||
changes.InitContainersToStart = []int{0}
|
if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) {
|
||||||
|
changes.NextInitContainerToStart = &pod.Spec.InitContainers[0]
|
||||||
|
} else {
|
||||||
|
changes.InitContainersToStart = []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
return changes
|
return changes
|
||||||
}
|
}
|
||||||
changes.ContainersToStart = containersToStart
|
changes.ContainersToStart = containersToStart
|
||||||
@@ -885,11 +902,39 @@ func (m *kubeGenericRuntimeManager) computePodActions(ctx context.Context, pod *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasInitialized := m.computeInitContainerActions(pod, podStatus, &changes)
|
// Check initialization progress.
|
||||||
if changes.KillPod || !hasInitialized {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) {
|
||||||
// Initialization failed or still in progress. Skip inspecting non-init
|
initLastStatus, next, done := findNextInitContainerToRun(pod, podStatus)
|
||||||
// containers.
|
if !done {
|
||||||
return changes
|
if next != nil {
|
||||||
|
initFailed := initLastStatus != nil && isInitContainerFailed(initLastStatus)
|
||||||
|
if initFailed && !shouldRestartOnFailure(pod) {
|
||||||
|
changes.KillPod = true
|
||||||
|
} else {
|
||||||
|
// Always try to stop containers in unknown state first.
|
||||||
|
if initLastStatus != nil && initLastStatus.State == kubecontainer.ContainerStateUnknown {
|
||||||
|
changes.ContainersToKill[initLastStatus.ID] = containerToKillInfo{
|
||||||
|
name: next.Name,
|
||||||
|
container: next,
|
||||||
|
message: fmt.Sprintf("Init container is in %q state, try killing it before restart",
|
||||||
|
initLastStatus.State),
|
||||||
|
reason: reasonUnknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changes.NextInitContainerToStart = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Initialization failed or still in progress. Skip inspecting non-init
|
||||||
|
// containers.
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasInitialized := m.computeInitContainerActions(pod, podStatus, &changes)
|
||||||
|
if changes.KillPod || !hasInitialized {
|
||||||
|
// Initialization failed or still in progress. Skip inspecting non-init
|
||||||
|
// containers.
|
||||||
|
return changes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isInPlacePodVerticalScalingAllowed(pod) {
|
if isInPlacePodVerticalScalingAllowed(pod) {
|
||||||
@@ -985,9 +1030,11 @@ func (m *kubeGenericRuntimeManager) computePodActions(ctx context.Context, pod *
|
|||||||
|
|
||||||
if keepCount == 0 && len(changes.ContainersToStart) == 0 {
|
if keepCount == 0 && len(changes.ContainersToStart) == 0 {
|
||||||
changes.KillPod = true
|
changes.KillPod = true
|
||||||
// To prevent the restartable init containers to keep pod alive, we should
|
if utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) {
|
||||||
// not restart them.
|
// To prevent the restartable init containers to keep pod alive, we should
|
||||||
changes.InitContainersToStart = nil
|
// not restart them.
|
||||||
|
changes.InitContainersToStart = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return changes
|
return changes
|
||||||
@@ -1227,21 +1274,34 @@ func (m *kubeGenericRuntimeManager) SyncPod(ctx context.Context, pod *v1.Pod, po
|
|||||||
start(ctx, "ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
|
start(ctx, "ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: start init containers.
|
if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) {
|
||||||
for _, idx := range podContainerChanges.InitContainersToStart {
|
// Step 6: start the init container.
|
||||||
container := &pod.Spec.InitContainers[idx]
|
if container := podContainerChanges.NextInitContainerToStart; container != nil {
|
||||||
// Start the next init container.
|
// Start the next init container.
|
||||||
if err := start(ctx, "init container", metrics.InitContainer, containerStartSpec(container)); err != nil {
|
if err := start(ctx, "init container", metrics.InitContainer, containerStartSpec(container)); err != nil {
|
||||||
if types.IsRestartableInitContainer(container) {
|
return
|
||||||
klog.V(4).InfoS("Failed to start the restartable init container for the pod, skipping", "initContainerName", container.Name, "pod", klog.KObj(pod))
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
klog.V(4).InfoS("Failed to initialize the pod, as the init container failed to start, aborting", "initContainerName", container.Name, "pod", klog.KObj(pod))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Successfully started the container; clear the entry in the failure
|
// Successfully started the container; clear the entry in the failure
|
||||||
klog.V(4).InfoS("Completed init container for pod", "containerName", container.Name, "pod", klog.KObj(pod))
|
klog.V(4).InfoS("Completed init container for pod", "containerName", container.Name, "pod", klog.KObj(pod))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Step 6: start init containers.
|
||||||
|
for _, idx := range podContainerChanges.InitContainersToStart {
|
||||||
|
container := &pod.Spec.InitContainers[idx]
|
||||||
|
// Start the next init container.
|
||||||
|
if err := start(ctx, "init container", metrics.InitContainer, containerStartSpec(container)); err != nil {
|
||||||
|
if types.IsRestartableInitContainer(container) {
|
||||||
|
klog.V(4).InfoS("Failed to start the restartable init container for the pod, skipping", "initContainerName", container.Name, "pod", klog.KObj(pod))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
klog.V(4).InfoS("Failed to initialize the pod, as the init container failed to start, aborting", "initContainerName", container.Name, "pod", klog.KObj(pod))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Successfully started the container; clear the entry in the failure
|
||||||
|
klog.V(4).InfoS("Completed init container for pod", "containerName", container.Name, "pod", klog.KObj(pod))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 7: For containers in podContainerChanges.ContainersToUpdate[CPU,Memory] list, invoke UpdateContainerResources
|
// Step 7: For containers in podContainerChanges.ContainersToUpdate[CPU,Memory] list, invoke UpdateContainerResources
|
||||||
|
@@ -1221,6 +1221,16 @@ func verifyActions(t *testing.T, expected, actual *podActions, desc string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestComputePodActionsWithInitContainers(t *testing.T) {
|
func TestComputePodActionsWithInitContainers(t *testing.T) {
|
||||||
|
t.Run("sidecar containers disabled", func(t *testing.T) {
|
||||||
|
testComputePodActionsWithInitContainers(t, false)
|
||||||
|
})
|
||||||
|
t.Run("sidecar containers enabled", func(t *testing.T) {
|
||||||
|
testComputePodActionsWithInitContainers(t, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testComputePodActionsWithInitContainers(t *testing.T, sidecarContainersEnabled bool) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SidecarContainers, sidecarContainersEnabled)()
|
||||||
_, _, m, err := createTestRuntimeManager()
|
_, _, m, err := createTestRuntimeManager()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -1250,10 +1260,11 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses = nil
|
status.ContainerStatuses = nil
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
InitContainersToStart: []int{0},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[0],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{0},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"initialization in progress; do nothing": {
|
"initialization in progress; do nothing": {
|
||||||
@@ -1269,13 +1280,14 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.SandboxStatuses[0].State = runtimeapi.PodSandboxState_SANDBOX_NOTREADY
|
status.SandboxStatuses[0].State = runtimeapi.PodSandboxState_SANDBOX_NOTREADY
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
KillPod: true,
|
KillPod: true,
|
||||||
CreateSandbox: true,
|
CreateSandbox: true,
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
Attempt: uint32(1),
|
Attempt: uint32(1),
|
||||||
InitContainersToStart: []int{0},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[0],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{0},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"initialization failed; restart the last init container if RestartPolicy == Always": {
|
"initialization failed; restart the last init container if RestartPolicy == Always": {
|
||||||
@@ -1284,10 +1296,11 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses[2].ExitCode = 137
|
status.ContainerStatuses[2].ExitCode = 137
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
InitContainersToStart: []int{2},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[2],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{2},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"initialization failed; restart the last init container if RestartPolicy == OnFailure": {
|
"initialization failed; restart the last init container if RestartPolicy == OnFailure": {
|
||||||
@@ -1296,10 +1309,11 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses[2].ExitCode = 137
|
status.ContainerStatuses[2].ExitCode = 137
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
InitContainersToStart: []int{2},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[2],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{2},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"initialization failed; kill pod if RestartPolicy == Never": {
|
"initialization failed; kill pod if RestartPolicy == Never": {
|
||||||
@@ -1320,10 +1334,11 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses[2].State = kubecontainer.ContainerStateUnknown
|
status.ContainerStatuses[2].State = kubecontainer.ContainerStateUnknown
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
InitContainersToStart: []int{2},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[2],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{2},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{2}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{2}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"init container state unknown; kill and recreate the last init container if RestartPolicy == OnFailure": {
|
"init container state unknown; kill and recreate the last init container if RestartPolicy == OnFailure": {
|
||||||
@@ -1332,10 +1347,11 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses[2].State = kubecontainer.ContainerStateUnknown
|
status.ContainerStatuses[2].State = kubecontainer.ContainerStateUnknown
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
InitContainersToStart: []int{2},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[2],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{2},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{2}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{2}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"init container state unknown; kill pod if RestartPolicy == Never": {
|
"init container state unknown; kill pod if RestartPolicy == Never": {
|
||||||
@@ -1371,13 +1387,14 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses = []*kubecontainer.Status{}
|
status.ContainerStatuses = []*kubecontainer.Status{}
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
KillPod: true,
|
KillPod: true,
|
||||||
CreateSandbox: true,
|
CreateSandbox: true,
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
Attempt: uint32(1),
|
Attempt: uint32(1),
|
||||||
InitContainersToStart: []int{0},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[0],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{0},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Pod sandbox not ready, init container failed, and RestartPolicy == OnFailure; create a new pod sandbox": {
|
"Pod sandbox not ready, init container failed, and RestartPolicy == OnFailure; create a new pod sandbox": {
|
||||||
@@ -1387,13 +1404,14 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses[2].ExitCode = 137
|
status.ContainerStatuses[2].ExitCode = 137
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
KillPod: true,
|
KillPod: true,
|
||||||
CreateSandbox: true,
|
CreateSandbox: true,
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
Attempt: uint32(1),
|
Attempt: uint32(1),
|
||||||
InitContainersToStart: []int{0},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[0],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{0},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"some of the init container statuses are missing but the last init container is running, don't restart preceding ones": {
|
"some of the init container statuses are missing but the last init container is running, don't restart preceding ones": {
|
||||||
@@ -1419,6 +1437,15 @@ func TestComputePodActionsWithInitContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
actions := m.computePodActions(ctx, pod, status)
|
actions := m.computePodActions(ctx, pod, status)
|
||||||
|
if !sidecarContainersEnabled {
|
||||||
|
// If sidecar containers are disabled, we should not see any
|
||||||
|
// InitContainersToStart in the actions.
|
||||||
|
test.actions.InitContainersToStart = nil
|
||||||
|
} else {
|
||||||
|
// If sidecar containers are enabled, we should not see any
|
||||||
|
// NextInitContainerToStart in the actions.
|
||||||
|
test.actions.NextInitContainerToStart = nil
|
||||||
|
}
|
||||||
verifyActions(t, &test.actions, &actions, desc)
|
verifyActions(t, &test.actions, &actions, desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1865,6 +1892,16 @@ func TestComputePodActionsWithInitAndEphemeralContainers(t *testing.T) {
|
|||||||
TestComputePodActions(t)
|
TestComputePodActions(t)
|
||||||
TestComputePodActionsWithInitContainers(t)
|
TestComputePodActionsWithInitContainers(t)
|
||||||
|
|
||||||
|
t.Run("sidecar containers disabled", func(t *testing.T) {
|
||||||
|
testComputePodActionsWithInitAndEphemeralContainers(t, false)
|
||||||
|
})
|
||||||
|
t.Run("sidecar containers enabled", func(t *testing.T) {
|
||||||
|
testComputePodActionsWithInitAndEphemeralContainers(t, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testComputePodActionsWithInitAndEphemeralContainers(t *testing.T, sidecarContainersEnabled bool) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SidecarContainers, sidecarContainersEnabled)()
|
||||||
_, _, m, err := createTestRuntimeManager()
|
_, _, m, err := createTestRuntimeManager()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -1941,13 +1978,14 @@ func TestComputePodActionsWithInitAndEphemeralContainers(t *testing.T) {
|
|||||||
status.ContainerStatuses[0].ExitCode = 137
|
status.ContainerStatuses[0].ExitCode = 137
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
KillPod: true,
|
KillPod: true,
|
||||||
CreateSandbox: true,
|
CreateSandbox: true,
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
Attempt: uint32(1),
|
Attempt: uint32(1),
|
||||||
InitContainersToStart: []int{0},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[0],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{0},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Kill pod and do not restart ephemeral container if the pod sandbox is dead": {
|
"Kill pod and do not restart ephemeral container if the pod sandbox is dead": {
|
||||||
@@ -1956,13 +1994,14 @@ func TestComputePodActionsWithInitAndEphemeralContainers(t *testing.T) {
|
|||||||
status.SandboxStatuses[0].State = runtimeapi.PodSandboxState_SANDBOX_NOTREADY
|
status.SandboxStatuses[0].State = runtimeapi.PodSandboxState_SANDBOX_NOTREADY
|
||||||
},
|
},
|
||||||
actions: podActions{
|
actions: podActions{
|
||||||
KillPod: true,
|
KillPod: true,
|
||||||
CreateSandbox: true,
|
CreateSandbox: true,
|
||||||
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
SandboxID: baseStatus.SandboxStatuses[0].Id,
|
||||||
Attempt: uint32(1),
|
Attempt: uint32(1),
|
||||||
InitContainersToStart: []int{0},
|
NextInitContainerToStart: &basePod.Spec.InitContainers[0],
|
||||||
ContainersToStart: []int{},
|
InitContainersToStart: []int{0},
|
||||||
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
ContainersToStart: []int{},
|
||||||
|
ContainersToKill: getKillMapWithInitContainers(basePod, baseStatus, []int{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Kill pod if all containers exited except ephemeral container": {
|
"Kill pod if all containers exited except ephemeral container": {
|
||||||
@@ -2001,6 +2040,15 @@ func TestComputePodActionsWithInitAndEphemeralContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
actions := m.computePodActions(ctx, pod, status)
|
actions := m.computePodActions(ctx, pod, status)
|
||||||
|
if !sidecarContainersEnabled {
|
||||||
|
// If sidecar containers are disabled, we should not see any
|
||||||
|
// InitContainersToStart in the actions.
|
||||||
|
test.actions.InitContainersToStart = nil
|
||||||
|
} else {
|
||||||
|
// If sidecar containers are enabled, we should not see any
|
||||||
|
// NextInitContainerToStart in the actions.
|
||||||
|
test.actions.NextInitContainerToStart = nil
|
||||||
|
}
|
||||||
verifyActions(t, &test.actions, &actions, desc)
|
verifyActions(t, &test.actions, &actions, desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -236,6 +236,23 @@ func (o containerOutputList) HasNotRestarted(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type containerOutputIndex int
|
||||||
|
|
||||||
|
func (i containerOutputIndex) IsBefore(other containerOutputIndex) error {
|
||||||
|
if i >= other {
|
||||||
|
return fmt.Errorf("%d should be before %d", i, other)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o containerOutputList) FindIndex(name string, command string, startIdx containerOutputIndex) (containerOutputIndex, error) {
|
||||||
|
idx := o.findIndex(name, command, int(startIdx))
|
||||||
|
if idx == -1 {
|
||||||
|
return -1, fmt.Errorf("couldn't find %s %s, got\n%v", name, command, o)
|
||||||
|
}
|
||||||
|
return containerOutputIndex(idx), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (o containerOutputList) findIndex(name string, command string, startIdx int) int {
|
func (o containerOutputList) findIndex(name string, command string, startIdx int) int {
|
||||||
for i, v := range o {
|
for i, v := range o {
|
||||||
if i < startIdx {
|
if i < startIdx {
|
||||||
|
@@ -22,8 +22,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
"github.com/onsi/gomega"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||||
admissionapi "k8s.io/pod-security-admission/api"
|
admissionapi "k8s.io/pod-security-admission/api"
|
||||||
|
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
@@ -731,6 +733,146 @@ var _ = SIGDescribe("[NodeConformance] Containers Lifecycle ", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var _ = SIGDescribe("[Serial] Containers Lifecycle ", func() {
|
||||||
|
f := framework.NewDefaultFramework("containers-lifecycle-test-serial")
|
||||||
|
f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged
|
||||||
|
|
||||||
|
ginkgo.It("should restart the containers in right order after the node reboot", func(ctx context.Context) {
|
||||||
|
init1 := "init-1"
|
||||||
|
init2 := "init-2"
|
||||||
|
init3 := "init-3"
|
||||||
|
regular1 := "regular-1"
|
||||||
|
|
||||||
|
podLabels := map[string]string{
|
||||||
|
"test": "containers-lifecycle-test-serial",
|
||||||
|
"namespace": f.Namespace.Name,
|
||||||
|
}
|
||||||
|
pod := &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "initialized-pod",
|
||||||
|
Labels: podLabels,
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
RestartPolicy: v1.RestartPolicyAlways,
|
||||||
|
InitContainers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: init1,
|
||||||
|
Image: busyboxImage,
|
||||||
|
Command: ExecCommand(init1, execCommand{
|
||||||
|
Delay: 5,
|
||||||
|
ExitCode: 0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: init2,
|
||||||
|
Image: busyboxImage,
|
||||||
|
Command: ExecCommand(init2, execCommand{
|
||||||
|
Delay: 5,
|
||||||
|
ExitCode: 0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: init3,
|
||||||
|
Image: busyboxImage,
|
||||||
|
Command: ExecCommand(init3, execCommand{
|
||||||
|
Delay: 5,
|
||||||
|
ExitCode: 0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: regular1,
|
||||||
|
Image: busyboxImage,
|
||||||
|
Command: ExecCommand(regular1, execCommand{
|
||||||
|
Delay: 30,
|
||||||
|
ExitCode: 0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
preparePod(pod)
|
||||||
|
|
||||||
|
client := e2epod.NewPodClient(f)
|
||||||
|
pod = client.Create(ctx, pod)
|
||||||
|
ginkgo.By("Waiting for the pod to be initialized and run")
|
||||||
|
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
ginkgo.By("Getting the current pod sandbox ID")
|
||||||
|
rs, _, err := getCRIClient()
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
sandboxes, err := rs.ListPodSandbox(ctx, &runtimeapi.PodSandboxFilter{
|
||||||
|
LabelSelector: podLabels,
|
||||||
|
})
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
gomega.Expect(sandboxes).To(gomega.HaveLen(1))
|
||||||
|
podSandboxID := sandboxes[0].Id
|
||||||
|
|
||||||
|
ginkgo.By("Stopping the kubelet")
|
||||||
|
restartKubelet := stopKubelet()
|
||||||
|
gomega.Eventually(ctx, func() bool {
|
||||||
|
return kubeletHealthCheck(kubeletHealthCheckURL)
|
||||||
|
}, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalse())
|
||||||
|
|
||||||
|
ginkgo.By("Stopping the pod sandbox to simulate the node reboot")
|
||||||
|
err = rs.StopPodSandbox(ctx, podSandboxID)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
ginkgo.By("Restarting the kubelet")
|
||||||
|
restartKubelet()
|
||||||
|
gomega.Eventually(ctx, func() bool {
|
||||||
|
return kubeletHealthCheck(kubeletHealthCheckURL)
|
||||||
|
}, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrue())
|
||||||
|
|
||||||
|
ginkgo.By("Waiting for the pod to be re-initialized and run")
|
||||||
|
err = e2epod.WaitForPodCondition(ctx, f.ClientSet, pod.Namespace, pod.Name, "re-initialized", f.Timeouts.PodStart, func(pod *v1.Pod) (bool, error) {
|
||||||
|
if pod.Status.ContainerStatuses[0].RestartCount < 2 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if pod.Status.Phase != v1.PodRunning {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
ginkgo.By("Parsing results")
|
||||||
|
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
results := parseOutput(pod)
|
||||||
|
|
||||||
|
ginkgo.By("Analyzing results")
|
||||||
|
init1Started, err := results.FindIndex(init1, "Started", 0)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
init2Started, err := results.FindIndex(init2, "Started", 0)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
init3Started, err := results.FindIndex(init3, "Started", 0)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
regular1Started, err := results.FindIndex(regular1, "Started", 0)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
init1Restarted, err := results.FindIndex(init1, "Started", init1Started+1)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
init2Restarted, err := results.FindIndex(init2, "Started", init2Started+1)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
init3Restarted, err := results.FindIndex(init3, "Started", init3Started+1)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
regular1Restarted, err := results.FindIndex(regular1, "Started", regular1Started+1)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
framework.ExpectNoError(init1Started.IsBefore(init2Started))
|
||||||
|
framework.ExpectNoError(init2Started.IsBefore(init3Started))
|
||||||
|
framework.ExpectNoError(init3Started.IsBefore(regular1Started))
|
||||||
|
|
||||||
|
framework.ExpectNoError(init1Restarted.IsBefore(init2Restarted))
|
||||||
|
framework.ExpectNoError(init2Restarted.IsBefore(init3Restarted))
|
||||||
|
framework.ExpectNoError(init3Restarted.IsBefore(regular1Restarted))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
var _ = SIGDescribe("[NodeAlphaFeature:SidecarContainers] Containers Lifecycle ", func() {
|
var _ = SIGDescribe("[NodeAlphaFeature:SidecarContainers] Containers Lifecycle ", func() {
|
||||||
f := framework.NewDefaultFramework("containers-lifecycle-test")
|
f := framework.NewDefaultFramework("containers-lifecycle-test")
|
||||||
f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged
|
f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged
|
||||||
|
Reference in New Issue
Block a user