Eng Zer Jun fb058a7c92
test: use T.TempDir to create temporary test directory
This commit replaces `ioutil.TempDir` with `t.TempDir` in tests. The
directory created by `t.TempDir` is automatically removed when the test
and all its subtests complete.

Prior to this commit, temporary directory created using `ioutil.TempDir`
needs to be removed manually by calling `os.RemoveAll`, which is omitted
in some tests. The error handling boilerplate e.g.
	defer func() {
		if err := os.RemoveAll(dir); err != nil {
			t.Fatal(err)
		}
	}
is also tedious, but `t.TempDir` handles this for us nicely.

Reference: https://pkg.go.dev/testing#T.TempDir
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2022-08-03 16:31:55 +04:00

298 lines
8.9 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package cri_test
import (
"context"
"os"
"path/filepath"
"runtime/debug"
"sync"
"testing"
"time"
"github.com/containerd/cgroups"
cgroupsv2 "github.com/containerd/cgroups/v2"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/suite"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/logging"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/events"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/process"
ctrs "github.com/talos-systems/talos/internal/pkg/containers"
"github.com/talos-systems/talos/internal/pkg/containers/cri"
criclient "github.com/talos-systems/talos/internal/pkg/cri"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
const (
busyboxImage = "docker.io/library/busybox:1.30.1"
// busyboxImageDigest = "sha256:64f5d945efcc0f39ab11b3cd4ba403cc9fefe1fa3613123ca016cf3708e8cafb".
// pauseImage = "k8s.gcr.io/pause:3.1".
// pauseImageDigest = "sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e".
)
func MockEventSink(state events.ServiceState, message string, args ...interface{}) {
}
type CRISuite struct {
suite.Suite
tmpDir string
containerdRunner runner.Runner
containerdWg sync.WaitGroup
containerdAddress string
client *criclient.Client
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
inspector ctrs.Inspector
pods []string
}
func (suite *CRISuite) SetupSuite() {
if cgroups.Mode() == cgroups.Unified {
suite.T().Skip("test doesn't pass under cgroupsv2")
}
var err error
suite.tmpDir = suite.T().TempDir()
stateDir, rootDir := filepath.Join(suite.tmpDir, "state"), filepath.Join(suite.tmpDir, "root")
suite.Require().NoError(os.Mkdir(stateDir, 0o777))
suite.Require().NoError(os.Mkdir(rootDir, 0o777))
suite.containerdAddress = filepath.Join(suite.tmpDir, "run.sock")
if cgroups.Mode() == cgroups.Unified {
var (
groupPath string
manager *cgroupsv2.Manager
)
groupPath, err = cgroupsv2.NestedGroupPath(suite.tmpDir)
suite.Require().NoError(err)
manager, err = cgroupsv2.NewManager(constants.CgroupMountPath, groupPath, &cgroupsv2.Resources{})
suite.Require().NoError(err)
defer manager.Delete() //nolint:errcheck
} else {
var manager cgroups.Cgroup
manager, err = cgroups.New(cgroups.V1, cgroups.NestedPath(suite.tmpDir), &specs.LinuxResources{})
suite.Require().NoError(err)
defer manager.Delete() //nolint:errcheck
}
args := &runner.Args{
ID: "containerd",
ProcessArgs: []string{
"/bin/containerd",
"--address", suite.containerdAddress,
"--state", stateDir,
"--root", rootDir,
"--config", constants.CRIContainerdConfig,
},
}
suite.containerdRunner = process.NewRunner(
false,
args,
runner.WithLoggingManager(logging.NewFileLoggingManager(suite.tmpDir)),
runner.WithEnv([]string{"PATH=/bin:" + constants.PATH}),
runner.WithCgroupPath(suite.tmpDir),
)
suite.Require().NoError(suite.containerdRunner.Open())
suite.containerdWg.Add(1)
go func() {
defer suite.containerdWg.Done()
defer suite.containerdRunner.Close() //nolint:errcheck
suite.containerdRunner.Run(MockEventSink) //nolint:errcheck
}()
suite.client, err = criclient.NewClient("unix:"+suite.containerdAddress, 30*time.Second)
suite.Require().NoError(err)
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second)
suite.inspector, err = cri.NewInspector(suite.ctx, cri.WithCRIEndpoint("unix:"+suite.containerdAddress))
suite.Require().NoError(err)
}
func (suite *CRISuite) TearDownSuite() {
suite.ctxCancel()
suite.Require().NoError(suite.inspector.Close())
suite.Require().NoError(suite.client.Close())
suite.Require().NoError(suite.containerdRunner.Stop())
suite.containerdWg.Wait()
}
func (suite *CRISuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second)
suite.pods = nil
podSandboxConfig := &runtimeapi.PodSandboxConfig{
Metadata: &runtimeapi.PodSandboxMetadata{
Name: "etcd-master-1",
Uid: "ed1a599a53090941c9b4025c7e3e883d",
Namespace: "kube-system",
Attempt: 0,
},
Labels: map[string]string{
"io.kubernetes.pod.name": "etcd-master-1",
"io.kubernetes.pod.namespace": "kube-system",
},
LogDirectory: suite.tmpDir,
Linux: &runtimeapi.LinuxPodSandboxConfig{
SecurityContext: &runtimeapi.LinuxSandboxSecurityContext{
NamespaceOptions: &runtimeapi.NamespaceOption{
Network: runtimeapi.NamespaceMode_NODE,
},
},
},
}
podSandboxID, err := suite.client.RunPodSandbox(suite.ctx, podSandboxConfig, "")
suite.Require().NoError(err)
suite.pods = append(suite.pods, podSandboxID)
suite.Require().Len(podSandboxID, 64)
imageRef, err := suite.client.PullImage(
suite.ctx, &runtimeapi.ImageSpec{
Image: busyboxImage,
}, podSandboxConfig,
)
suite.Require().NoError(err)
ctrID, err := suite.client.CreateContainer(
suite.ctx, podSandboxID,
&runtimeapi.ContainerConfig{
Metadata: &runtimeapi.ContainerMetadata{
Name: "etcd",
},
Labels: map[string]string{
"io.kubernetes.container.name": "etcd",
"io.kubernetes.pod.name": "etcd-master-1",
"io.kubernetes.pod.namespace": "kube-system",
},
Annotations: map[string]string{
"io.kubernetes.container.restartCount": "1",
},
Image: &runtimeapi.ImageSpec{
Image: imageRef,
},
Command: []string{"/bin/sh", "-c", "sleep 3600"},
}, podSandboxConfig,
)
suite.Require().NoError(err)
suite.Require().Len(ctrID, 64)
err = suite.client.StartContainer(suite.ctx, ctrID)
suite.Require().NoError(err)
}
func (suite *CRISuite) TearDownTest() {
for _, pod := range suite.pods {
suite.Require().NoError(suite.client.StopPodSandbox(suite.ctx, pod))
suite.Require().NoError(suite.client.RemovePodSandbox(suite.ctx, pod))
}
suite.ctxCancel()
}
func (suite *CRISuite) TestPods() {
pods, err := suite.inspector.Pods()
suite.Require().NoError(err)
suite.Require().Len(pods, 1)
suite.Assert().Equal("kube-system/etcd-master-1", pods[0].Name)
suite.Require().Len(pods[0].Containers, 2)
suite.Assert().Equal(pods[0].Name, pods[0].Containers[0].Display)
suite.Assert().Equal(pods[0].Name, pods[0].Containers[0].Name)
suite.Assert().Equal("SANDBOX_READY", pods[0].Containers[0].Status)
// suite.Assert().Equal(pauseImageDigest, pods[0].Containers[0].Digest)
// suite.Assert().Equal(pauseImage, pods[0].Containers[0].Image)
suite.Assert().True(pods[0].Containers[0].Pid > 0)
suite.Assert().Equal("kube-system/etcd-master-1:etcd", pods[0].Containers[1].Display)
suite.Assert().Equal("etcd", pods[0].Containers[1].Name)
// suite.Assert().Equal(busyboxImage, pods[0].Containers[1].Image)
// suite.Assert().Equal(busyboxImageDigest, pods[0].Containers[1].Digest)
suite.Assert().Equal("CONTAINER_RUNNING", pods[0].Containers[1].Status)
suite.Assert().Equal("1", pods[0].Containers[1].RestartCount)
suite.Assert().True(pods[0].Containers[1].Pid > 0)
}
func (suite *CRISuite) TestContainer() {
defer func() {
r := recover()
if r != nil {
t := suite.T()
t.Errorf("test panicked: %v %s", r, debug.Stack())
t.FailNow()
}
}()
container, err := suite.inspector.Container("kube-system/etcd-master-1")
suite.Require().NoError(err)
suite.Assert().Equal("kube-system/etcd-master-1", container.Display)
suite.Assert().Equal(container.Display, container.Name)
suite.Assert().Equal("SANDBOX_READY", container.Status)
// suite.Assert().Equal(pauseImageDigest, container.Digest)
suite.Assert().True(container.Pid > 0)
container, err = suite.inspector.Container("kube-system/etcd-master-1:etcd")
suite.Require().NoError(err)
suite.Assert().Equal("kube-system/etcd-master-1:etcd", container.Display)
suite.Assert().Equal("etcd", container.Name)
suite.Assert().Equal("CONTAINER_RUNNING", container.Status)
// suite.Assert().Equal(busyboxImageDigest, container.Image)
// suite.Assert().Equal(busyboxImageDigest, container.Digest)
suite.Assert().Equal("1", container.RestartCount)
suite.Assert().True(container.Pid > 0)
container, err = suite.inspector.Container("kube-system/etcd-master-1:etcd2")
suite.Require().NoError(err)
suite.Require().Nil(container)
container, err = suite.inspector.Container("kube-system/etcd-master-2")
suite.Require().NoError(err)
suite.Require().Nil(container)
container, err = suite.inspector.Container("talos")
suite.Require().NoError(err)
suite.Require().Nil(container)
}
func TestCRISuite(t *testing.T) {
if os.Getuid() != 0 {
t.Skip("can't run the test as non-root")
}
_, err := os.Stat("/bin/containerd")
if err != nil {
t.Skip("containerd binary is not available, skipping the test")
}
suite.Run(t, new(CRISuite))
}