diff --git a/pkg/server/events.go b/pkg/server/events.go index 8d4d82998..bb28fd04a 100644 --- a/pkg/server/events.go +++ b/pkg/server/events.go @@ -49,6 +49,7 @@ func (c *criContainerdService) startEventMonitor() error { func (c *criContainerdService) handleEventStream(events execution.ContainerService_EventsClient) { // TODO(random-liu): [P1] Should backoff on this error, or else this will // cause a busy loop. + // TODO(random-liu): Handle io.EOF. e, err := events.Recv() if err != nil { glog.Errorf("Failed to receive event: %v", err) diff --git a/pkg/server/helpers.go b/pkg/server/helpers.go index 3850b2066..ee8ae1076 100644 --- a/pkg/server/helpers.go +++ b/pkg/server/helpers.go @@ -55,6 +55,9 @@ const ( ) const ( + // defaultSandboxImage is the image used by sandbox container. + // TODO(random-liu): [P1] Build schema 2 pause image and use it here. + defaultSandboxImage = "gcr.io/google.com/noogler-kubernetes/pause-amd64:3.0" // relativeRootfsPath is the rootfs path relative to bundle path. relativeRootfsPath = "rootfs" // defaultRuntime is the runtime to use in containerd. We may support @@ -305,31 +308,39 @@ func getRepoDigestAndTag(namedRef reference.Named, digest imagedigest.Digest) (s return repoDigest, repoTag } -// localResolve resolves image reference to image id locally. It returns empty string -// without error if the reference doesn't exist. -func (c *criContainerdService) localResolve(ctx context.Context, ref string) (string, error) { +// localResolve resolves image reference locally and returns corresponding image metadata. It returns +// nil without error if the reference doesn't exist. +func (c *criContainerdService) localResolve(ctx context.Context, ref string) (*metadata.ImageMetadata, error) { _, err := imagedigest.Parse(ref) - if err == nil { - return ref, nil - } - // ref is not image id, try to resolve it locally. - normalized, err := normalizeImageRef(ref) if err != nil { - return "", fmt.Errorf("invalid image reference %q: %v", ref, err) - } - image, err := c.imageStoreService.Get(ctx, normalized.String()) - if err != nil { - if images.IsNotFound(err) { - return "", nil + // ref is not image id, try to resolve it locally. + normalized, err := normalizeImageRef(ref) + if err != nil { + return nil, fmt.Errorf("invalid image reference %q: %v", ref, err) } - return "", fmt.Errorf("an error occurred when getting image %q from containerd image store: %v", - normalized.String(), err) + image, err := c.imageStoreService.Get(ctx, normalized.String()) + if err != nil { + if images.IsNotFound(err) { + return nil, nil + } + return nil, fmt.Errorf("an error occurred when getting image %q from containerd image store: %v", + normalized.String(), err) + } + desc, err := image.Config(ctx, c.contentStoreService) + if err != nil { + return nil, fmt.Errorf("failed to get image config descriptor: %v", err) + } + ref = desc.Digest.String() } - desc, err := image.Config(ctx, c.contentStoreService) + imageID := ref + meta, err := c.imageMetadataStore.Get(imageID) if err != nil { - return "", fmt.Errorf("failed to get image config descriptor: %v", err) + if metadata.IsNotExistError(err) { + return nil, nil + } + return nil, fmt.Errorf("failed to get image %q metadata: %v", imageID, err) } - return desc.Digest.String(), nil + return meta, nil } // getUserFromImage gets uid or user name of the image user. @@ -350,3 +361,27 @@ func getUserFromImage(user string) (*int64, string) { // If user is a numeric uid. return &uid, "" } + +// ensureImageExists returns corresponding metadata of the image reference, if image is not +// pulled yet, the function will pull the image. +func (c *criContainerdService) ensureImageExists(ctx context.Context, ref string) (*metadata.ImageMetadata, error) { + meta, err := c.localResolve(ctx, ref) + if err != nil { + return nil, fmt.Errorf("failed to resolve image %q: %v", ref, err) + } + if meta != nil { + return meta, nil + } + // Pull image to ensure the image exists + resp, err := c.PullImage(ctx, &runtime.PullImageRequest{Image: &runtime.ImageSpec{Image: ref}}) + if err != nil { + return nil, fmt.Errorf("failed to pull image %q: %v", ref, err) + } + imageID := resp.GetImageRef() + meta, err = c.imageMetadataStore.Get(imageID) + if err != nil { + // It's still possible that someone removed the image right after it is pulled. + return nil, fmt.Errorf("failed to get image %q metadata after pulling: %v", imageID, err) + } + return meta, nil +} diff --git a/pkg/server/image_remove.go b/pkg/server/image_remove.go index aeaed2328..69b9886bd 100644 --- a/pkg/server/image_remove.go +++ b/pkg/server/image_remove.go @@ -41,21 +41,14 @@ func (c *criContainerdService) RemoveImage(ctx context.Context, r *runtime.Remov glog.V(2).Infof("RemoveImage %q returns successfully", r.GetImage().GetImage()) } }() - imageID, err := c.localResolve(ctx, r.GetImage().GetImage()) + meta, err := c.localResolve(ctx, r.GetImage().GetImage()) if err != nil { return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err) } - if imageID == "" { + if meta == nil { // return empty without error when image not found. return &runtime.RemoveImageResponse{}, nil } - meta, err := c.imageMetadataStore.Get(imageID) - if err != nil { - if metadata.IsNotExistError(err) { - return &runtime.RemoveImageResponse{}, nil - } - return nil, fmt.Errorf("an error occurred when get image %q metadata: %v", imageID, err) - } // Also include repo digest, because if user pull image with digest, // there will also be a corresponding repo digest reference. for _, ref := range append(meta.RepoTags, meta.RepoDigests...) { @@ -65,14 +58,14 @@ func (c *criContainerdService) RemoveImage(ctx context.Context, r *runtime.Remov if err == nil || images.IsNotFound(err) { continue } - return nil, fmt.Errorf("failed to delete image reference %q for image %q: %v", ref, imageID, err) + return nil, fmt.Errorf("failed to delete image reference %q for image %q: %v", ref, meta.ID, err) } - err = c.imageMetadataStore.Delete(imageID) + err = c.imageMetadataStore.Delete(meta.ID) if err != nil { if metadata.IsNotExistError(err) { return &runtime.RemoveImageResponse{}, nil } - return nil, fmt.Errorf("an error occurred when delete image %q matadata: %v", imageID, err) + return nil, fmt.Errorf("an error occurred when delete image %q matadata: %v", meta.ID, err) } return &runtime.RemoveImageResponse{}, nil } diff --git a/pkg/server/image_status.go b/pkg/server/image_status.go index 5ef2a1633..39e5adc3b 100644 --- a/pkg/server/image_status.go +++ b/pkg/server/image_status.go @@ -22,8 +22,6 @@ import ( "github.com/golang/glog" "golang.org/x/net/context" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" - - "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" ) // ImageStatus returns the status of the image, returns nil if the image isn't present. @@ -37,23 +35,14 @@ func (c *criContainerdService) ImageStatus(ctx context.Context, r *runtime.Image r.GetImage().GetImage(), retRes.GetImage()) } }() - imageID, err := c.localResolve(ctx, r.GetImage().GetImage()) + meta, err := c.localResolve(ctx, r.GetImage().GetImage()) if err != nil { return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err) } - if imageID == "" { + if meta == nil { // return empty without error when image not found. return &runtime.ImageStatusResponse{}, nil } - - meta, err := c.imageMetadataStore.Get(imageID) - if err != nil { - if metadata.IsNotExistError(err) { - return &runtime.ImageStatusResponse{}, nil - } - return nil, fmt.Errorf("an error occurred during get image %q metadata: %v", - imageID, err) - } // TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot // doesn't exist? runtimeImage := &runtime.Image{ diff --git a/pkg/server/sandbox_remove.go b/pkg/server/sandbox_remove.go index bc03050be..e40c1d944 100644 --- a/pkg/server/sandbox_remove.go +++ b/pkg/server/sandbox_remove.go @@ -35,7 +35,7 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime. glog.V(2).Infof("RemovePodSandbox for sandbox %q", r.GetPodSandboxId()) defer func() { if retErr == nil { - glog.V(2).Info("RemovePodSandbox %q returns successfully", r.GetPodSandboxId()) + glog.V(2).Infof("RemovePodSandbox %q returns successfully", r.GetPodSandboxId()) } }() @@ -65,6 +65,7 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime. return nil, fmt.Errorf("sandbox container %q is not fully stopped", id) } + // TODO(random-liu): [P0] Cleanup snapshot after switching to new snapshot api. // TODO(random-liu): [P0] Cleanup shm created in RunPodSandbox. // TODO(random-liu): [P1] Remove permanent namespace once used. diff --git a/pkg/server/sandbox_run.go b/pkg/server/sandbox_run.go index e6df9e1d4..64d03e744 100644 --- a/pkg/server/sandbox_run.go +++ b/pkg/server/sandbox_run.go @@ -21,17 +21,18 @@ import ( "fmt" "io" "io/ioutil" + "strings" "time" + "github.com/containerd/containerd/api/services/execution" + rootfsapi "github.com/containerd/containerd/api/services/rootfs" prototypes "github.com/gogo/protobuf/types" "github.com/golang/glog" + imagedigest "github.com/opencontainers/go-digest" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "golang.org/x/net/context" - - "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/mount" - runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" @@ -81,10 +82,22 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run Config: config, } - // TODO(random-liu): [P0] Ensure pause image snapshot, apply default image config - // and get snapshot mounts. - // Use fixed rootfs path and sleep command. - const rootPath = "/" + // Ensure sandbox container image snapshot. + imageMeta, err := c.ensureImageExists(ctx, c.sandboxImage) + if err != nil { + return nil, fmt.Errorf("failed to get sandbox image %q: %v", defaultSandboxImage, err) + } + prepareResp, err := c.rootfsService.Prepare(ctx, &rootfsapi.PrepareRequest{ + Name: id, + // We are sure that ChainID must be a digest. + ChainID: imagedigest.Digest(imageMeta.ChainID), + Readonly: true, + }) + if err != nil { + return nil, fmt.Errorf("failed to prepare sandbox rootfs %q: %v", imageMeta.ChainID, err) + } + // TODO(random-liu): [P0] Cleanup snapshot on failure after switching to new rootfs api. + rootfsMounts := prepareResp.Mounts // Create sandbox container root directory. // Prepare streaming named pipe. @@ -124,7 +137,10 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run } // Start sandbox container. - spec := c.generateSandboxContainerSpec(id, config) + spec, err := c.generateSandboxContainerSpec(id, config, imageMeta.Config) + if err != nil { + return nil, fmt.Errorf("failed to generate sandbox container spec: %v", err) + } rawSpec, err := json.Marshal(spec) if err != nil { return nil, fmt.Errorf("failed to marshal oci spec %+v: %v", spec, err) @@ -137,16 +153,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run Value: rawSpec, }, // TODO(random-liu): [P0] Get rootfs mount from containerd. - Rootfs: []*mount.Mount{ - { - Type: "bind", - Source: rootPath, - Options: []string{ - "rw", - "rbind", - }, - }, - }, + Rootfs: rootfsMounts, Runtime: defaultRuntime, // No stdin for sandbox container. Stdout: stdout, @@ -205,20 +212,35 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run return &runtime.RunPodSandboxResponse{PodSandboxId: id}, nil } -func (c *criContainerdService) generateSandboxContainerSpec(id string, config *runtime.PodSandboxConfig) *runtimespec.Spec { - // TODO(random-liu): [P0] Get command from image config. - pauseCommand := []string{"sh", "-c", "while true; do sleep 1000000000; done"} - +func (c *criContainerdService) generateSandboxContainerSpec(id string, config *runtime.PodSandboxConfig, + imageConfig *imagespec.ImageConfig) (*runtimespec.Spec, error) { // Creates a spec Generator with the default spec. // TODO(random-liu): [P1] Compare the default settings with docker and containerd default. g := generate.New() + // Apply default config from image config. + for _, e := range imageConfig.Env { + kv := strings.Split(e, "=") + if len(kv) != 2 { + return nil, fmt.Errorf("invalid environment variable in image config %+v", imageConfig) + } + g.AddProcessEnv(kv[0], kv[1]) + } + + if imageConfig.WorkingDir != "" { + g.SetProcessCwd(imageConfig.WorkingDir) + } + + if len(imageConfig.Entrypoint) == 0 { + // Pause image must have entrypoint. + return nil, fmt.Errorf("invalid empty entrypoint in image config %+v", imageConfig) + } + // Set process commands. + g.SetProcessArgs(append(imageConfig.Entrypoint, imageConfig.Cmd...)) + // Set relative root path. g.SetRootPath(relativeRootfsPath) - // Set process commands. - g.SetProcessArgs(pauseCommand) - // Make root of sandbox container read-only. g.SetRootReadonly(true) @@ -276,5 +298,5 @@ func (c *criContainerdService) generateSandboxContainerSpec(id string, config *r // TODO(random-liu): [P1] Set default sandbox container resource limit. - return g.Spec() + return g.Spec(), nil } diff --git a/pkg/server/sandbox_run_test.go b/pkg/server/sandbox_run_test.go index 217db5aa6..39ff714e8 100644 --- a/pkg/server/sandbox_run_test.go +++ b/pkg/server/sandbox_run_test.go @@ -23,20 +23,22 @@ import ( "syscall" "testing" + "github.com/containerd/containerd/api/services/execution" + rootfsapi "github.com/containerd/containerd/api/services/rootfs" + imagedigest "github.com/opencontainers/go-digest" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" + runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" - "github.com/containerd/containerd/api/services/execution" - runtimespec "github.com/opencontainers/runtime-spec/specs-go" - + "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" - - runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" ) -func getRunPodSandboxTestData() (*runtime.PodSandboxConfig, func(*testing.T, string, *runtimespec.Spec)) { +func getRunPodSandboxTestData() (*runtime.PodSandboxConfig, *imagespec.ImageConfig, func(*testing.T, string, *runtimespec.Spec)) { config := &runtime.PodSandboxConfig{ Metadata: &runtime.PodSandboxMetadata{ Name: "test-name", @@ -52,20 +54,31 @@ func getRunPodSandboxTestData() (*runtime.PodSandboxConfig, func(*testing.T, str CgroupParent: "/test/cgroup/parent", }, } + imageConfig := &imagespec.ImageConfig{ + Env: []string{"a=b", "c=d"}, + Entrypoint: []string{"/pause"}, + Cmd: []string{"forever"}, + WorkingDir: "/workspace", + } specCheck := func(t *testing.T, id string, spec *runtimespec.Spec) { assert.Equal(t, "test-hostname", spec.Hostname) assert.Equal(t, getCgroupsPath("/test/cgroup/parent", id), spec.Linux.CgroupsPath) assert.Equal(t, relativeRootfsPath, spec.Root.Path) assert.Equal(t, true, spec.Root.Readonly) + assert.Contains(t, spec.Process.Env, "a=b", "c=d") + assert.Equal(t, []string{"/pause", "forever"}, spec.Process.Args) + assert.Equal(t, "/workspace", spec.Process.Cwd) } - return config, specCheck + return config, imageConfig, specCheck } func TestGenerateSandboxContainerSpec(t *testing.T) { testID := "test-id" for desc, test := range map[string]struct { - configChange func(*runtime.PodSandboxConfig) - specCheck func(*testing.T, *runtimespec.Spec) + configChange func(*runtime.PodSandboxConfig) + imageConfigChange func(*imagespec.ImageConfig) + specCheck func(*testing.T, *runtimespec.Spec) + expectErr bool }{ "spec should reflect original config": { specCheck: func(t *testing.T, spec *runtimespec.Spec) { @@ -106,14 +119,36 @@ func TestGenerateSandboxContainerSpec(t *testing.T) { }) }, }, + "should return error when entrypoint is empty": { + imageConfigChange: func(c *imagespec.ImageConfig) { + c.Entrypoint = nil + }, + expectErr: true, + }, + "should return error when env is invalid ": { + imageConfigChange: func(c *imagespec.ImageConfig) { + c.Env = []string{"a"} + }, + expectErr: true, + }, } { t.Logf("TestCase %q", desc) c := newTestCRIContainerdService() - config, specCheck := getRunPodSandboxTestData() + config, imageConfig, specCheck := getRunPodSandboxTestData() if test.configChange != nil { test.configChange(config) } - spec := c.generateSandboxContainerSpec(testID, config) + if test.imageConfigChange != nil { + test.imageConfigChange(imageConfig) + } + spec, err := c.generateSandboxContainerSpec(testID, config, imageConfig) + if test.expectErr { + assert.Error(t, err) + assert.Nil(t, spec) + continue + } + assert.NoError(t, err) + assert.NotNil(t, spec) specCheck(t, testID, spec) if test.specCheck != nil { test.specCheck(t, spec) @@ -122,8 +157,9 @@ func TestGenerateSandboxContainerSpec(t *testing.T) { } func TestRunPodSandbox(t *testing.T) { - config, specCheck := getRunPodSandboxTestData() + config, imageConfig, specCheck := getRunPodSandboxTestData() c := newTestCRIContainerdService() + fakeRootfsClient := c.rootfsService.(*servertesting.FakeRootfsClient) fakeExecutionClient := c.containerService.(*servertesting.FakeExecutionClient) fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) fakeOS := c.os.(*ostesting.FakeOS) @@ -140,6 +176,17 @@ func TestRunPodSandbox(t *testing.T) { assert.Equal(t, os.FileMode(0700), perm) return nopReadWriteCloser{}, nil } + testChainID := imagedigest.Digest("test-sandbox-chain-id") + imageMetadata := metadata.ImageMetadata{ + ID: testSandboxImage, + ChainID: testChainID.String(), + Config: imageConfig, + } + // Insert sandbox image metadata. + assert.NoError(t, c.imageMetadataStore.Create(imageMetadata)) + // Insert fake chainID + fakeRootfsClient.SetFakeChainIDs([]imagedigest.Digest{testChainID}) + expectRootfsClientCalls := []string{"prepare"} expectExecutionClientCalls := []string{"create", "start"} res, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config}) @@ -155,13 +202,24 @@ func TestRunPodSandbox(t *testing.T) { assert.Contains(t, pipes, stdout, "sandbox stdout pipe should be created") assert.Contains(t, pipes, stderr, "sandbox stderr pipe should be created") + assert.Equal(t, expectRootfsClientCalls, fakeRootfsClient.GetCalledNames(), "expect rootfs functions should be called") + calls := fakeRootfsClient.GetCalledDetails() + prepareOpts := calls[0].Argument.(*rootfsapi.PrepareRequest) + assert.Equal(t, &rootfsapi.PrepareRequest{ + Name: id, + ChainID: testChainID, + Readonly: true, + }, prepareOpts, "prepare request should be correct") + assert.Equal(t, expectExecutionClientCalls, fakeExecutionClient.GetCalledNames(), "expect containerd functions should be called") - calls := fakeExecutionClient.GetCalledDetails() + calls = fakeExecutionClient.GetCalledDetails() createOpts := calls[0].Argument.(*execution.CreateRequest) assert.Equal(t, id, createOpts.ID, "create id should be correct") - // TODO(random-liu): Test rootfs mount when image management part is integrated. assert.Equal(t, stdout, createOpts.Stdout, "stdout pipe should be passed to containerd") assert.Equal(t, stderr, createOpts.Stderr, "stderr pipe should be passed to containerd") + mountsResp, err := fakeRootfsClient.Mounts(context.Background(), &rootfsapi.MountsRequest{Name: id}) + assert.NoError(t, err) + assert.Equal(t, mountsResp.Mounts, createOpts.Rootfs, "rootfs mount should be correct") spec := &runtimespec.Spec{} assert.NoError(t, json.Unmarshal(createOpts.Spec.Value, spec)) t.Logf("oci spec check") diff --git a/pkg/server/sandbox_stop.go b/pkg/server/sandbox_stop.go index a2b6e23b0..934708ffe 100644 --- a/pkg/server/sandbox_stop.go +++ b/pkg/server/sandbox_stop.go @@ -34,7 +34,7 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St glog.V(2).Infof("StopPodSandbox for sandbox %q", r.GetPodSandboxId()) defer func() { if retErr == nil { - glog.V(2).Info("StopPodSandbox %q returns successfully", r.GetPodSandboxId()) + glog.V(2).Infof("StopPodSandbox %q returns successfully", r.GetPodSandboxId()) } }() @@ -56,7 +56,7 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St } else if !os.IsNotExist(err) { // It's ok for sandbox.NetNS to *not* exist return nil, fmt.Errorf("failed to stat netns path for sandbox %q before tearing down the network: %v", id, err) } - glog.V(2).Info("TearDown network for sandbox %q successfully", id) + glog.V(2).Infof("TearDown network for sandbox %q successfully", id) // TODO(random-liu): [P1] Handle sandbox container graceful deletion. // Delete the sandbox container from containerd. diff --git a/pkg/server/service.go b/pkg/server/service.go index 77ff5a144..73660d7e8 100644 --- a/pkg/server/service.go +++ b/pkg/server/service.go @@ -65,6 +65,9 @@ type criContainerdService struct { os osinterface.OS // rootDir is the directory for managing cri-containerd files. rootDir string + // sandboxImage is the image to use for sandbox container. + // TODO(random-liu): Make this configurable via flag. + sandboxImage string // sandboxStore stores all sandbox metadata. sandboxStore metadata.SandboxStore // imageMetadataStore stores all image metadata. @@ -100,6 +103,7 @@ func NewCRIContainerdService(conn *grpc.ClientConn, rootDir, networkPluginBinDir c := &criContainerdService{ os: osinterface.RealOS{}, rootDir: rootDir, + sandboxImage: defaultSandboxImage, sandboxStore: metadata.NewSandboxStore(store.NewMetadataStore()), containerStore: metadata.NewContainerStore(store.NewMetadataStore()), imageMetadataStore: metadata.NewImageMetadataStore(store.NewMetadataStore()), diff --git a/pkg/server/service_test.go b/pkg/server/service_test.go index 5b6bffcae..8b73f6fd4 100644 --- a/pkg/server/service_test.go +++ b/pkg/server/service_test.go @@ -32,6 +32,8 @@ import ( ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" "github.com/kubernetes-incubator/cri-containerd/pkg/registrar" servertesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/testing" + imagedigest "github.com/opencontainers/go-digest" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" ) @@ -43,20 +45,28 @@ func (nopReadWriteCloser) Read(p []byte) (n int, err error) { return 0, io.EOF func (nopReadWriteCloser) Write(p []byte) (n int, err error) { return 0, io.ErrShortWrite } func (nopReadWriteCloser) Close() error { return nil } -const testRootDir = "/test/rootfs" +const ( + testRootDir = "/test/rootfs" + // Use an image id as test sandbox image to avoid image name resolve. + // TODO(random-liu): Change this to image name after we have complete image + // management unit test framework. + testSandboxImage = "sha256:c75bebcdd211f41b3a460c7bf82970ed6c75acaab9cd4c9a4e125b03ca113798" +) // newTestCRIContainerdService creates a fake criContainerdService for test. func newTestCRIContainerdService() *criContainerdService { return &criContainerdService{ os: ostesting.NewFakeOS(), rootDir: testRootDir, - containerService: servertesting.NewFakeExecutionClient(), + sandboxImage: testSandboxImage, sandboxStore: metadata.NewSandboxStore(store.NewMetadataStore()), imageMetadataStore: metadata.NewImageMetadataStore(store.NewMetadataStore()), sandboxNameIndex: registrar.NewRegistrar(), sandboxIDIndex: truncindex.NewTruncIndex(nil), containerStore: metadata.NewContainerStore(store.NewMetadataStore()), containerNameIndex: registrar.NewRegistrar(), + containerService: servertesting.NewFakeExecutionClient(), + rootfsService: servertesting.NewFakeRootfsClient(), netPlugin: servertesting.NewFakeCNIPlugin(), } } @@ -65,11 +75,21 @@ func newTestCRIContainerdService() *criContainerdService { func TestSandboxOperations(t *testing.T) { c := newTestCRIContainerdService() fake := c.containerService.(*servertesting.FakeExecutionClient) + fakeRootfsClient := c.rootfsService.(*servertesting.FakeRootfsClient) fakeOS := c.os.(*ostesting.FakeOS) fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) fakeOS.OpenFifoFn = func(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { return nopReadWriteCloser{}, nil } + // Insert sandbox image metadata. + assert.NoError(t, c.imageMetadataStore.Create(metadata.ImageMetadata{ + ID: testSandboxImage, + ChainID: "test-chain-id", + Config: &imagespec.ImageConfig{Entrypoint: []string{"/pause"}}, + })) + // Insert fake chainID + fakeRootfsClient.SetFakeChainIDs([]imagedigest.Digest{imagedigest.Digest("test-chain-id")}) + config := &runtime.PodSandboxConfig{ Metadata: &runtime.PodSandboxMetadata{ Name: "test-name", diff --git a/pkg/server/testing/fake_rootfs_client.go b/pkg/server/testing/fake_rootfs_client.go index f9bea8f57..da10522a7 100644 --- a/pkg/server/testing/fake_rootfs_client.go +++ b/pkg/server/testing/fake_rootfs_client.go @@ -169,7 +169,11 @@ func (f *FakeRootfsClient) Prepare(ctx context.Context, prepareOpts *rootfs.Prep if ok { return nil, fmt.Errorf("mounts already exist") } - f.MountList[prepareOpts.Name] = []*mount.Mount{} + f.MountList[prepareOpts.Name] = []*mount.Mount{{ + Type: "bind", + Source: prepareOpts.Name, + // TODO(random-liu): Fake options based on Readonly option. + }} return &rootfs.MountResponse{ Mounts: f.MountList[prepareOpts.Name], }, nil