1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
15
"github.com/containerd/cgroups/v3"
16
"github.com/containerd/cgroups/v3/cgroup1"
17
"github.com/containerd/cgroups/v3/cgroup2"
18
"github.com/opencontainers/runtime-spec/specs-go"
19
"github.com/stretchr/testify/suite"
20
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
22
"github.com/aenix-io/talm/internal/app/machined/pkg/runtime/logging"
23
"github.com/aenix-io/talm/internal/app/machined/pkg/system/events"
24
"github.com/aenix-io/talm/internal/app/machined/pkg/system/runner"
25
"github.com/aenix-io/talm/internal/app/machined/pkg/system/runner/process"
26
"github.com/aenix-io/talm/internal/pkg/cri"
27
"github.com/siderolabs/talos/pkg/machinery/constants"
31
busyboxImage = "docker.io/library/busybox:1.30.1"
34
func MockEventSink(t *testing.T) func(state events.ServiceState, message string, args ...interface{}) {
35
return func(state events.ServiceState, message string, args ...interface{}) {
36
t.Logf(message, args...)
45
containerdRunner runner.Runner
46
containerdWg sync.WaitGroup
47
containerdAddress string
50
ctx context.Context //nolint:containedctx
51
ctxCancel context.CancelFunc
54
func (suite *CRISuite) SetupSuite() {
55
if cgroups.Mode() == cgroups.Unified {
56
suite.T().Skip("test doesn't pass under cgroupsv2")
61
suite.tmpDir = suite.T().TempDir()
63
stateDir, rootDir := filepath.Join(suite.tmpDir, "state"), filepath.Join(suite.tmpDir, "root")
64
suite.Require().NoError(os.Mkdir(stateDir, 0o777))
65
suite.Require().NoError(os.Mkdir(rootDir, 0o777))
67
if cgroups.Mode() == cgroups.Unified {
70
manager *cgroup2.Manager
73
groupPath, err = cgroup2.NestedGroupPath(suite.tmpDir)
74
suite.Require().NoError(err)
76
manager, err = cgroup2.NewManager(constants.CgroupMountPath, groupPath, &cgroup2.Resources{})
77
suite.Require().NoError(err)
79
defer manager.Delete() //nolint:errcheck
81
var manager cgroup1.Cgroup
83
manager, err = cgroup1.New(cgroup1.NestedPath(suite.tmpDir), &specs.LinuxResources{})
84
suite.Require().NoError(err)
86
defer manager.Delete() //nolint:errcheck
89
suite.containerdAddress = filepath.Join(suite.tmpDir, "run.sock")
93
ProcessArgs: []string{
95
"--address", suite.containerdAddress,
98
"--config", constants.CRIContainerdConfig,
102
suite.containerdRunner = process.NewRunner(
105
runner.WithLoggingManager(logging.NewFileLoggingManager(suite.tmpDir)),
106
runner.WithEnv([]string{"PATH=/bin:" + constants.PATH}),
107
runner.WithCgroupPath(suite.tmpDir),
109
suite.Require().NoError(suite.containerdRunner.Open())
110
suite.containerdWg.Add(1)
113
defer suite.containerdWg.Done()
114
defer suite.containerdRunner.Close() //nolint:errcheck
115
suite.containerdRunner.Run(MockEventSink(suite.T())) //nolint:errcheck
118
suite.client, err = cri.NewClient("unix:"+suite.containerdAddress, 30*time.Second)
119
suite.Require().NoError(err)
122
func (suite *CRISuite) TearDownSuite() {
125
suite.Require().NoError(suite.client.Close())
127
suite.Require().NoError(suite.containerdRunner.Stop())
128
suite.containerdWg.Wait()
131
func (suite *CRISuite) SetupTest() {
132
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second)
135
func (suite *CRISuite) TearDownTest() {
139
func (suite *CRISuite) TestRunSandboxContainer() {
140
podSandboxConfig := &runtimeapi.PodSandboxConfig{
141
Metadata: &runtimeapi.PodSandboxMetadata{
142
Name: "etcd-master-1",
143
Uid: "ed1a599a53090941c9b4025c7e3e883d",
144
Namespace: "kube-system",
147
Labels: map[string]string{
148
"io.kubernetes.pod.name": "etcd-master-1",
149
"io.kubernetes.pod.namespace": "kube-system",
151
LogDirectory: suite.tmpDir,
152
Linux: &runtimeapi.LinuxPodSandboxConfig{
153
SecurityContext: &runtimeapi.LinuxSandboxSecurityContext{
154
NamespaceOptions: &runtimeapi.NamespaceOption{
155
Network: runtimeapi.NamespaceMode_NODE,
161
podSandboxID, err := suite.client.RunPodSandbox(suite.ctx, podSandboxConfig, "")
162
suite.Require().NoError(err)
163
suite.Require().Len(podSandboxID, 64)
165
imageRef, err := suite.client.PullImage(
166
suite.ctx, &runtimeapi.ImageSpec{
170
suite.Require().NoError(err)
172
_, err = suite.client.ImageStatus(
173
suite.ctx, &runtimeapi.ImageSpec{
177
suite.Require().NoError(err)
179
ctrID, err := suite.client.CreateContainer(
180
suite.ctx, podSandboxID,
181
&runtimeapi.ContainerConfig{
182
Metadata: &runtimeapi.ContainerMetadata{
185
Labels: map[string]string{
186
"io.kubernetes.container.name": "etcd",
187
"io.kubernetes.pod.name": "etcd-master-1",
188
"io.kubernetes.pod.namespace": "kube-system",
190
Annotations: map[string]string{
191
"io.kubernetes.container.restartCount": "1",
193
Image: &runtimeapi.ImageSpec{
196
Command: []string{"/bin/sh", "-c", "sleep 3600"},
199
suite.Require().NoError(err)
200
suite.Require().Len(ctrID, 64)
202
err = suite.client.StartContainer(suite.ctx, ctrID)
203
suite.Require().NoError(err)
205
_, err = suite.client.ContainerStats(suite.ctx, ctrID)
206
suite.Require().NoError(err)
208
_, _, err = suite.client.ContainerStatus(suite.ctx, ctrID, true)
209
suite.Require().NoError(err)
211
err = suite.client.StopContainer(suite.ctx, ctrID, 10)
212
suite.Require().NoError(err)
214
err = suite.client.RemoveContainer(suite.ctx, ctrID)
215
suite.Require().NoError(err)
217
err = suite.client.StopPodSandbox(suite.ctx, podSandboxID)
218
suite.Require().NoError(err)
220
err = suite.client.RemovePodSandbox(suite.ctx, podSandboxID)
221
suite.Require().NoError(err)
224
func (suite *CRISuite) TestList() {
225
pods, err := suite.client.ListPodSandbox(suite.ctx, &runtimeapi.PodSandboxFilter{})
226
suite.Require().NoError(err)
227
suite.Require().Len(pods, 0)
229
containers, err := suite.client.ListContainers(suite.ctx, &runtimeapi.ContainerFilter{})
230
suite.Require().NoError(err)
231
suite.Require().Len(containers, 0)
233
containerStats, err := suite.client.ListContainerStats(suite.ctx, &runtimeapi.ContainerStatsFilter{})
234
suite.Require().NoError(err)
235
suite.Require().Len(containerStats, 0)
237
_, err = suite.client.ListImages(suite.ctx, &runtimeapi.ImageFilter{})
238
suite.Require().NoError(err)
241
func TestCRISuite(t *testing.T) {
242
if os.Getuid() != 0 {
243
t.Skip("can't run the test as non-root")
246
_, err := os.Stat("/bin/containerd")
248
t.Skip("containerd binary is not available, skipping the test")
251
suite.Run(t, new(CRISuite))