1// +build linux
2
3/*
4   Copyright The containerd Authors.
5
6   Licensed under the Apache License, Version 2.0 (the "License");
7   you may not use this file except in compliance with the License.
8   You may obtain a copy of the License at
9
10       http://www.apache.org/licenses/LICENSE-2.0
11
12   Unless required by applicable law or agreed to in writing, software
13   distributed under the License is distributed on an "AS IS" BASIS,
14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   See the License for the specific language governing permissions and
16   limitations under the License.
17*/
18
19package integration
20
21import (
22	"context"
23	"testing"
24	"time"
25
26	"github.com/stretchr/testify/assert"
27	"github.com/stretchr/testify/require"
28	runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
29)
30
31func TestSharedPidMultiProcessContainerStop(t *testing.T) {
32	for name, sbConfig := range map[string]*runtime.PodSandboxConfig{
33		"hostpid": PodSandboxConfig("sandbox", "host-pid-container-stop", WithHostPid),
34		"podpid":  PodSandboxConfig("sandbox", "pod-pid-container-stop", WithPodPid),
35	} {
36		t.Run(name, func(t *testing.T) {
37			t.Log("Create a shared pid sandbox")
38			sb, err := runtimeService.RunPodSandbox(sbConfig, *runtimeHandler)
39			require.NoError(t, err)
40			defer func() {
41				assert.NoError(t, runtimeService.StopPodSandbox(sb))
42				assert.NoError(t, runtimeService.RemovePodSandbox(sb))
43			}()
44
45			var (
46				testImage     = GetImage(BusyBox)
47				containerName = "test-container"
48			)
49			t.Logf("Pull test image %q", testImage)
50			img, err := imageService.PullImage(&runtime.ImageSpec{Image: testImage}, nil, sbConfig)
51			require.NoError(t, err)
52			defer func() {
53				assert.NoError(t, imageService.RemoveImage(&runtime.ImageSpec{Image: img}))
54			}()
55
56			t.Log("Create a multi-process container")
57			cnConfig := ContainerConfig(
58				containerName,
59				testImage,
60				WithCommand("sh", "-c", "sleep 10000 & sleep 10000"),
61			)
62			cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
63			require.NoError(t, err)
64
65			t.Log("Start the container")
66			require.NoError(t, runtimeService.StartContainer(cn))
67
68			t.Log("Stop the container")
69			require.NoError(t, runtimeService.StopContainer(cn, 0))
70
71			t.Log("The container state should be exited")
72			s, err := runtimeService.ContainerStatus(cn)
73			require.NoError(t, err)
74			assert.Equal(t, s.GetState(), runtime.ContainerState_CONTAINER_EXITED)
75		})
76	}
77}
78
79func TestContainerStopCancellation(t *testing.T) {
80	t.Log("Create a pod sandbox")
81	sbConfig := PodSandboxConfig("sandbox", "cancel-container-stop")
82	sb, err := runtimeService.RunPodSandbox(sbConfig, *runtimeHandler)
83	require.NoError(t, err)
84	defer func() {
85		assert.NoError(t, runtimeService.StopPodSandbox(sb))
86		assert.NoError(t, runtimeService.RemovePodSandbox(sb))
87	}()
88
89	var (
90		testImage     = GetImage(BusyBox)
91		containerName = "test-container"
92	)
93	t.Logf("Pull test image %q", testImage)
94	img, err := imageService.PullImage(&runtime.ImageSpec{Image: testImage}, nil, sbConfig)
95	require.NoError(t, err)
96	defer func() {
97		assert.NoError(t, imageService.RemoveImage(&runtime.ImageSpec{Image: img}))
98	}()
99
100	t.Log("Create a container which traps sigterm")
101	cnConfig := ContainerConfig(
102		containerName,
103		testImage,
104		WithCommand("sh", "-c", `trap "echo ignore sigterm" TERM; sleep 1000`),
105	)
106	cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
107	require.NoError(t, err)
108
109	t.Log("Start the container")
110	require.NoError(t, runtimeService.StartContainer(cn))
111
112	t.Log("Stop the container with 3s timeout, but 1s context timeout")
113	// Note that with container pid namespace, the sleep process
114	// is pid 1, and SIGTERM sent by `StopContainer` will be ignored.
115	rawClient, err := RawRuntimeClient()
116	require.NoError(t, err)
117	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
118	defer cancel()
119	_, err = rawClient.StopContainer(ctx, &runtime.StopContainerRequest{
120		ContainerId: cn,
121		Timeout:     3,
122	})
123	assert.Error(t, err)
124
125	t.Log("The container should still be running even after 5 seconds")
126	assert.NoError(t, Consistently(func() (bool, error) {
127		s, err := runtimeService.ContainerStatus(cn)
128		if err != nil {
129			return false, err
130		}
131		return s.GetState() == runtime.ContainerState_CONTAINER_RUNNING, nil
132	}, 100*time.Millisecond, 5*time.Second))
133
134	t.Log("Stop the container with 1s timeout, without shorter context timeout")
135	assert.NoError(t, runtimeService.StopContainer(cn, 1))
136
137	t.Log("The container state should be exited")
138	s, err := runtimeService.ContainerStatus(cn)
139	require.NoError(t, err)
140	assert.Equal(t, s.GetState(), runtime.ContainerState_CONTAINER_EXITED)
141}
142