From 0b8471953764e4e490a11d77db0386ec622c6642 Mon Sep 17 00:00:00 2001 From: Michael Zappa Date: Thu, 19 Feb 2026 15:12:12 -0700 Subject: [PATCH 1/3] fix issue where cni del is never executed Signed-off-by: Michael Zappa --- internal/cri/server/sandbox_stop.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/cri/server/sandbox_stop.go b/internal/cri/server/sandbox_stop.go index b44e74e7c56e2..52c74cbe6647e 100644 --- a/internal/cri/server/sandbox_stop.go +++ b/internal/cri/server/sandbox_stop.go @@ -118,6 +118,9 @@ func (c *criService) stopPodSandbox(ctx context.Context, sandbox sandboxstore.Sa if err := c.teardownPodNetwork(ctx, sandbox); err != nil { return fmt.Errorf("failed to destroy network for sandbox %q: %w", id, err) } + } else { + log.G(ctx).Warnf("CNI result is not found for sandbox %q, attempting teardown anyway, ignoring errors", id) + _ = c.teardownPodNetwork(ctx, sandbox) } if err := sandbox.NetNS.Remove(); err != nil { return fmt.Errorf("failed to remove network namespace for sandbox %q: %w", id, err) From 1092b85a8ce13fb0ec72b1e232b6a781ccef9214 Mon Sep 17 00:00:00 2001 From: Michael Zappa Date: Fri, 20 Feb 2026 08:05:53 -0700 Subject: [PATCH 2/3] address comment Signed-off-by: Michael Zappa --- internal/cri/server/sandbox_stop.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/cri/server/sandbox_stop.go b/internal/cri/server/sandbox_stop.go index 52c74cbe6647e..dacb7149a2392 100644 --- a/internal/cri/server/sandbox_stop.go +++ b/internal/cri/server/sandbox_stop.go @@ -114,13 +114,12 @@ func (c *criService) stopPodSandbox(ctx context.Context, sandbox sandboxstore.Sa } else if closed { sandbox.NetNSPath = "" } - if sandbox.CNIResult != nil { - if err := c.teardownPodNetwork(ctx, sandbox); err != nil { + if err := c.teardownPodNetwork(ctx, sandbox); err != nil { + if sandbox.CNIResult != nil { return fmt.Errorf("failed to destroy network for sandbox %q: %w", id, err) + } else { + log.G(ctx).WithError(err).Warnf("failed to destroy network for sandbox %q; and ignoring because the sandbox network setup result is nil indicating the network setup never completed", id) } - } else { - log.G(ctx).Warnf("CNI result is not found for sandbox %q, attempting teardown anyway, ignoring errors", id) - _ = c.teardownPodNetwork(ctx, sandbox) } if err := sandbox.NetNS.Remove(); err != nil { return fmt.Errorf("failed to remove network namespace for sandbox %q: %w", id, err) From 96dee5f6440d147042aa61d7da2a7d10bcabbce5 Mon Sep 17 00:00:00 2001 From: Michael Zappa Date: Fri, 20 Feb 2026 08:44:16 -0700 Subject: [PATCH 3/3] add integration test for cni result nil Signed-off-by: Michael Zappa --- integration/sandbox_clean_remove_test.go | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/integration/sandbox_clean_remove_test.go b/integration/sandbox_clean_remove_test.go index 5b369d8ee540d..1e35c9bd587c3 100644 --- a/integration/sandbox_clean_remove_test.go +++ b/integration/sandbox_clean_remove_test.go @@ -22,6 +22,8 @@ import ( "os" "path/filepath" "strings" + "sync" + "syscall" "testing" "time" @@ -123,3 +125,67 @@ func TestSandboxRemoveWithoutIPLeakage(t *testing.T) { t.Logf("Should not be able to find the pod ip in host-local checkpoint") assert.False(t, checkIP(ip)) } + +// TestSandboxStopWithNilCNIResult verifies that StopPodSandbox succeeds when +// CNIResult is nil (network setup never completed) even if the network teardown +// itself fails. This exercises the condition in sandbox_stop.go where a nil +// CNIResult causes the teardown error to be logged as a warning instead of +// returned as a hard error. +func TestSandboxStopWithNilCNIResult(t *testing.T) { + t.Log("Init PodSandboxConfig with specific label") + sbName := t.Name() + labels := map[string]string{ + sbName: "true", + } + sbConfig := PodSandboxConfig(sbName, "failpoint", WithPodLabels(labels)) + + t.Log("Inject CNI failpoint: delay Add (so CNIResult is never set) and fail Del") + conf := &failpointConf{ + // Delay CNI Add for 1 day so network setup never completes and CNIResult stays nil + Add: "1*delay(86400000)", + // Make CNI Del fail so teardownPodNetwork returns an error during stop + Del: "1*error(network-teardown-injected-error)", + } + injectCNIFailpoint(t, sbConfig, conf) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + t.Log("Create a sandbox (will hang on CNI Add)") + _, err := runtimeService.RunPodSandbox(sbConfig, failpointRuntimeHandler) + assert.Error(t, err) + }() + + t.Log("Wait for CNI Add to start running") + assert.NoError(t, ensureCNIAddRunning(t, sbName), "CNI Add should be running") + + // Kill containerd while CNI Add is in progress so CNIResult is never set + // and no deferred cleanup runs. + t.Log("Kill containerd with SIGKILL to leave sandbox with nil CNIResult") + RestartContainerd(t, syscall.SIGKILL) + + wg.Wait() + + t.Log("ListPodSandbox with the specific label") + l, err := runtimeService.ListPodSandbox(&runtime.PodSandboxFilter{LabelSelector: labels}) + require.NoError(t, err) + require.Len(t, l, 1) + + sb := l[0] + require.Equal(t, runtime.PodSandboxState_SANDBOX_NOTREADY, sb.State) + + t.Log("Get sandbox info and verify CNIResult is nil") + _, info, err := SandboxInfo(sb.Id) + require.NoError(t, err) + require.Nil(t, info.CNIResult, "CNIResult should be nil because CNI setup never completed") + + t.Log("StopPodSandbox should succeed even though CNI Del fails, because CNIResult is nil") + err = runtimeService.StopPodSandbox(sb.Id) + assert.NoError(t, err, "StopPodSandbox should not return error when CNIResult is nil") + + t.Log("RemovePodSandbox should succeed") + err = runtimeService.RemovePodSandbox(sb.Id) + assert.NoError(t, err) +}