mirror of
https://github.com/containerd/containerd.git
synced 2026-11-06 05:17:02 +00:00
190 lines
6.0 KiB
Go
190 lines
6.0 KiB
Go
//go:build linux
|
|
|
|
/*
|
|
Copyright The containerd 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 integration
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/unix"
|
|
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
|
|
)
|
|
|
|
func TestSandboxRemoveWithoutIPLeakage(t *testing.T) {
|
|
const hostLocalCheckpointDir = "/var/lib/cni"
|
|
|
|
t.Logf("Make sure host-local ipam is in use")
|
|
config, err := CRIConfig()
|
|
require.NoError(t, err)
|
|
fs, err := os.ReadDir(config.NetworkPluginConfDir)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, fs)
|
|
f := filepath.Join(config.NetworkPluginConfDir, fs[0].Name())
|
|
cniConfig, err := os.ReadFile(f)
|
|
require.NoError(t, err)
|
|
if !strings.Contains(string(cniConfig), "host-local") {
|
|
t.Skip("host-local ipam is not in use")
|
|
}
|
|
|
|
t.Logf("Create a sandbox")
|
|
sbConfig := PodSandboxConfig("sandbox", "remove-without-ip-leakage")
|
|
sb, err := runtimeService.RunPodSandbox(sbConfig, *runtimeHandler)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
// Make sure the sandbox is cleaned up in any case.
|
|
runtimeService.StopPodSandbox(sb)
|
|
runtimeService.RemovePodSandbox(sb)
|
|
}()
|
|
|
|
t.Logf("Get pod information")
|
|
status, info, err := SandboxInfo(sb)
|
|
require.NoError(t, err)
|
|
ip := status.GetNetwork().GetIp()
|
|
require.NotEmpty(t, ip)
|
|
require.NotNil(t, info.RuntimeSpec.Linux)
|
|
var netNS string
|
|
for _, n := range info.RuntimeSpec.Linux.Namespaces {
|
|
if n.Type == runtimespec.NetworkNamespace {
|
|
netNS = n.Path
|
|
}
|
|
}
|
|
require.NotEmpty(t, netNS, "network namespace should be set")
|
|
|
|
t.Logf("Should be able to find the pod ip in host-local checkpoint")
|
|
checkIP := func(ip string) bool {
|
|
found := false
|
|
filepath.Walk(hostLocalCheckpointDir, func(_ string, info os.FileInfo, _ error) error {
|
|
if info != nil && info.Name() == ip {
|
|
found = true
|
|
}
|
|
return nil
|
|
})
|
|
return found
|
|
}
|
|
require.True(t, checkIP(ip))
|
|
|
|
t.Logf("Kill sandbox container")
|
|
require.NoError(t, KillPid(int(info.Pid)))
|
|
|
|
t.Logf("Unmount network namespace")
|
|
require.NoError(t, unix.Unmount(netNS, unix.MNT_DETACH))
|
|
|
|
t.Logf("Network namespace should be closed")
|
|
_, info, err = SandboxInfo(sb)
|
|
require.NoError(t, err)
|
|
assert.True(t, info.NetNSClosed)
|
|
|
|
t.Logf("Remove network namespace")
|
|
require.NoError(t, os.RemoveAll(netNS))
|
|
|
|
t.Logf("Network namespace should still be closed")
|
|
_, info, err = SandboxInfo(sb)
|
|
require.NoError(t, err)
|
|
assert.True(t, info.NetNSClosed)
|
|
|
|
t.Logf("Sandbox state should be NOTREADY")
|
|
assert.NoError(t, Eventually(func() (bool, error) {
|
|
status, err := runtimeService.PodSandboxStatus(sb)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return status.GetState() == runtime.PodSandboxState_SANDBOX_NOTREADY, nil
|
|
}, time.Second, 30*time.Second), "sandbox state should become NOTREADY")
|
|
|
|
t.Logf("Should still be able to find the pod ip in host-local checkpoint")
|
|
assert.True(t, checkIP(ip))
|
|
|
|
t.Logf("Should be able to stop and remove the sandbox")
|
|
assert.NoError(t, runtimeService.StopPodSandbox(sb))
|
|
assert.NoError(t, runtimeService.RemovePodSandbox(sb))
|
|
|
|
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.Go(func() {
|
|
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)
|
|
}
|