moby/daemon/containerd/image_delete_test.go
Derek McGowan 0c6e9121b2
Add support for removing repo and digest
When repo and digest is provided, remove all references within a
repository for the given digest.

Signed-off-by: Derek McGowan <derek@mcg.dev>
2023-12-18 16:33:53 -08:00

286 lines
6.4 KiB
Go

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) {}