123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- package containerd
- import (
- "context"
- "io"
- "math/rand"
- "path/filepath"
- "testing"
- "github.com/containerd/containerd/images"
- "github.com/containerd/containerd/metadata"
- "github.com/containerd/containerd/namespaces"
- "github.com/containerd/containerd/snapshots"
- "github.com/containerd/log/logtest"
- "github.com/distribution/reference"
- dockerimages "github.com/docker/docker/daemon/images"
- "github.com/opencontainers/go-digest"
- ocispec "github.com/opencontainers/image-spec/specs-go/v1"
- "go.etcd.io/bbolt"
- "gotest.tools/v3/assert"
- is "gotest.tools/v3/assert/cmp"
- )
- func TestLookup(t *testing.T) {
- ctx := namespaces.WithNamespace(context.TODO(), "testing")
- ctx = logtest.WithT(ctx, t)
- mdb := newTestDB(ctx, t)
- service := &ImageService{
- images: metadata.NewImageStore(mdb),
- }
- ubuntuLatest := images.Image{
- Name: "docker.io/library/ubuntu:latest",
- Target: desc(10),
- }
- ubuntuLatestWithDigest := images.Image{
- Name: "docker.io/library/ubuntu:latest@" + digestFor(10).String(),
- Target: desc(10),
- }
- ubuntuLatestWithOldDigest := images.Image{
- Name: "docker.io/library/ubuntu:latest@" + digestFor(11).String(),
- Target: desc(11),
- }
- ambiguousShortName := images.Image{
- Name: "docker.io/library/abcdef:latest",
- Target: desc(12),
- }
- ambiguousShortNameWithDigest := images.Image{
- Name: "docker.io/library/abcdef:latest@" + digestFor(12).String(),
- Target: desc(12),
- }
- shortNameIsHashAlgorithm := images.Image{
- Name: "docker.io/library/sha256:defcab",
- Target: desc(13),
- }
- testImages := []images.Image{
- ubuntuLatest,
- ubuntuLatestWithDigest,
- ubuntuLatestWithOldDigest,
- ambiguousShortName,
- ambiguousShortNameWithDigest,
- shortNameIsHashAlgorithm,
- {
- Name: "docker.io/test/volatile:retried",
- Target: desc(14),
- },
- {
- Name: "docker.io/test/volatile:inconsistent",
- Target: desc(15),
- },
- }
- for _, img := range testImages {
- if _, err := service.images.Create(ctx, img); err != nil {
- t.Fatalf("failed to create image %q: %v", img.Name, err)
- }
- }
- for _, tc := range []struct {
- lookup string
- img *images.Image
- all []images.Image
- err error
- }{
- {
- // Get ubuntu images with default "latest" tag
- lookup: "ubuntu",
- img: &ubuntuLatest,
- all: []images.Image{ubuntuLatest, ubuntuLatestWithDigest},
- },
- {
- // Get all images by image id
- lookup: ubuntuLatest.Target.Digest.String(),
- img: nil,
- all: []images.Image{ubuntuLatest, ubuntuLatestWithDigest},
- },
- {
- // Fail to lookup reference with no tag, reference has both tag and digest
- lookup: "ubuntu@" + ubuntuLatestWithOldDigest.Target.Digest.String(),
- img: nil,
- all: []images.Image{ubuntuLatestWithOldDigest},
- },
- {
- // Get all image with both tag and digest
- lookup: "ubuntu:latest@" + ubuntuLatestWithOldDigest.Target.Digest.String(),
- img: &ubuntuLatestWithOldDigest,
- all: []images.Image{ubuntuLatestWithOldDigest},
- },
- {
- // Fail to lookup reference with no tag for digest that doesn't exist
- lookup: "ubuntu@" + digestFor(20).String(),
- err: dockerimages.ErrImageDoesNotExist{Ref: nameDigest("ubuntu", digestFor(20))},
- },
- {
- // Fail to lookup reference with nonexistent tag
- lookup: "ubuntu:nonexistent",
- err: dockerimages.ErrImageDoesNotExist{Ref: nameTag("ubuntu", "nonexistent")},
- },
- {
- // Get abcdef image which also matches short image id
- lookup: "abcdef",
- img: &ambiguousShortName,
- all: []images.Image{ambiguousShortName, ambiguousShortNameWithDigest},
- },
- {
- // Fail to lookup image named "sha256" with tag that doesn't exist
- lookup: "sha256:abcdef",
- err: dockerimages.ErrImageDoesNotExist{Ref: nameTag("sha256", "abcdef")},
- },
- {
- // Lookup with shortened image id
- lookup: ambiguousShortName.Target.Digest.Encoded()[:8],
- img: nil,
- all: []images.Image{ambiguousShortName, ambiguousShortNameWithDigest},
- },
- {
- // Lookup an actual image named "sha256" in the default namespace
- lookup: "sha256:defcab",
- img: &shortNameIsHashAlgorithm,
- all: []images.Image{shortNameIsHashAlgorithm},
- },
- } {
- tc := tc
- t.Run(tc.lookup, func(t *testing.T) {
- t.Parallel()
- img, all, err := service.resolveAllReferences(ctx, tc.lookup)
- if tc.err == nil {
- assert.NilError(t, err)
- } else {
- assert.Error(t, err, tc.err.Error())
- }
- if tc.img == nil {
- assert.Assert(t, is.Nil(img))
- } else {
- assert.Assert(t, img != nil)
- assert.Check(t, is.Equal(img.Name, tc.img.Name))
- assert.Check(t, is.Equal(img.Target.Digest, tc.img.Target.Digest))
- }
- assert.Assert(t, is.Len(tc.all, len(all)))
- // Order should match
- for i := range all {
- assert.Check(t, is.Equal(all[i].Name, tc.all[i].Name), "image[%d]", i)
- assert.Check(t, is.Equal(all[i].Target.Digest, tc.all[i].Target.Digest), "image[%d]", i)
- }
- })
- }
- t.Run("fail-inconsistency", func(t *testing.T) {
- service := &ImageService{
- images: &mutateOnGetImageStore{
- Store: service.images,
- getMutations: []images.Image{
- {
- Name: "docker.io/test/volatile:inconsistent",
- Target: desc(18),
- },
- {
- Name: "docker.io/test/volatile:inconsistent",
- Target: desc(19),
- },
- {
- Name: "docker.io/test/volatile:inconsistent",
- Target: desc(20),
- },
- {
- Name: "docker.io/test/volatile:inconsistent",
- Target: desc(21),
- },
- {
- Name: "docker.io/test/volatile:inconsistent",
- Target: desc(22),
- },
- },
- t: t,
- },
- }
- _, _, err := service.resolveAllReferences(ctx, "test/volatile:inconsistent")
- assert.ErrorIs(t, err, errInconsistentData)
- })
- t.Run("retry-inconsistency", func(t *testing.T) {
- service := &ImageService{
- images: &mutateOnGetImageStore{
- Store: service.images,
- getMutations: []images.Image{
- {
- Name: "docker.io/test/volatile:retried",
- Target: desc(16),
- },
- {
- Name: "docker.io/test/volatile:retried",
- Target: desc(17),
- },
- },
- t: t,
- },
- }
- img, all, err := service.resolveAllReferences(ctx, "test/volatile:retried")
- assert.NilError(t, err)
- assert.Assert(t, img != nil)
- assert.Check(t, is.Equal(img.Name, "docker.io/test/volatile:retried"))
- assert.Check(t, is.Equal(img.Target.Digest, digestFor(17)))
- assert.Assert(t, is.Len(all, 1))
- assert.Check(t, is.Equal(all[0].Name, "docker.io/test/volatile:retried"))
- assert.Check(t, is.Equal(all[0].Target.Digest, digestFor(17)))
- })
- }
- type mutateOnGetImageStore struct {
- images.Store
- getMutations []images.Image
- t *testing.T
- }
- func (m *mutateOnGetImageStore) Get(ctx context.Context, name string) (images.Image, error) {
- img, err := m.Store.Get(ctx, name)
- if len(m.getMutations) > 0 {
- m.Store.Update(ctx, m.getMutations[0])
- m.getMutations = m.getMutations[1:]
- m.t.Logf("Get %s", name)
- }
- return img, err
- }
- func nameDigest(name string, dgst digest.Digest) reference.Reference {
- named, _ := reference.WithName(name)
- digested, _ := reference.WithDigest(named, dgst)
- return digested
- }
- func nameTag(name, tag string) reference.Reference {
- named, _ := reference.WithName(name)
- tagged, _ := reference.WithTag(named, tag)
- return tagged
- }
- func desc(size int64) ocispec.Descriptor {
- return ocispec.Descriptor{
- Digest: digestFor(size),
- Size: size,
- MediaType: ocispec.MediaTypeImageIndex,
- }
- }
- func digestFor(i int64) digest.Digest {
- r := rand.New(rand.NewSource(i))
- dgstr := digest.SHA256.Digester()
- _, err := io.Copy(dgstr.Hash(), io.LimitReader(r, i))
- if err != nil {
- panic(err)
- }
- return dgstr.Digest()
- }
- func newTestDB(ctx context.Context, t *testing.T) *metadata.DB {
- t.Helper()
- p := filepath.Join(t.TempDir(), "metadata")
- bdb, err := bbolt.Open(p, 0600, &bbolt.Options{})
- if err != nil {
- t.Fatal(err)
- }
- t.Cleanup(func() { bdb.Close() })
- mdb := metadata.NewDB(bdb, nil, nil)
- if err := mdb.Init(ctx); err != nil {
- t.Fatal(err)
- }
- return mdb
- }
- type testSnapshotterService struct {
- snapshots.Snapshotter
- }
- func (s *testSnapshotterService) Stat(ctx context.Context, key string) (snapshots.Info, error) {
- return snapshots.Info{}, nil
- }
- func (s *testSnapshotterService) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
- return snapshots.Usage{}, nil
- }
|