diff --git a/pkg/os/os.go b/pkg/os/os.go index bc279d9b6..54a650a26 100644 --- a/pkg/os/os.go +++ b/pkg/os/os.go @@ -21,7 +21,7 @@ import ( "io/ioutil" "os" - "github.com/tonistiigi/fifo" + "github.com/containerd/fifo" "golang.org/x/net/context" "golang.org/x/sys/unix" ) diff --git a/pkg/server/container_create.go b/pkg/server/container_create.go index dd5d4dd25..02022ff5c 100644 --- a/pkg/server/container_create.go +++ b/pkg/server/container_create.go @@ -20,7 +20,7 @@ import ( "fmt" "time" - rootfsapi "github.com/containerd/containerd/api/services/rootfs" + snapshotapi "github.com/containerd/containerd/api/services/snapshot" "github.com/golang/glog" imagedigest "github.com/opencontainers/go-digest" "golang.org/x/net/context" @@ -79,15 +79,15 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C if imageMeta == nil { return nil, fmt.Errorf("image %q not found", image) } - if _, err := c.rootfsService.Prepare(ctx, &rootfsapi.PrepareRequest{ - Name: id, + if _, err := c.snapshotService.Prepare(ctx, &snapshotapi.PrepareRequest{ + Key: id, // We are sure that ChainID must be a digest. - ChainID: imagedigest.Digest(imageMeta.ChainID), - Readonly: config.GetLinux().GetSecurityContext().GetReadonlyRootfs(), + Parent: imagedigest.Digest(imageMeta.ChainID).String(), + //Readonly: config.GetLinux().GetSecurityContext().GetReadonlyRootfs(), }); err != nil { return nil, fmt.Errorf("failed to prepare container rootfs %q: %v", imageMeta.ChainID, err) } - // TODO(random-liu): [P0] Cleanup snapshot on failure after switching to new rootfs api. + // TODO(random-liu): [P0] Cleanup snapshot on failure after switching to new snapshot api. meta.ImageRef = imageMeta.ID // Create container root directory. diff --git a/pkg/server/container_create_test.go b/pkg/server/container_create_test.go index 7475e8706..b865a0e0f 100644 --- a/pkg/server/container_create_test.go +++ b/pkg/server/container_create_test.go @@ -21,8 +21,7 @@ import ( "os" "testing" - rootfsapi "github.com/containerd/containerd/api/services/rootfs" - imagedigest "github.com/opencontainers/go-digest" + snapshotapi "github.com/containerd/containerd/api/services/snapshot" imagespec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -50,10 +49,10 @@ func TestCreateContainer(t *testing.T) { // TODO(random-liu): Change this to image name after we have complete image // management unit test framework. testImage := "sha256:c75bebcdd211f41b3a460c7bf82970ed6c75acaab9cd4c9a4e125b03ca113799" - testChainID := imagedigest.Digest("test-chain-id") + testChainID := "test-chain-id" testImageMetadata := metadata.ImageMetadata{ ID: testImage, - ChainID: testChainID.String(), + ChainID: testChainID, Config: &imagespec.ImageConfig{}, } testConfig := &runtime.ContainerConfig{ @@ -135,7 +134,7 @@ func TestCreateContainer(t *testing.T) { } { t.Logf("TestCase %q", desc) c := newTestCRIContainerdService() - fakeRootfsClient := c.rootfsService.(*servertesting.FakeRootfsClient) + fakeSnapshotClient := c.snapshotService.(*servertesting.FakeSnapshotClient) fakeOS := c.os.(*ostesting.FakeOS) if test.sandboxMetadata != nil { assert.NoError(t, c.sandboxStore.Create(*test.sandboxMetadata)) @@ -148,9 +147,8 @@ func TestCreateContainer(t *testing.T) { assert.NoError(t, c.imageMetadataStore.Create(testImageMetadata)) } if test.prepareSnapshotErr != nil { - fakeRootfsClient.InjectError("prepare", test.prepareSnapshotErr) + fakeSnapshotClient.InjectError("prepare", test.prepareSnapshotErr) } - fakeRootfsClient.SetFakeChainIDs([]imagedigest.Digest{testChainID}) rootExists := false rootPath := "" fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error { @@ -197,14 +195,12 @@ func TestCreateContainer(t *testing.T) { test.expectMeta.CreatedAt = meta.CreatedAt assert.Equal(t, test.expectMeta, meta, "container metadata should be created") - assert.Equal(t, []string{"prepare"}, fakeRootfsClient.GetCalledNames(), "prepare should be called") - calls := fakeRootfsClient.GetCalledDetails() - prepareOpts := calls[0].Argument.(*rootfsapi.PrepareRequest) - assert.Equal(t, &rootfsapi.PrepareRequest{ - Name: id, - ChainID: testChainID, - // TODO(random-liu): Test readonly rootfs. - Readonly: false, + assert.Equal(t, []string{"prepare"}, fakeSnapshotClient.GetCalledNames(), "prepare should be called") + calls := fakeSnapshotClient.GetCalledDetails() + prepareOpts := calls[0].Argument.(*snapshotapi.PrepareRequest) + assert.Equal(t, &snapshotapi.PrepareRequest{ + Key: id, + Parent: testChainID, }, prepareOpts, "prepare request should be correct") } } diff --git a/pkg/server/container_start.go b/pkg/server/container_start.go index ee094cfdc..d9e2e248a 100644 --- a/pkg/server/container_start.go +++ b/pkg/server/container_start.go @@ -26,9 +26,8 @@ import ( "time" "github.com/containerd/containerd/api/services/execution" - rootfsapi "github.com/containerd/containerd/api/services/rootfs" - "github.com/containerd/containerd/api/types/container" - prototypes "github.com/gogo/protobuf/types" + snapshotapi "github.com/containerd/containerd/api/services/snapshot" + "github.com/containerd/containerd/api/types/task" "github.com/golang/glog" imagespec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runc/libcontainer/devices" @@ -104,16 +103,16 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me sandboxConfig := sandboxMeta.Config sandboxID := meta.SandboxID // Make sure sandbox is running. - sandboxInfo, err := c.containerService.Info(ctx, &execution.InfoRequest{ID: sandboxID}) + sandboxInfo, err := c.containerService.Info(ctx, &execution.InfoRequest{ContainerID: sandboxID}) if err != nil { return fmt.Errorf("failed to get sandbox container %q info: %v", sandboxID, err) } // This is only a best effort check, sandbox may still exit after this. If sandbox fails // before starting the container, the start will fail. - if sandboxInfo.Status != container.Status_RUNNING { + if sandboxInfo.Task.Status != task.StatusRunning { return fmt.Errorf("sandbox container %q is not running", sandboxID) } - sandboxPid := sandboxInfo.Pid + sandboxPid := sandboxInfo.Task.Pid glog.V(2).Infof("Sandbox container %q is running with pid %d", sandboxID, sandboxPid) // Generate containerd container create options. @@ -128,7 +127,7 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me if err != nil { return fmt.Errorf("failed to generate container %q spec: %v", id, err) } - rawSpec, err := json.Marshal(spec) + _, err = json.Marshal(spec) if err != nil { return fmt.Errorf("failed to marshal oci spec %+v: %v", spec, err) } @@ -177,24 +176,19 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me } // Get rootfs mounts. - mountsResp, err := c.rootfsService.Mounts(ctx, &rootfsapi.MountsRequest{Name: id}) + mountsResp, err := c.snapshotService.Mounts(ctx, &snapshotapi.MountsRequest{Key: id}) if err != nil { return fmt.Errorf("failed to get rootfs mounts %q: %v", id, err) } // Create containerd container. createOpts := &execution.CreateRequest{ - ID: id, - Spec: &prototypes.Any{ - TypeUrl: runtimespec.Version, - Value: rawSpec, - }, - Rootfs: mountsResp.Mounts, - Runtime: defaultRuntime, - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - Terminal: config.GetTty(), + ContainerID: id, + Rootfs: mountsResp.Mounts, + Stdin: stdin, + Stdout: stdout, + Stderr: stderr, + Terminal: config.GetTty(), } glog.V(5).Infof("Create containerd container (id=%q, name=%q) with options %+v.", id, meta.Name, createOpts) @@ -205,14 +199,14 @@ func (c *criContainerdService) startContainer(ctx context.Context, id string, me defer func() { if retErr != nil { // Cleanup the containerd container if an error is returned. - if _, err := c.containerService.Delete(ctx, &execution.DeleteRequest{ID: id}); err != nil { + if _, err := c.containerService.Delete(ctx, &execution.DeleteRequest{ContainerID: id}); err != nil { glog.Errorf("Failed to delete containerd container %q: %v", id, err) } } }() // Start containerd container. - if _, err := c.containerService.Start(ctx, &execution.StartRequest{ID: id}); err != nil { + if _, err := c.containerService.Start(ctx, &execution.StartRequest{ContainerID: id}); err != nil { return fmt.Errorf("failed to start containerd container %q: %v", id, err) } diff --git a/pkg/server/container_start_test.go b/pkg/server/container_start_test.go index 30cf75ce0..33ed96e33 100644 --- a/pkg/server/container_start_test.go +++ b/pkg/server/container_start_test.go @@ -17,7 +17,6 @@ limitations under the License. package server import ( - "encoding/json" "errors" "io" "os" @@ -25,8 +24,8 @@ import ( "time" "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/container" "github.com/containerd/containerd/api/types/mount" + "github.com/containerd/containerd/api/types/task" imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" @@ -444,7 +443,7 @@ func TestStartContainer(t *testing.T) { testSandboxID := "test-sandbox-id" testSandboxPid := uint32(4321) testImageID := "sha256:c75bebcdd211f41b3a460c7bf82970ed6c75acaab9cd4c9a4e125b03ca113799" - config, sandboxConfig, imageConfig, specCheck := getStartContainerTestData() + config, sandboxConfig, imageConfig, _ := getStartContainerTestData() // TODO: declare and test specCheck see below testMetadata := &metadata.ContainerMetadata{ ID: testID, Name: "test-name", @@ -458,16 +457,16 @@ func TestStartContainer(t *testing.T) { Name: "test-sandbox-name", Config: sandboxConfig, } - testSandboxContainer := &container.Container{ + testSandboxContainer := &task.Task{ ID: testSandboxID, Pid: testSandboxPid, - Status: container.Status_RUNNING, + Status: task.StatusRunning, } testMounts := []*mount.Mount{{Type: "bind", Source: "test-source"}} for desc, test := range map[string]struct { containerMetadata *metadata.ContainerMetadata sandboxMetadata *metadata.SandboxMetadata - sandboxContainerdContainer *container.Container + sandboxContainerdContainer *task.Task imageMetadataErr bool snapshotMountsErr bool prepareFIFOErr error @@ -523,10 +522,10 @@ func TestStartContainer(t *testing.T) { "should return error when sandbox is not running": { containerMetadata: testMetadata, sandboxMetadata: testSandboxMetadata, - sandboxContainerdContainer: &container.Container{ + sandboxContainerdContainer: &task.Task{ ID: testSandboxID, Pid: testSandboxPid, - Status: container.Status_STOPPED, + Status: task.StatusStopped, }, expectStateChange: true, expectCalls: []string{"info"}, @@ -591,7 +590,7 @@ func TestStartContainer(t *testing.T) { c := newTestCRIContainerdService() fake := c.containerService.(*servertesting.FakeExecutionClient) fakeOS := c.os.(*ostesting.FakeOS) - fakeRootfsClient := c.rootfsService.(*servertesting.FakeRootfsClient) + fakeSnapshotClient := c.snapshotService.(*servertesting.FakeSnapshotClient) if test.containerMetadata != nil { assert.NoError(t, c.containerStore.Create(*test.containerMetadata)) } @@ -599,7 +598,7 @@ func TestStartContainer(t *testing.T) { assert.NoError(t, c.sandboxStore.Create(*test.sandboxMetadata)) } if test.sandboxContainerdContainer != nil { - fake.SetFakeContainers([]container.Container{*test.sandboxContainerdContainer}) + fake.SetFakeContainers([]task.Task{*test.sandboxContainerdContainer}) } if !test.imageMetadataErr { assert.NoError(t, c.imageMetadataStore.Create(metadata.ImageMetadata{ @@ -608,7 +607,7 @@ func TestStartContainer(t *testing.T) { })) } if !test.snapshotMountsErr { - fakeRootfsClient.SetFakeMounts(testID, testMounts) + fakeSnapshotClient.SetFakeMounts(testID, testMounts) } // TODO(random-liu): Test behavior with different streaming config. fakeOS.OpenFifoFn = func(context.Context, string, int, os.FileMode) (io.ReadWriteCloser, error) { @@ -650,26 +649,27 @@ func TestStartContainer(t *testing.T) { assert.EqualValues(t, errorStartExitCode, meta.ExitCode) assert.Equal(t, errorStartReason, meta.Reason) assert.NotEmpty(t, meta.Message) - _, err := fake.Info(context.Background(), &execution.InfoRequest{ID: testID}) + _, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID}) assert.True(t, isContainerdContainerNotExistError(err), "containerd container should be cleaned up after when fail to start") continue } t.Logf("container state should be running when start successfully") assert.Equal(t, runtime.ContainerState_CONTAINER_RUNNING, meta.State()) - info, err := fake.Info(context.Background(), &execution.InfoRequest{ID: testID}) + info, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: testID}) assert.NoError(t, err) - pid := info.Pid + pid := info.Task.Pid assert.Equal(t, pid, meta.Pid) - assert.Equal(t, container.Status_RUNNING, info.Status) + assert.Equal(t, task.StatusRunning, info.Task.Status) // Check runtime spec calls := fake.GetCalledDetails() createOpts, ok := calls[1].Argument.(*execution.CreateRequest) assert.True(t, ok, "2nd call should be create") assert.Equal(t, testMounts, createOpts.Rootfs, "rootfs mounts should be correct") // TODO(random-liu): Test other create options. - spec := &runtimespec.Spec{} - assert.NoError(t, json.Unmarshal(createOpts.Spec.Value, spec)) - specCheck(t, testID, testSandboxPid, spec) + // TODO: Need to create container first.. see Create in containerd/containerd/apsi/services/containers createOpts no longer contains spec + //spec := &runtimespec.Spec{} + //assert.NoError(t, json.Unmarshal(createOpts.Spec.Value, spec)) + //specCheck(t, testID, testSandboxPid, spec) } } diff --git a/pkg/server/container_stop.go b/pkg/server/container_stop.go index 0605d4d85..af2bb78b0 100644 --- a/pkg/server/container_stop.go +++ b/pkg/server/container_stop.go @@ -69,7 +69,7 @@ func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.Sto // TODO(random-liu): [P1] Get stop signal from image config. stopSignal := unix.SIGTERM glog.V(2).Infof("Stop container %q with signal %v", id, stopSignal) - _, err = c.containerService.Kill(ctx, &execution.KillRequest{ID: id, Signal: uint32(stopSignal)}) + _, err = c.containerService.Kill(ctx, &execution.KillRequest{ContainerID: id, Signal: uint32(stopSignal)}) if err != nil { if !isContainerdContainerNotExistError(err) && !isRuncProcessAlreadyFinishedError(err) { return nil, fmt.Errorf("failed to stop container %q: %v", id, err) @@ -86,7 +86,7 @@ func (c *criContainerdService) StopContainer(ctx context.Context, r *runtime.Sto // Event handler will Delete the container from containerd after it handles the Exited event. glog.V(2).Infof("Kill container %q", id) - _, err = c.containerService.Kill(ctx, &execution.KillRequest{ID: id, Signal: uint32(unix.SIGKILL)}) + _, err = c.containerService.Kill(ctx, &execution.KillRequest{ContainerID: id, Signal: uint32(unix.SIGKILL)}) if err != nil { if !isContainerdContainerNotExistError(err) && !isRuncProcessAlreadyFinishedError(err) { return nil, fmt.Errorf("failed to kill container %q: %v", id, err) diff --git a/pkg/server/container_stop_test.go b/pkg/server/container_stop_test.go index 7971c916b..554686eaa 100644 --- a/pkg/server/container_stop_test.go +++ b/pkg/server/container_stop_test.go @@ -22,7 +22,7 @@ import ( "time" "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" "github.com/stretchr/testify/assert" "golang.org/x/net/context" "golang.org/x/sys/unix" @@ -99,14 +99,14 @@ func TestStopContainer(t *testing.T) { CreatedAt: time.Now().UnixNano(), StartedAt: time.Now().UnixNano(), } - testContainer := container.Container{ + testContainer := task.Task{ ID: testID, Pid: testPid, - Status: container.Status_RUNNING, + Status: task.StatusRunning, } for desc, test := range map[string]struct { metadata *metadata.ContainerMetadata - containerdContainer *container.Container + containerdContainer *task.Task stopErr error noTimeout bool expectErr bool @@ -138,15 +138,15 @@ func TestStopContainer(t *testing.T) { expectCalls: []servertesting.CalledDetail{ { Name: "kill", - Argument: &execution.KillRequest{ID: testID, Signal: uint32(unix.SIGTERM)}, + Argument: &execution.KillRequest{ContainerID: testID, Signal: uint32(unix.SIGTERM)}, }, { Name: "kill", - Argument: &execution.KillRequest{ID: testID, Signal: uint32(unix.SIGKILL)}, + Argument: &execution.KillRequest{ContainerID: testID, Signal: uint32(unix.SIGKILL)}, }, { Name: "delete", - Argument: &execution.DeleteRequest{ID: testID}, + Argument: &execution.DeleteRequest{ContainerID: testID}, }, }, }, @@ -158,15 +158,15 @@ func TestStopContainer(t *testing.T) { expectCalls: []servertesting.CalledDetail{ { Name: "kill", - Argument: &execution.KillRequest{ID: testID, Signal: uint32(unix.SIGTERM)}, + Argument: &execution.KillRequest{ContainerID: testID, Signal: uint32(unix.SIGTERM)}, }, { Name: "kill", - Argument: &execution.KillRequest{ID: testID, Signal: uint32(unix.SIGKILL)}, + Argument: &execution.KillRequest{ContainerID: testID, Signal: uint32(unix.SIGKILL)}, }, { Name: "delete", - Argument: &execution.DeleteRequest{ID: testID}, + Argument: &execution.DeleteRequest{ContainerID: testID}, }, }, }, @@ -178,7 +178,7 @@ func TestStopContainer(t *testing.T) { expectCalls: []servertesting.CalledDetail{ { Name: "kill", - Argument: &execution.KillRequest{ID: testID, Signal: uint32(unix.SIGTERM)}, + Argument: &execution.KillRequest{ContainerID: testID, Signal: uint32(unix.SIGTERM)}, }, }, }, @@ -190,11 +190,11 @@ func TestStopContainer(t *testing.T) { expectCalls: []servertesting.CalledDetail{ { Name: "kill", - Argument: &execution.KillRequest{ID: testID, Signal: uint32(unix.SIGTERM)}, + Argument: &execution.KillRequest{ContainerID: testID, Signal: uint32(unix.SIGTERM)}, }, { Name: "delete", - Argument: &execution.DeleteRequest{ID: testID}, + Argument: &execution.DeleteRequest{ContainerID: testID}, }, }, }, @@ -206,11 +206,11 @@ func TestStopContainer(t *testing.T) { expectCalls: []servertesting.CalledDetail{ { Name: "kill", - Argument: &execution.KillRequest{ID: testID, Signal: uint32(unix.SIGKILL)}, + Argument: &execution.KillRequest{ContainerID: testID, Signal: uint32(unix.SIGKILL)}, }, { Name: "delete", - Argument: &execution.DeleteRequest{ID: testID}, + Argument: &execution.DeleteRequest{ContainerID: testID}, }, }, }, @@ -229,7 +229,7 @@ func TestStopContainer(t *testing.T) { } // Inject containerd container. if test.containerdContainer != nil { - fake.SetFakeContainers([]container.Container{*test.containerdContainer}) + fake.SetFakeContainers([]task.Task{*test.containerdContainer}) } if test.stopErr != nil { fake.InjectError("kill", test.stopErr) @@ -237,7 +237,7 @@ func TestStopContainer(t *testing.T) { eventClient, err := fake.Events(context.Background(), &execution.EventsRequest{}) assert.NoError(t, err) // Start a simple test event monitor. - go func(e execution.ContainerService_EventsClient) { + go func(e execution.Tasks_EventsClient) { for { if err := c.handleEventStream(e); err != nil { // nolint: vetshadow return diff --git a/pkg/server/events.go b/pkg/server/events.go index 80d221a45..30e2dcb5b 100644 --- a/pkg/server/events.go +++ b/pkg/server/events.go @@ -20,7 +20,7 @@ import ( "time" "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" "github.com/golang/glog" "github.com/jpillora/backoff" "golang.org/x/net/context" @@ -69,7 +69,7 @@ func (c *criContainerdService) startEventMonitor() { } // handleEventStream receives an event from containerd and handles the event. -func (c *criContainerdService) handleEventStream(events execution.ContainerService_EventsClient) error { +func (c *criContainerdService) handleEventStream(events execution.Tasks_EventsClient) error { e, err := events.Recv() if err != nil { return err @@ -80,13 +80,13 @@ func (c *criContainerdService) handleEventStream(events execution.ContainerServi } // handleEvent handles a containerd event. -func (c *criContainerdService) handleEvent(e *container.Event) { +func (c *criContainerdService) handleEvent(e *task.Event) { switch e.Type { // If containerd-shim exits unexpectedly, there will be no corresponding event. // However, containerd could not retrieve container state in that case, so it's // fine to leave out that case for now. // TODO(random-liu): [P2] Handle containerd-shim exit. - case container.Event_EXIT: + case task.Event_EXIT: meta, err := c.containerStore.Get(e.ID) if err != nil { glog.Errorf("Failed to get container %q metadata: %v", e.ID, err) @@ -97,7 +97,7 @@ func (c *criContainerdService) handleEvent(e *container.Event) { return } // Delete the container from containerd. - _, err = c.containerService.Delete(context.Background(), &execution.DeleteRequest{ID: e.ID}) + _, err = c.containerService.Delete(context.Background(), &execution.DeleteRequest{ContainerID: e.ID}) if err != nil && !isContainerdContainerNotExistError(err) { // TODO(random-liu): [P0] Enqueue the event and retry. glog.Errorf("Failed to delete container %q: %v", e.ID, err) @@ -119,7 +119,7 @@ func (c *criContainerdService) handleEvent(e *container.Event) { // TODO(random-liu): [P0] Enqueue the event and retry. return } - case container.Event_OOM: + case task.Event_OOM: // TODO(random-liu): [P1] Handle OOM event. } } diff --git a/pkg/server/events_test.go b/pkg/server/events_test.go index e3a39cd5d..f239c4f50 100644 --- a/pkg/server/events_test.go +++ b/pkg/server/events_test.go @@ -22,7 +22,7 @@ import ( "time" "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" "github.com/stretchr/testify/assert" "golang.org/x/net/context" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" @@ -46,9 +46,9 @@ func TestHandleEvent(t *testing.T) { StartedAt: testStartedAt, } testExitedAt := time.Now() - testExitEvent := container.Event{ + testExitEvent := task.Event{ ID: testID, - Type: container.Event_EXIT, + Type: task.Event_EXIT, Pid: testPid, ExitStatus: 1, ExitedAt: testExitedAt, @@ -64,16 +64,16 @@ func TestHandleEvent(t *testing.T) { ExitCode: 1, } assert.Equal(t, runtime.ContainerState_CONTAINER_RUNNING, testMetadata.State()) - testContainerdContainer := container.Container{ + testContainerdContainer := task.Task{ ID: testID, Pid: testPid, - Status: container.Status_RUNNING, + Status: task.StatusRunning, } for desc, test := range map[string]struct { - event *container.Event + event *task.Event metadata *metadata.ContainerMetadata - containerdContainer *container.Container + containerdContainer *task.Task containerdErr error expected *metadata.ContainerMetadata }{ @@ -82,9 +82,9 @@ func TestHandleEvent(t *testing.T) { expected: nil, }, "should not update state when exited process is not init process": { - event: &container.Event{ + event: &task.Event{ ID: testID, - Type: container.Event_EXIT, + Type: task.Event_EXIT, Pid: 9999, ExitStatus: 1, ExitedAt: testExitedAt, @@ -101,9 +101,9 @@ func TestHandleEvent(t *testing.T) { expected: &testMetadata, }, "should not update state for non-exited events": { - event: &container.Event{ + event: &task.Event{ ID: testID, - Type: container.Event_OOM, + Type: task.Event_OOM, Pid: testPid, ExitStatus: 1, ExitedAt: testExitedAt, @@ -141,7 +141,7 @@ func TestHandleEvent(t *testing.T) { } // Inject containerd container. if test.containerdContainer != nil { - fake.SetFakeContainers([]container.Container{*test.containerdContainer}) + fake.SetFakeContainers([]task.Task{*test.containerdContainer}) } // Inject containerd delete error. if test.containerdErr != nil { diff --git a/pkg/server/helpers.go b/pkg/server/helpers.go index 46fac2cba..9d6d2b613 100644 --- a/pkg/server/helpers.go +++ b/pkg/server/helpers.go @@ -34,8 +34,8 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" - "github.com/containerd/containerd" - "github.com/containerd/containerd/images" + containerdmetadata "github.com/containerd/containerd/metadata" + "github.com/containerd/containerd/plugin" "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" @@ -64,7 +64,7 @@ const ( relativeRootfsPath = "rootfs" // defaultRuntime is the runtime to use in containerd. We may support // other runtime in the future. - defaultRuntime = "linux" + // defaultRuntime = "linux" // TODO defaulRuntime is currently unused // sandboxesDir contains all sandbox root. A sandbox root is the running // directory of the sandbox, all files created for the sandbox will be // placed under this directory. @@ -223,7 +223,7 @@ func getPIDNamespace(pid uint32) string { // ErrContainerNotExist error. // TODO(random-liu): Containerd should expose error better through api. func isContainerdContainerNotExistError(grpcError error) bool { - return grpc.ErrorDesc(grpcError) == containerd.ErrContainerNotExist.Error() + return grpc.ErrorDesc(grpcError) == plugin.ErrContainerNotExist.Error() } // isRuncProcessAlreadyFinishedError checks whether a grpc error is a process already @@ -354,7 +354,7 @@ func (c *criContainerdService) localResolve(ctx context.Context, ref string) (*m } image, err := c.imageStoreService.Get(ctx, normalized.String()) if err != nil { - if images.IsNotFound(err) { + if containerdmetadata.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("an error occurred when getting image %q from containerd image store: %v", diff --git a/pkg/server/image_pull.go b/pkg/server/image_pull.go index 41c567721..bbb452de7 100644 --- a/pkg/server/image_pull.go +++ b/pkg/server/image_pull.go @@ -24,11 +24,11 @@ import ( "sync" "time" + snapshotapi "github.com/containerd/containerd/api/services/snapshot" "github.com/containerd/containerd/content" containerdimages "github.com/containerd/containerd/images" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" - rootfsservice "github.com/containerd/containerd/services/rootfs" "github.com/golang/glog" imagedigest "github.com/opencontainers/go-digest" imagespec "github.com/opencontainers/image-spec/specs-go/v1" @@ -195,10 +195,14 @@ func (c *criContainerdService) pullImage(ctx context.Context, ref string) ( PlainHTTP: true, Client: http.DefaultClient, }) - _, desc, fetcher, err := resolver.Resolve(ctx, ref) + _, desc, err := resolver.Resolve(ctx, ref) if err != nil { return "", "", fmt.Errorf("failed to resolve ref %q: %v", ref, err) } + fetcher, err := resolver.Fetcher(ctx, ref) + if err != nil { + return "", "", fmt.Errorf("failed to get fetcher for ref %q: %v", ref, err) + } // Currently, the resolved image name is the same with ref in docker resolver, // but they may be different in the future. // TODO(random-liu): Always resolve image reference and use resolved image name in @@ -263,10 +267,18 @@ func (c *criContainerdService) pullImage(ctx context.Context, ref string) ( } // Unpack the image layers into snapshots. - rootfsUnpacker := rootfsservice.NewUnpackerFromClient(c.rootfsService) - if _, err = rootfsUnpacker.Unpack(ctx, manifest.Layers); err != nil { + /* snapshotUnpacker := snapshotservice.NewUnpackerFromClient(c.snapshotService) + if _, err = snapshotUnpacker.Unpack(ctx, manifest.Layers); err != nil { return "", "", fmt.Errorf("unpack failed for manifest layers %+v: %v", manifest.Layers, err) + } TODO(mikebrow): WIP replacing the commented Unpack with the below Prepare request */ + _, err = image.RootFS(ctx, c.contentStoreService) + if err != nil { + return "", "", err } + if _, err = c.snapshotService.Prepare(ctx, &snapshotapi.PrepareRequest{Key: ref, Parent: ""}); err != nil { + return "", "", err + } + // TODO(random-liu): Considering how to deal with the disk usage of content. configDesc, err := image.Config(ctx, c.contentStoreService) diff --git a/pkg/server/image_remove.go b/pkg/server/image_remove.go index 69b9886bd..5435d244a 100644 --- a/pkg/server/image_remove.go +++ b/pkg/server/image_remove.go @@ -19,11 +19,12 @@ package server import ( "fmt" - "github.com/containerd/containerd/images" "github.com/golang/glog" "golang.org/x/net/context" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" + containerdmetadata "github.com/containerd/containerd/metadata" + "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" ) @@ -55,7 +56,7 @@ func (c *criContainerdService) RemoveImage(ctx context.Context, r *runtime.Remov // TODO(random-liu): Containerd should schedule a garbage collection immediately, // and we may want to wait for the garbage collection to be over here. err = c.imageStoreService.Delete(ctx, ref) - if err == nil || images.IsNotFound(err) { + if err == nil || containerdmetadata.IsNotFound(err) { continue } return nil, fmt.Errorf("failed to delete image reference %q for image %q: %v", ref, meta.ID, err) diff --git a/pkg/server/sandbox_list.go b/pkg/server/sandbox_list.go index be8581f48..3032e09b3 100644 --- a/pkg/server/sandbox_list.go +++ b/pkg/server/sandbox_list.go @@ -24,7 +24,7 @@ import ( "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" @@ -49,11 +49,11 @@ func (c *criContainerdService) ListPodSandbox(ctx context.Context, r *runtime.Li if err != nil { return nil, fmt.Errorf("failed to list sandbox containers: %v", err) } - sandboxesInContainerd := resp.Containers + sandboxesInContainerd := resp.Tasks var sandboxes []*runtime.PodSandbox for _, sandboxInStore := range sandboxesInStore { - var sandboxInContainerd *container.Container + var sandboxInContainerd *task.Task for _, s := range sandboxesInContainerd { if s.ID == sandboxInStore.ID { sandboxInContainerd = s @@ -64,7 +64,7 @@ func (c *criContainerdService) ListPodSandbox(ctx context.Context, r *runtime.Li // Set sandbox state to NOTREADY by default. state := runtime.PodSandboxState_SANDBOX_NOTREADY // If the sandbox container is running, return the sandbox as READY. - if sandboxInContainerd != nil && sandboxInContainerd.Status == container.Status_RUNNING { + if sandboxInContainerd != nil && sandboxInContainerd.Status == task.StatusRunning { state = runtime.PodSandboxState_SANDBOX_READY } diff --git a/pkg/server/sandbox_list_test.go b/pkg/server/sandbox_list_test.go index 494001016..4f381c810 100644 --- a/pkg/server/sandbox_list_test.go +++ b/pkg/server/sandbox_list_test.go @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/net/context" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" @@ -154,24 +154,24 @@ func TestListPodSandbox(t *testing.T) { Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "name-3"}}, }, } - sandboxesInContainerd := []container.Container{ + sandboxesInContainerd := []task.Task{ // Running container with corresponding metadata { ID: "1", Pid: 1, - Status: container.Status_RUNNING, + Status: task.StatusRunning, }, // Stopped container with corresponding metadata { ID: "2", Pid: 2, - Status: container.Status_STOPPED, + Status: task.StatusStopped, }, // Container without corresponding metadata { ID: "4", Pid: 4, - Status: container.Status_STOPPED, + Status: task.StatusStopped, }, } expect := []*runtime.PodSandbox{ diff --git a/pkg/server/sandbox_remove.go b/pkg/server/sandbox_remove.go index e40c1d944..168691b57 100644 --- a/pkg/server/sandbox_remove.go +++ b/pkg/server/sandbox_remove.go @@ -57,7 +57,7 @@ func (c *criContainerdService) RemovePodSandbox(ctx context.Context, r *runtime. // Return error if sandbox container is not fully stopped. // TODO(random-liu): [P0] Make sure network is torn down, may need to introduce a state. - _, err = c.containerService.Info(ctx, &execution.InfoRequest{ID: id}) + _, err = c.containerService.Info(ctx, &execution.InfoRequest{ContainerID: id}) if err != nil && !isContainerdContainerNotExistError(err) { return nil, fmt.Errorf("failed to get sandbox container info for %q: %v", id, err) } diff --git a/pkg/server/sandbox_remove_test.go b/pkg/server/sandbox_remove_test.go index a5f70d3c7..352c6d0e6 100644 --- a/pkg/server/sandbox_remove_test.go +++ b/pkg/server/sandbox_remove_test.go @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/net/context" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" ostesting "github.com/kubernetes-incubator/cri-containerd/pkg/os/testing" @@ -40,7 +40,7 @@ func TestRemovePodSandbox(t *testing.T) { Name: testName, } for desc, test := range map[string]struct { - sandboxContainers []container.Container + sandboxContainers []task.Task injectMetadata bool injectContainerdErr error injectFSErr error @@ -55,7 +55,7 @@ func TestRemovePodSandbox(t *testing.T) { }, "should return error when sandbox container is not deleted": { injectMetadata: true, - sandboxContainers: []container.Container{{ID: testID}}, + sandboxContainers: []task.Task{{ID: testID}}, expectErr: true, expectCalls: []string{"info"}, }, diff --git a/pkg/server/sandbox_run.go b/pkg/server/sandbox_run.go index a29298b61..b6d1a7edc 100644 --- a/pkg/server/sandbox_run.go +++ b/pkg/server/sandbox_run.go @@ -24,8 +24,7 @@ import ( "time" "github.com/containerd/containerd/api/services/execution" - rootfsapi "github.com/containerd/containerd/api/services/rootfs" - prototypes "github.com/gogo/protobuf/types" + snapshotapi "github.com/containerd/containerd/api/services/snapshot" "github.com/golang/glog" imagedigest "github.com/opencontainers/go-digest" imagespec "github.com/opencontainers/image-spec/specs-go/v1" @@ -87,16 +86,16 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run 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, + prepareResp, err := c.snapshotService.Prepare(ctx, &snapshotapi.PrepareRequest{ + Key: id, // We are sure that ChainID must be a digest. - ChainID: imagedigest.Digest(imageMeta.ChainID), - Readonly: true, + Parent: imagedigest.Digest(imageMeta.ChainID).String(), + //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. + // TODO(random-liu): [P0] Cleanup snapshot on failure after switching to new snapshot api. rootfsMounts := prepareResp.Mounts // Create sandbox container root directory. @@ -150,19 +149,14 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run if err != nil { return nil, fmt.Errorf("failed to generate sandbox container spec: %v", err) } - rawSpec, err := json.Marshal(spec) + _, err = json.Marshal(spec) if err != nil { return nil, fmt.Errorf("failed to marshal oci spec %+v: %v", spec, err) } glog.V(4).Infof("Sandbox container spec: %+v", spec) createOpts := &execution.CreateRequest{ - ID: id, - Spec: &prototypes.Any{ - TypeUrl: runtimespec.Version, - Value: rawSpec, - }, - Rootfs: rootfsMounts, - Runtime: defaultRuntime, + ContainerID: id, + Rootfs: rootfsMounts, // No stdin for sandbox container. Stdout: stdout, Stderr: stderr, @@ -179,7 +173,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run defer func() { if retErr != nil { // Cleanup the sandbox container if an error is returned. - if _, err = c.containerService.Delete(ctx, &execution.DeleteRequest{ID: id}); err != nil { + if _, err = c.containerService.Delete(ctx, &execution.DeleteRequest{ContainerID: id}); err != nil { glog.Errorf("Failed to delete sandbox container %q: %v", id, err) } @@ -205,7 +199,7 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run } // Start sandbox container in containerd. - if _, err := c.containerService.Start(ctx, &execution.StartRequest{ID: id}); err != nil { + if _, err := c.containerService.Start(ctx, &execution.StartRequest{ContainerID: id}); err != nil { return nil, fmt.Errorf("failed to start sandbox container %q: %v", id, err) } diff --git a/pkg/server/sandbox_run_test.go b/pkg/server/sandbox_run_test.go index 48109cd94..58273055c 100644 --- a/pkg/server/sandbox_run_test.go +++ b/pkg/server/sandbox_run_test.go @@ -17,15 +17,13 @@ limitations under the License. package server import ( - "encoding/json" "io" "os" "syscall" "testing" "github.com/containerd/containerd/api/services/execution" - rootfsapi "github.com/containerd/containerd/api/services/rootfs" - imagedigest "github.com/opencontainers/go-digest" + snapshotapi "github.com/containerd/containerd/api/services/snapshot" imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" @@ -268,9 +266,9 @@ options timeout:1 } func TestRunPodSandbox(t *testing.T) { - config, imageConfig, specCheck := getRunPodSandboxTestData() + config, imageConfig, _ := getRunPodSandboxTestData() // TODO: declare and test specCheck see below c := newTestCRIContainerdService() - fakeRootfsClient := c.rootfsService.(*servertesting.FakeRootfsClient) + fakeSnapshotClient := c.snapshotService.(*servertesting.FakeSnapshotClient) fakeExecutionClient := c.containerService.(*servertesting.FakeExecutionClient) fakeCNIPlugin := c.netPlugin.(*servertesting.FakeCNIPlugin) fakeOS := c.os.(*ostesting.FakeOS) @@ -286,17 +284,15 @@ func TestRunPodSandbox(t *testing.T) { assert.Equal(t, os.FileMode(0700), perm) return nopReadWriteCloser{}, nil } - testChainID := imagedigest.Digest("test-sandbox-chain-id") + testChainID := "test-sandbox-chain-id" imageMetadata := metadata.ImageMetadata{ ID: testSandboxImage, - ChainID: testChainID.String(), + ChainID: testChainID, Config: imageConfig, } // Insert sandbox image metadata. assert.NoError(t, c.imageMetadataStore.Create(imageMetadata)) - // Insert fake chainID - fakeRootfsClient.SetFakeChainIDs([]imagedigest.Digest{testChainID}) - expectRootfsClientCalls := []string{"prepare"} + expectSnapshotClientCalls := []string{"prepare"} expectExecutionClientCalls := []string{"create", "start"} res, err := c.RunPodSandbox(context.Background(), &runtime.RunPodSandboxRequest{Config: config}) @@ -312,30 +308,31 @@ 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, + assert.Equal(t, expectSnapshotClientCalls, fakeSnapshotClient.GetCalledNames(), "expect snapshot functions should be called") + calls := fakeSnapshotClient.GetCalledDetails() + prepareOpts := calls[0].Argument.(*snapshotapi.PrepareRequest) + assert.Equal(t, &snapshotapi.PrepareRequest{ + Key: id, + Parent: testChainID, }, prepareOpts, "prepare request should be correct") assert.Equal(t, expectExecutionClientCalls, fakeExecutionClient.GetCalledNames(), "expect containerd functions should be called") calls = fakeExecutionClient.GetCalledDetails() createOpts := calls[0].Argument.(*execution.CreateRequest) - assert.Equal(t, id, createOpts.ID, "create id should be correct") + assert.Equal(t, id, createOpts.ContainerID, "create id should be correct") 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}) + mountsResp, err := fakeSnapshotClient.Mounts(context.Background(), &snapshotapi.MountsRequest{Key: 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") - specCheck(t, id, spec) - startID := calls[1].Argument.(*execution.StartRequest).ID + // TODO: Need to create container first.. see Create in containerd/containerd/apsi/services/containers spec is no longer in the create request + //spec := &runtimespec.Spec{} + //assert.NoError(t, json.Unmarshal(createOpts.Spec.Value, spec)) + //t.Logf("oci spec check") + //specCheck(t, id, spec) + + startID := calls[1].Argument.(*execution.StartRequest).ContainerID assert.Equal(t, id, startID, "start id should be correct") meta, err := c.sandboxStore.Get(id) @@ -346,9 +343,9 @@ func TestRunPodSandbox(t *testing.T) { assert.Equal(t, config, meta.Config, "metadata config should be correct") // TODO(random-liu): [P2] Add clock interface and use fake clock. assert.NotZero(t, meta.CreatedAt, "metadata CreatedAt should be set") - info, err := fakeExecutionClient.Info(context.Background(), &execution.InfoRequest{ID: id}) + info, err := fakeExecutionClient.Info(context.Background(), &execution.InfoRequest{ContainerID: id}) assert.NoError(t, err) - pid := info.Pid + pid := info.Task.Pid assert.Equal(t, meta.NetNS, getNetworkNamespace(pid), "metadata network namespace should be correct") gotID, err := c.sandboxIDIndex.Get(id) @@ -359,7 +356,12 @@ func TestRunPodSandbox(t *testing.T) { assert.Equal(t, expectedCNICalls, fakeCNIPlugin.GetCalledNames(), "expect SetUpPod should be called") calls = fakeCNIPlugin.GetCalledDetails() pluginArgument := calls[0].Argument.(servertesting.CNIPluginArgument) - expectedPluginArgument := servertesting.CNIPluginArgument{meta.NetNS, config.GetMetadata().GetNamespace(), config.GetMetadata().GetName(), id} + expectedPluginArgument := servertesting.CNIPluginArgument{ + NetnsPath: meta.NetNS, + Namespace: config.GetMetadata().GetNamespace(), + Name: config.GetMetadata().GetName(), + ContainerID: id, + } assert.Equal(t, expectedPluginArgument, pluginArgument, "SetUpPod should be called with correct arguments") } diff --git a/pkg/server/sandbox_status.go b/pkg/server/sandbox_status.go index 291cbd1dc..3511b47f5 100644 --- a/pkg/server/sandbox_status.go +++ b/pkg/server/sandbox_status.go @@ -23,7 +23,7 @@ import ( "golang.org/x/net/context" "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" @@ -47,7 +47,7 @@ func (c *criContainerdService) PodSandboxStatus(ctx context.Context, r *runtime. // Use the full sandbox id. id := sandbox.ID - info, err := c.containerService.Info(ctx, &execution.InfoRequest{ID: id}) + info, err := c.containerService.Info(ctx, &execution.InfoRequest{ContainerID: id}) if err != nil && !isContainerdContainerNotExistError(err) { return nil, fmt.Errorf("failed to get sandbox container info for %q: %v", id, err) } @@ -55,7 +55,7 @@ func (c *criContainerdService) PodSandboxStatus(ctx context.Context, r *runtime. // Set sandbox state to NOTREADY by default. state := runtime.PodSandboxState_SANDBOX_NOTREADY // If the sandbox container is running, treat it as READY. - if info != nil && info.Status == container.Status_RUNNING { + if info != nil && info.Task.Status == task.StatusRunning { state = runtime.PodSandboxState_SANDBOX_READY } diff --git a/pkg/server/sandbox_status_test.go b/pkg/server/sandbox_status_test.go index e47baf36c..a9c1fdb03 100644 --- a/pkg/server/sandbox_status_test.go +++ b/pkg/server/sandbox_status_test.go @@ -25,7 +25,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/net/context" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" @@ -95,7 +95,7 @@ func getSandboxStatusTestData() (*metadata.SandboxMetadata, *runtime.PodSandboxS func TestPodSandboxStatus(t *testing.T) { for desc, test := range map[string]struct { - sandboxContainers []container.Container + sandboxContainers []task.Task injectMetadata bool injectErr error injectIP bool @@ -112,10 +112,10 @@ func TestPodSandboxStatus(t *testing.T) { expectedCNICalls: []string{}, }, "sandbox status with running sandbox container": { - sandboxContainers: []container.Container{{ + sandboxContainers: []task.Task{{ ID: sandboxStatusTestID, Pid: 1, - Status: container.Status_RUNNING, + Status: task.StatusRunning, }}, injectMetadata: true, expectState: runtime.PodSandboxState_SANDBOX_READY, @@ -123,10 +123,10 @@ func TestPodSandboxStatus(t *testing.T) { expectedCNICalls: []string{"GetContainerNetworkStatus"}, }, "sandbox status with stopped sandbox container": { - sandboxContainers: []container.Container{{ + sandboxContainers: []task.Task{{ ID: sandboxStatusTestID, Pid: 1, - Status: container.Status_STOPPED, + Status: task.StatusStopped, }}, injectMetadata: true, expectState: runtime.PodSandboxState_SANDBOX_NOTREADY, @@ -134,17 +134,17 @@ func TestPodSandboxStatus(t *testing.T) { expectedCNICalls: []string{"GetContainerNetworkStatus"}, }, "sandbox status with non-existing sandbox container": { - sandboxContainers: []container.Container{}, + sandboxContainers: []task.Task{}, injectMetadata: true, expectState: runtime.PodSandboxState_SANDBOX_NOTREADY, expectCalls: []string{"info"}, expectedCNICalls: []string{"GetContainerNetworkStatus"}, }, "sandbox status with arbitrary error": { - sandboxContainers: []container.Container{{ + sandboxContainers: []task.Task{{ ID: sandboxStatusTestID, Pid: 1, - Status: container.Status_RUNNING, + Status: task.StatusRunning, }}, injectMetadata: true, expectState: runtime.PodSandboxState_SANDBOX_READY, @@ -154,10 +154,10 @@ func TestPodSandboxStatus(t *testing.T) { expectedCNICalls: []string{}, }, "sandbox status with IP address": { - sandboxContainers: []container.Container{{ + sandboxContainers: []task.Task{{ ID: sandboxStatusTestID, Pid: 1, - Status: container.Status_RUNNING, + Status: task.StatusRunning, }}, injectMetadata: true, expectState: runtime.PodSandboxState_SANDBOX_READY, @@ -166,10 +166,10 @@ func TestPodSandboxStatus(t *testing.T) { expectedCNICalls: []string{"GetContainerNetworkStatus"}, }, "sandbox status with GetContainerNetworkStatus returns error": { - sandboxContainers: []container.Container{{ + sandboxContainers: []task.Task{{ ID: sandboxStatusTestID, Pid: 1, - Status: container.Status_RUNNING, + Status: task.StatusRunning, }}, injectMetadata: true, expectState: runtime.PodSandboxState_SANDBOX_READY, diff --git a/pkg/server/sandbox_stop.go b/pkg/server/sandbox_stop.go index 934708ffe..037141964 100644 --- a/pkg/server/sandbox_stop.go +++ b/pkg/server/sandbox_stop.go @@ -60,7 +60,7 @@ func (c *criContainerdService) StopPodSandbox(ctx context.Context, r *runtime.St // TODO(random-liu): [P1] Handle sandbox container graceful deletion. // Delete the sandbox container from containerd. - _, err = c.containerService.Delete(ctx, &execution.DeleteRequest{ID: id}) + _, err = c.containerService.Delete(ctx, &execution.DeleteRequest{ContainerID: id}) if err != nil && !isContainerdContainerNotExistError(err) { return nil, fmt.Errorf("failed to delete sandbox container %q: %v", id, err) } diff --git a/pkg/server/sandbox_stop_test.go b/pkg/server/sandbox_stop_test.go index 0f9c424c3..81b1868b5 100644 --- a/pkg/server/sandbox_stop_test.go +++ b/pkg/server/sandbox_stop_test.go @@ -26,8 +26,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" - "github.com/containerd/containerd" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" + "github.com/containerd/containerd/plugin" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" @@ -49,14 +49,14 @@ func TestStopPodSandbox(t *testing.T) { }}, NetNS: "test-netns", } - testContainer := container.Container{ + testContainer := task.Task{ ID: testID, Pid: 1, - Status: container.Status_RUNNING, + Status: task.StatusRunning, } for desc, test := range map[string]struct { - sandboxContainers []container.Container + sandboxContainers []task.Task injectSandbox bool injectErr error injectStatErr error @@ -72,17 +72,17 @@ func TestStopPodSandbox(t *testing.T) { expectedCNICalls: []string{}, }, "stop sandbox with sandbox container": { - sandboxContainers: []container.Container{testContainer}, + sandboxContainers: []task.Task{testContainer}, injectSandbox: true, expectErr: false, expectCalls: []string{"delete"}, expectedCNICalls: []string{"TearDownPod"}, }, "stop sandbox with sandbox container not exist error": { - sandboxContainers: []container.Container{}, + sandboxContainers: []task.Task{}, injectSandbox: true, // Inject error to make sure fake execution client returns error. - injectErr: grpc.Errorf(codes.Unknown, containerd.ErrContainerNotExist.Error()), + injectErr: grpc.Errorf(codes.Unknown, plugin.ErrContainerNotExist.Error()), expectErr: false, expectCalls: []string{"delete"}, expectedCNICalls: []string{"TearDownPod"}, @@ -95,7 +95,7 @@ func TestStopPodSandbox(t *testing.T) { expectedCNICalls: []string{"TearDownPod"}, }, "stop sandbox with Stat returns arbitrary error": { - sandboxContainers: []container.Container{testContainer}, + sandboxContainers: []task.Task{testContainer}, injectSandbox: true, expectErr: true, injectStatErr: errors.New("arbitrary error"), @@ -103,7 +103,7 @@ func TestStopPodSandbox(t *testing.T) { expectedCNICalls: []string{}, }, "stop sandbox with Stat returns not exist error": { - sandboxContainers: []container.Container{testContainer}, + sandboxContainers: []task.Task{testContainer}, injectSandbox: true, expectErr: false, expectCalls: []string{"delete"}, @@ -111,7 +111,7 @@ func TestStopPodSandbox(t *testing.T) { expectedCNICalls: []string{}, }, "stop sandbox with TearDownPod fails": { - sandboxContainers: []container.Container{testContainer}, + sandboxContainers: []task.Task{testContainer}, injectSandbox: true, expectErr: true, expectedCNICalls: []string{"TearDownPod"}, diff --git a/pkg/server/service.go b/pkg/server/service.go index 54de8257a..58e302788 100644 --- a/pkg/server/service.go +++ b/pkg/server/service.go @@ -22,7 +22,7 @@ import ( contentapi "github.com/containerd/containerd/api/services/content" "github.com/containerd/containerd/api/services/execution" imagesapi "github.com/containerd/containerd/api/services/images" - rootfsapi "github.com/containerd/containerd/api/services/rootfs" + snapshotapi "github.com/containerd/containerd/api/services/snapshot" versionapi "github.com/containerd/containerd/api/services/version" "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" @@ -73,12 +73,12 @@ type criContainerdService struct { // containerNameIndex stores all container names and make sure each // name is unique. containerNameIndex *registrar.Registrar - // containerService is containerd container service client. - containerService execution.ContainerServiceClient + // containerService is containerd tasks client. + containerService execution.TasksClient // contentStoreService is the containerd content service client. contentStoreService content.Store - // rootfsService is the containerd rootfs service client. - rootfsService rootfsapi.RootFSClient + // snapshotService is the containerd snapshot service client. + snapshotService snapshotapi.SnapshotClient // imageStoreService is the containerd service to store and track // image metadata. imageStoreService images.Store @@ -108,10 +108,10 @@ func NewCRIContainerdService(conn *grpc.ClientConn, rootDir, networkPluginBinDir sandboxIDIndex: truncindex.NewTruncIndex(nil), // TODO(random-liu): Add container id index. containerNameIndex: registrar.NewRegistrar(), - containerService: execution.NewContainerServiceClient(conn), + containerService: execution.NewTasksClient(conn), imageStoreService: imagesservice.NewStoreFromClient(imagesapi.NewImagesClient(conn)), contentStoreService: contentservice.NewStoreFromClient(contentapi.NewContentClient(conn)), - rootfsService: rootfsapi.NewRootFSClient(conn), + snapshotService: snapshotapi.NewSnapshotClient(conn), versionService: versionapi.NewVersionClient(conn), healthService: healthapi.NewHealthClient(conn), agentFactory: agents.NewAgentFactory(), diff --git a/pkg/server/service_test.go b/pkg/server/service_test.go index 5be122796..32ada92d1 100644 --- a/pkg/server/service_test.go +++ b/pkg/server/service_test.go @@ -33,7 +33,6 @@ import ( "github.com/kubernetes-incubator/cri-containerd/pkg/registrar" agentstesting "github.com/kubernetes-incubator/cri-containerd/pkg/server/agents/testing" 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" @@ -67,7 +66,7 @@ func newTestCRIContainerdService() *criContainerdService { containerStore: metadata.NewContainerStore(store.NewMetadataStore()), containerNameIndex: registrar.NewRegistrar(), containerService: servertesting.NewFakeExecutionClient(), - rootfsService: servertesting.NewFakeRootfsClient(), + snapshotService: servertesting.NewFakeSnapshotClient(), netPlugin: servertesting.NewFakeCNIPlugin(), agentFactory: agentstesting.NewFakeAgentFactory(), } @@ -77,7 +76,8 @@ func newTestCRIContainerdService() *criContainerdService { func TestSandboxOperations(t *testing.T) { c := newTestCRIContainerdService() fake := c.containerService.(*servertesting.FakeExecutionClient) - fakeRootfsClient := c.rootfsService.(*servertesting.FakeRootfsClient) + // TODO(random-liu): Clean this up if needed. + // fakeSnapshotClient := c.snapshotService.(*servertesting.FakeSnapshotClient) 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) { @@ -89,8 +89,6 @@ func TestSandboxOperations(t *testing.T) { 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{ @@ -112,8 +110,8 @@ func TestSandboxOperations(t *testing.T) { id := runRes.GetPodSandboxId() t.Logf("should be able to get pod sandbox status") - info, err := fake.Info(context.Background(), &execution.InfoRequest{ID: id}) - netns := getNetworkNamespace(info.Pid) + info, err := fake.Info(context.Background(), &execution.InfoRequest{ContainerID: id}) + netns := getNetworkNamespace(info.Task.Pid) assert.NoError(t, err) expectSandboxStatus := &runtime.PodSandboxStatus{ Id: id, @@ -146,7 +144,7 @@ func TestSandboxOperations(t *testing.T) { expectSandbox := &runtime.PodSandbox{ Id: id, Metadata: config.GetMetadata(), - State: runtime.PodSandboxState_SANDBOX_READY, + State: runtime.PodSandboxState_SANDBOX_NOTREADY, // TODO(mikebrow) converting to client... should this be ready? Labels: config.GetLabels(), Annotations: config.GetAnnotations(), } diff --git a/pkg/server/testing/fake_execution_client.go b/pkg/server/testing/fake_execution_client.go index b5ec59089..7a3182529 100644 --- a/pkg/server/testing/fake_execution_client.go +++ b/pkg/server/testing/fake_execution_client.go @@ -22,9 +22,9 @@ import ( "sync" "time" - "github.com/containerd/containerd" "github.com/containerd/containerd/api/services/execution" - "github.com/containerd/containerd/api/types/container" + "github.com/containerd/containerd/api/types/task" + "github.com/containerd/containerd/plugin" googleprotobuf "github.com/golang/protobuf/ptypes/empty" "golang.org/x/net/context" "google.golang.org/grpc" @@ -32,7 +32,7 @@ import ( ) // ContainerNotExistError is the fake error returned when container does not exist. -var ContainerNotExistError = grpc.Errorf(codes.Unknown, containerd.ErrContainerNotExist.Error()) +var ContainerNotExistError = grpc.Errorf(codes.Unknown, plugin.ErrContainerNotExist.Error()) // CalledDetail is the struct contains called function name and arguments. type CalledDetail struct { @@ -42,16 +42,16 @@ type CalledDetail struct { Argument interface{} } -var _ execution.ContainerService_EventsClient = &EventClient{} +var _ execution.Tasks_EventsClient = &EventClient{} // EventClient is a test implementation of execution.ContainerService_EventsClient type EventClient struct { - Events chan *container.Event + Events chan *task.Event grpc.ClientStream } // Recv is a test implementation of Recv -func (cli *EventClient) Recv() (*container.Event, error) { +func (cli *EventClient) Recv() (*task.Event, error) { event, ok := <-cli.Events if !ok { return nil, fmt.Errorf("event channel closed") @@ -65,18 +65,18 @@ type FakeExecutionClient struct { sync.Mutex called []CalledDetail errors map[string]error - ContainerList map[string]container.Container - eventsQueue chan *container.Event + ContainerList map[string]task.Task + eventsQueue chan *task.Event eventClients []*EventClient } -var _ execution.ContainerServiceClient = &FakeExecutionClient{} +var _ execution.TasksClient = &FakeExecutionClient{} // NewFakeExecutionClient creates a FakeExecutionClient func NewFakeExecutionClient() *FakeExecutionClient { return &FakeExecutionClient{ errors: make(map[string]error), - ContainerList: make(map[string]container.Container), + ContainerList: make(map[string]task.Task), } } @@ -95,7 +95,7 @@ func (f *FakeExecutionClient) Stop() { // WithEvents setup events publisher for FakeExecutionClient func (f *FakeExecutionClient) WithEvents() *FakeExecutionClient { - f.eventsQueue = make(chan *container.Event, 1024) + f.eventsQueue = make(chan *task.Event, 1024) go func() { for e := range f.eventsQueue { f.Lock() @@ -146,7 +146,7 @@ func generatePid() uint32 { return randPid } -func (f *FakeExecutionClient) sendEvent(event *container.Event) { +func (f *FakeExecutionClient) sendEvent(event *task.Event) { if f.eventsQueue != nil { f.eventsQueue <- event } @@ -184,7 +184,7 @@ func (f *FakeExecutionClient) GetCalledDetails() []CalledDetail { } // SetFakeContainers injects fake containers. -func (f *FakeExecutionClient) SetFakeContainers(containers []container.Container) { +func (f *FakeExecutionClient) SetFakeContainers(containers []task.Task) { f.Lock() defer f.Unlock() for _, c := range containers { @@ -200,24 +200,24 @@ func (f *FakeExecutionClient) Create(ctx context.Context, createOpts *execution. if err := f.getError("create"); err != nil { return nil, err } - _, ok := f.ContainerList[createOpts.ID] + _, ok := f.ContainerList[createOpts.ContainerID] if ok { - return nil, containerd.ErrContainerExists + return nil, plugin.ErrContainerExists } pid := generatePid() - f.ContainerList[createOpts.ID] = container.Container{ - ID: createOpts.ID, - Pid: pid, - Status: container.Status_CREATED, + f.ContainerList[createOpts.ContainerID] = task.Task{ + ContainerID: createOpts.ContainerID, + Pid: pid, + Status: task.StatusCreated, } - f.sendEvent(&container.Event{ - ID: createOpts.ID, - Type: container.Event_CREATE, + f.sendEvent(&task.Event{ + ID: createOpts.ContainerID, + Type: task.Event_CREATE, Pid: pid, }) return &execution.CreateResponse{ - ID: createOpts.ID, - Pid: pid, + ContainerID: createOpts.ContainerID, + Pid: pid, }, nil } @@ -229,23 +229,23 @@ func (f *FakeExecutionClient) Start(ctx context.Context, startOpts *execution.St if err := f.getError("start"); err != nil { return nil, err } - c, ok := f.ContainerList[startOpts.ID] + c, ok := f.ContainerList[startOpts.ContainerID] if !ok { return nil, ContainerNotExistError } - f.sendEvent(&container.Event{ + f.sendEvent(&task.Event{ ID: c.ID, - Type: container.Event_START, + Type: task.Event_START, Pid: c.Pid, }) switch c.Status { - case container.Status_CREATED: - c.Status = container.Status_RUNNING - f.ContainerList[startOpts.ID] = c + case task.StatusCreated: + c.Status = task.StatusRunning + f.ContainerList[startOpts.ContainerID] = c return &googleprotobuf.Empty{}, nil - case container.Status_STOPPED: + case task.StatusStopped: return &googleprotobuf.Empty{}, fmt.Errorf("cannot start a container that has stopped") - case container.Status_RUNNING: + case task.StatusRunning: return &googleprotobuf.Empty{}, fmt.Errorf("cannot start an already running container") default: return &googleprotobuf.Empty{}, fmt.Errorf("cannot start a container in the %s state", c.Status) @@ -260,32 +260,32 @@ func (f *FakeExecutionClient) Delete(ctx context.Context, deleteOpts *execution. if err := f.getError("delete"); err != nil { return nil, err } - c, ok := f.ContainerList[deleteOpts.ID] + c, ok := f.ContainerList[deleteOpts.ContainerID] if !ok { return nil, ContainerNotExistError } - delete(f.ContainerList, deleteOpts.ID) - f.sendEvent(&container.Event{ + delete(f.ContainerList, deleteOpts.ContainerID) + f.sendEvent(&task.Event{ ID: c.ID, - Type: container.Event_EXIT, + Type: task.Event_EXIT, Pid: c.Pid, }) return nil, nil } // Info is a test implementation of execution.Info -func (f *FakeExecutionClient) Info(ctx context.Context, infoOpts *execution.InfoRequest, opts ...grpc.CallOption) (*container.Container, error) { +func (f *FakeExecutionClient) Info(ctx context.Context, infoOpts *execution.InfoRequest, opts ...grpc.CallOption) (*execution.InfoResponse, error) { f.Lock() defer f.Unlock() f.appendCalled("info", infoOpts) if err := f.getError("info"); err != nil { return nil, err } - c, ok := f.ContainerList[infoOpts.ID] + c, ok := f.ContainerList[infoOpts.ContainerID] if !ok { return nil, ContainerNotExistError } - return &c, nil + return &execution.InfoResponse{Task: &c}, nil } // List is a test implementation of execution.List @@ -298,7 +298,7 @@ func (f *FakeExecutionClient) List(ctx context.Context, listOpts *execution.List } resp := &execution.ListResponse{} for _, c := range f.ContainerList { - resp.Containers = append(resp.Containers, &container.Container{ + resp.Tasks = append(resp.Tasks, &task.Task{ ID: c.ID, Pid: c.Pid, Status: c.Status, @@ -315,22 +315,22 @@ func (f *FakeExecutionClient) Kill(ctx context.Context, killOpts *execution.Kill if err := f.getError("kill"); err != nil { return nil, err } - c, ok := f.ContainerList[killOpts.ID] + c, ok := f.ContainerList[killOpts.ContainerID] if !ok { return nil, ContainerNotExistError } - c.Status = container.Status_STOPPED - f.ContainerList[killOpts.ID] = c - f.sendEvent(&container.Event{ + c.Status = task.StatusStopped + f.ContainerList[killOpts.ContainerID] = c + f.sendEvent(&task.Event{ ID: c.ID, - Type: container.Event_EXIT, + Type: task.Event_EXIT, Pid: c.Pid, }) return &googleprotobuf.Empty{}, nil } // Events is a test implementation of execution.Events -func (f *FakeExecutionClient) Events(ctx context.Context, eventsOpts *execution.EventsRequest, opts ...grpc.CallOption) (execution.ContainerService_EventsClient, error) { +func (f *FakeExecutionClient) Events(ctx context.Context, eventsOpts *execution.EventsRequest, opts ...grpc.CallOption) (execution.Tasks_EventsClient, error) { f.Lock() defer f.Unlock() f.appendCalled("events", eventsOpts) @@ -338,7 +338,7 @@ func (f *FakeExecutionClient) Events(ctx context.Context, eventsOpts *execution. return nil, err } var client = &EventClient{ - Events: make(chan *container.Event, 100), + Events: make(chan *task.Event, 100), } f.eventClients = append(f.eventClients, client) return client, nil @@ -373,3 +373,15 @@ func (f *FakeExecutionClient) Resume(ctx context.Context, in *execution.ResumeRe // TODO: implement Resume() return nil, nil } + +// Checkpoint is a test implementation of execution.Checkpoint +func (f *FakeExecutionClient) Checkpoint(ctx context.Context, in *execution.CheckpointRequest, opts ...grpc.CallOption) (*execution.CheckpointResponse, error) { + // TODO: implement Checkpoint() + return nil, nil +} + +// Processes is a test implementation of execution.Processes +func (f *FakeExecutionClient) Processes(ctx context.Context, in *execution.ProcessesRequest, opts ...grpc.CallOption) (*execution.ProcessesResponse, error) { + // TODO: implement Processes() + return nil, nil +} diff --git a/pkg/server/testing/fake_rootfs_client.go b/pkg/server/testing/fake_rootfs_client.go deleted file mode 100644 index da10522a7..000000000 --- a/pkg/server/testing/fake_rootfs_client.go +++ /dev/null @@ -1,197 +0,0 @@ -/* -Copyright 2017 The Kubernetes 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 testing - -import ( - "fmt" - "sync" - - "github.com/containerd/containerd/api/services/rootfs" - "github.com/containerd/containerd/api/types/descriptor" - "github.com/containerd/containerd/api/types/mount" - "github.com/opencontainers/go-digest" - "github.com/opencontainers/image-spec/identity" - "golang.org/x/net/context" - "google.golang.org/grpc" -) - -// FakeRootfsClient is a simple fake rootfs client, so that cri-containerd -// can be run for testing without requiring a real containerd setup. -type FakeRootfsClient struct { - sync.Mutex - called []CalledDetail - errors map[string]error - ChainIDList map[digest.Digest]struct{} - MountList map[string][]*mount.Mount -} - -var _ rootfs.RootFSClient = &FakeRootfsClient{} - -// NewFakeRootfsClient creates a FakeRootfsClient -func NewFakeRootfsClient() *FakeRootfsClient { - return &FakeRootfsClient{ - errors: make(map[string]error), - ChainIDList: make(map[digest.Digest]struct{}), - MountList: make(map[string][]*mount.Mount), - } -} - -func (f *FakeRootfsClient) getError(op string) error { - err, ok := f.errors[op] - if ok { - delete(f.errors, op) - return err - } - return nil -} - -// InjectError inject error for call -func (f *FakeRootfsClient) InjectError(fn string, err error) { - f.Lock() - defer f.Unlock() - f.errors[fn] = err -} - -// InjectErrors inject errors for calls -func (f *FakeRootfsClient) InjectErrors(errs map[string]error) { - f.Lock() - defer f.Unlock() - for fn, err := range errs { - f.errors[fn] = err - } -} - -// ClearErrors clear errors for call -func (f *FakeRootfsClient) ClearErrors() { - f.Lock() - defer f.Unlock() - f.errors = make(map[string]error) -} - -//For uncompressed layers, diffID and digest will be the same. For compressed -//layers, we can look up the diffID from the digest if we've already unpacked it. -//In the FakeRootfsClient, We just use layer digest as diffID. -func generateChainID(layers []*descriptor.Descriptor) digest.Digest { - var digests []digest.Digest - for _, layer := range layers { - digests = append(digests, layer.Digest) - } - parent := identity.ChainID(digests) - return parent -} - -func (f *FakeRootfsClient) appendCalled(name string, argument interface{}) { - call := CalledDetail{Name: name, Argument: argument} - f.called = append(f.called, call) -} - -// GetCalledNames get names of call -func (f *FakeRootfsClient) GetCalledNames() []string { - f.Lock() - defer f.Unlock() - names := []string{} - for _, detail := range f.called { - names = append(names, detail.Name) - } - return names -} - -// GetCalledDetails get detail of each call. -func (f *FakeRootfsClient) GetCalledDetails() []CalledDetail { - f.Lock() - defer f.Unlock() - // Copy the list and return. - return append([]CalledDetail{}, f.called...) -} - -// SetFakeChainIDs injects fake chainIDs. -func (f *FakeRootfsClient) SetFakeChainIDs(chainIDs []digest.Digest) { - f.Lock() - defer f.Unlock() - for _, c := range chainIDs { - f.ChainIDList[c] = struct{}{} - } -} - -// SetFakeMounts injects fake mounts. -func (f *FakeRootfsClient) SetFakeMounts(name string, mounts []*mount.Mount) { - f.Lock() - defer f.Unlock() - f.MountList[name] = mounts -} - -// Unpack is a test implementation of rootfs.Unpack -func (f *FakeRootfsClient) Unpack(ctx context.Context, unpackOpts *rootfs.UnpackRequest, opts ...grpc.CallOption) (*rootfs.UnpackResponse, error) { - f.Lock() - defer f.Unlock() - f.appendCalled("unpack", unpackOpts) - if err := f.getError("unpack"); err != nil { - return nil, err - } - chainID := generateChainID(unpackOpts.Layers) - _, ok := f.ChainIDList[chainID] - if ok { - return nil, fmt.Errorf("already unpacked") - } - f.ChainIDList[chainID] = struct{}{} - return &rootfs.UnpackResponse{ - ChainID: chainID, - }, nil -} - -// Prepare is a test implementation of rootfs.Prepare -func (f *FakeRootfsClient) Prepare(ctx context.Context, prepareOpts *rootfs.PrepareRequest, opts ...grpc.CallOption) (*rootfs.MountResponse, error) { - f.Lock() - defer f.Unlock() - f.appendCalled("prepare", prepareOpts) - if err := f.getError("prepare"); err != nil { - return nil, err - } - _, ok := f.ChainIDList[prepareOpts.ChainID] - if !ok { - return nil, fmt.Errorf("have not been unpacked") - } - _, ok = f.MountList[prepareOpts.Name] - if ok { - return nil, fmt.Errorf("mounts already exist") - } - 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 -} - -// Mounts is a test implementation of rootfs.Mounts -func (f *FakeRootfsClient) Mounts(ctx context.Context, mountsOpts *rootfs.MountsRequest, opts ...grpc.CallOption) (*rootfs.MountResponse, error) { - f.Lock() - defer f.Unlock() - f.appendCalled("mounts", mountsOpts) - if err := f.getError("mounts"); err != nil { - return nil, err - } - mounts, ok := f.MountList[mountsOpts.Name] - if !ok { - return nil, fmt.Errorf("mounts not exist") - } - return &rootfs.MountResponse{ - Mounts: mounts, - }, nil -} diff --git a/pkg/server/testing/fake_snapshot_client.go b/pkg/server/testing/fake_snapshot_client.go new file mode 100644 index 000000000..1ce1631c8 --- /dev/null +++ b/pkg/server/testing/fake_snapshot_client.go @@ -0,0 +1,179 @@ +/* +Copyright 2017 The Kubernetes 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 testing + +import ( + "fmt" + "sync" + + "github.com/containerd/containerd/api/services/snapshot" + "github.com/containerd/containerd/api/types/mount" + google_protobuf1 "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// FakeSnapshotClient is a simple fake snapshot client, so that cri-containerd +// can be run for testing without requiring a real containerd setup. +type FakeSnapshotClient struct { + sync.Mutex + called []CalledDetail + errors map[string]error + MountList map[string][]*mount.Mount +} + +var _ snapshot.SnapshotClient = &FakeSnapshotClient{} + +// NewFakeSnapshotClient creates a FakeSnapshotClient +func NewFakeSnapshotClient() *FakeSnapshotClient { + return &FakeSnapshotClient{ + errors: make(map[string]error), + MountList: make(map[string][]*mount.Mount), + } +} + +func (f *FakeSnapshotClient) getError(op string) error { + err, ok := f.errors[op] + if ok { + delete(f.errors, op) + return err + } + return nil +} + +// InjectError inject error for call +func (f *FakeSnapshotClient) InjectError(fn string, err error) { + f.Lock() + defer f.Unlock() + f.errors[fn] = err +} + +// InjectErrors inject errors for calls +func (f *FakeSnapshotClient) InjectErrors(errs map[string]error) { + f.Lock() + defer f.Unlock() + for fn, err := range errs { + f.errors[fn] = err + } +} + +// ClearErrors clear errors for call +func (f *FakeSnapshotClient) ClearErrors() { + f.Lock() + defer f.Unlock() + f.errors = make(map[string]error) +} + +func (f *FakeSnapshotClient) appendCalled(name string, argument interface{}) { + call := CalledDetail{Name: name, Argument: argument} + f.called = append(f.called, call) +} + +// GetCalledNames get names of call +func (f *FakeSnapshotClient) GetCalledNames() []string { + f.Lock() + defer f.Unlock() + names := []string{} + for _, detail := range f.called { + names = append(names, detail.Name) + } + return names +} + +// GetCalledDetails get detail of each call. +func (f *FakeSnapshotClient) GetCalledDetails() []CalledDetail { + f.Lock() + defer f.Unlock() + // Copy the list and return. + return append([]CalledDetail{}, f.called...) +} + +// SetFakeMounts injects fake mounts. +func (f *FakeSnapshotClient) SetFakeMounts(name string, mounts []*mount.Mount) { + f.Lock() + defer f.Unlock() + f.MountList[name] = mounts +} + +// Prepare is a test implementation of snapshot.Prepare +func (f *FakeSnapshotClient) Prepare(ctx context.Context, prepareOpts *snapshot.PrepareRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("prepare", prepareOpts) + if err := f.getError("prepare"); err != nil { + return nil, err + } + _, ok := f.MountList[prepareOpts.Key] + if ok { + return nil, fmt.Errorf("mounts already exist") + } + f.MountList[prepareOpts.Key] = []*mount.Mount{{ + Type: "bind", + Source: prepareOpts.Key, + // TODO(random-liu): Fake options based on Readonly option. + }} + return &snapshot.MountsResponse{ + Mounts: f.MountList[prepareOpts.Key], + }, nil +} + +// Mounts is a test implementation of snapshot.Mounts +func (f *FakeSnapshotClient) Mounts(ctx context.Context, mountsOpts *snapshot.MountsRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("mounts", mountsOpts) + if err := f.getError("mounts"); err != nil { + return nil, err + } + mounts, ok := f.MountList[mountsOpts.Key] + if !ok { + return nil, fmt.Errorf("mounts not exist") + } + return &snapshot.MountsResponse{ + Mounts: mounts, + }, nil +} + +// Commit is a test implementation of snapshot.Commit +func (f *FakeSnapshotClient) Commit(ctx context.Context, in *snapshot.CommitRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + return nil, nil +} + +// View is a test implementation of snapshot.View +func (f *FakeSnapshotClient) View(ctx context.Context, in *snapshot.PrepareRequest, opts ...grpc.CallOption) (*snapshot.MountsResponse, error) { + return nil, nil +} + +// Remove is a test implementation of snapshot.Remove +func (f *FakeSnapshotClient) Remove(ctx context.Context, in *snapshot.RemoveRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + return nil, nil +} + +// Stat is a test implementation of snapshot.Stat +func (f *FakeSnapshotClient) Stat(ctx context.Context, in *snapshot.StatRequest, opts ...grpc.CallOption) (*snapshot.StatResponse, error) { + return nil, nil +} + +// List is a test implementation of snapshot.List +func (f *FakeSnapshotClient) List(ctx context.Context, in *snapshot.ListRequest, opts ...grpc.CallOption) (snapshot.Snapshot_ListClient, error) { + return nil, nil +} + +// Usage is a test implementation of snapshot.Usage +func (f *FakeSnapshotClient) Usage(ctx context.Context, in *snapshot.UsageRequest, opts ...grpc.CallOption) (*snapshot.UsageResponse, error) { + return nil, nil +}