package containerd import ( "context" "testing" "github.com/containerd/containerd/images" "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/namespaces" "github.com/containerd/log/logtest" "github.com/docker/docker/container" daemonevents "github.com/docker/docker/daemon/events" dimages "github.com/docker/docker/daemon/images" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestImageDelete(t *testing.T) { ctx := namespaces.WithNamespace(context.TODO(), "testing") for _, tc := range []struct { ref string starting []images.Image remaining []images.Image err error // TODO: Records // TODO: Containers // TODO: Events }{ { ref: "nothingthere", err: dimages.ErrImageDoesNotExist{Ref: nameTag("nothingthere", "latest")}, }, { ref: "justoneimage", starting: []images.Image{ { Name: "docker.io/library/justoneimage:latest", Target: desc(10), }, }, }, { ref: "justoneref", starting: []images.Image{ { Name: "docker.io/library/justoneref:latest", Target: desc(10), }, { Name: "docker.io/library/differentrepo:latest", Target: desc(10), }, }, remaining: []images.Image{ { Name: "docker.io/library/differentrepo:latest", Target: desc(10), }, }, }, { ref: "hasdigest", starting: []images.Image{ { Name: "docker.io/library/hasdigest:latest", Target: desc(10), }, { Name: "docker.io/library/hasdigest@" + digestFor(10).String(), Target: desc(10), }, }, }, { ref: digestFor(11).String(), starting: []images.Image{ { Name: "docker.io/library/byid:latest", Target: desc(11), }, { Name: "docker.io/library/byid@" + digestFor(11).String(), Target: desc(11), }, }, }, { ref: "bydigest@" + digestFor(12).String(), starting: []images.Image{ { Name: "docker.io/library/bydigest:latest", Target: desc(12), }, { Name: "docker.io/library/bydigest@" + digestFor(12).String(), Target: desc(12), }, }, }, { ref: "onerefoftwo", starting: []images.Image{ { Name: "docker.io/library/onerefoftwo:latest", Target: desc(12), }, { Name: "docker.io/library/onerefoftwo:other", Target: desc(12), }, { Name: "docker.io/library/onerefoftwo@" + digestFor(12).String(), Target: desc(12), }, }, remaining: []images.Image{ { Name: "docker.io/library/onerefoftwo:other", Target: desc(12), }, { Name: "docker.io/library/onerefoftwo@" + digestFor(12).String(), Target: desc(12), }, }, }, { ref: "otherreporemaining", starting: []images.Image{ { Name: "docker.io/library/otherreporemaining:latest", Target: desc(12), }, { Name: "docker.io/library/otherreporemaining@" + digestFor(12).String(), Target: desc(12), }, { Name: "docker.io/library/someotherrepo:latest", Target: desc(12), }, }, remaining: []images.Image{ { Name: "docker.io/library/someotherrepo:latest", Target: desc(12), }, }, }, { ref: "repoanddigest@" + digestFor(15).String(), starting: []images.Image{ { Name: "docker.io/library/repoanddigest:latest", Target: desc(15), }, { Name: "docker.io/library/repoanddigest:latest@" + digestFor(15).String(), Target: desc(15), }, { Name: "docker.io/library/someotherrepo:latest", Target: desc(15), }, }, remaining: []images.Image{ { Name: "docker.io/library/someotherrepo:latest", Target: desc(15), }, }, }, { ref: "repoanddigestothertags@" + digestFor(15).String(), starting: []images.Image{ { Name: "docker.io/library/repoanddigestothertags:v1", Target: desc(15), }, { Name: "docker.io/library/repoanddigestothertags:v1@" + digestFor(15).String(), Target: desc(15), }, { Name: "docker.io/library/repoanddigestothertags:v2", Target: desc(15), }, { Name: "docker.io/library/repoanddigestothertags:v2@" + digestFor(15).String(), Target: desc(15), }, { Name: "docker.io/library/someotherrepo:latest", Target: desc(15), }, }, remaining: []images.Image{ { Name: "docker.io/library/someotherrepo:latest", Target: desc(15), }, }, }, { ref: "repoanddigestzerocase@" + digestFor(16).String(), starting: []images.Image{ { Name: "docker.io/library/someotherrepo:latest", Target: desc(16), }, }, remaining: []images.Image{ { Name: "docker.io/library/someotherrepo:latest", Target: desc(16), }, }, err: dimages.ErrImageDoesNotExist{Ref: nameDigest("repoanddigestzerocase", digestFor(16))}, }, } { tc := tc t.Run(tc.ref, func(t *testing.T) { t.Parallel() ctx := logtest.WithT(ctx, t) mdb := newTestDB(ctx, t) service := &ImageService{ images: metadata.NewImageStore(mdb), containers: emptyTestContainerStore(), eventsService: daemonevents.New(), } for _, img := range tc.starting { if _, err := service.images.Create(ctx, img); err != nil { t.Fatalf("failed to create image %q: %v", img.Name, err) } } _, err := service.ImageDelete(ctx, tc.ref, false, false) if tc.err == nil { assert.NilError(t, err) } else { assert.Error(t, err, tc.err.Error()) } all, err := service.images.List(ctx) assert.NilError(t, err) assert.Assert(t, is.Len(tc.remaining, len(all))) // Order should match for i := range all { assert.Check(t, is.Equal(all[i].Name, tc.remaining[i].Name), "image[%d]", i) assert.Check(t, is.Equal(all[i].Target.Digest, tc.remaining[i].Target.Digest), "image[%d]", i) // TODO: Check labels too } }) } } type testContainerStore struct{} func emptyTestContainerStore() container.Store { return &testContainerStore{} } func (*testContainerStore) Add(string, *container.Container) {} func (*testContainerStore) Get(string) *container.Container { return nil } func (*testContainerStore) Delete(string) {} func (*testContainerStore) List() []*container.Container { return []*container.Container{} } func (*testContainerStore) Size() int { return 0 } func (*testContainerStore) First(container.StoreFilter) *container.Container { return nil } func (*testContainerStore) ApplyAll(container.StoreReducer) {}