github.com/rish1988/moby@v25.0.2+incompatible/daemon/containerd/image_test.go (about) 1 package containerd 2 3 import ( 4 "context" 5 "io" 6 "math/rand" 7 "path/filepath" 8 "testing" 9 10 "github.com/containerd/containerd/images" 11 "github.com/containerd/containerd/metadata" 12 "github.com/containerd/containerd/namespaces" 13 "github.com/containerd/log/logtest" 14 "github.com/distribution/reference" 15 dockerimages "github.com/docker/docker/daemon/images" 16 "github.com/opencontainers/go-digest" 17 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 18 19 "go.etcd.io/bbolt" 20 21 "gotest.tools/v3/assert" 22 is "gotest.tools/v3/assert/cmp" 23 ) 24 25 func TestLookup(t *testing.T) { 26 ctx := namespaces.WithNamespace(context.TODO(), "testing") 27 ctx = logtest.WithT(ctx, t) 28 mdb := newTestDB(ctx, t) 29 service := &ImageService{ 30 images: metadata.NewImageStore(mdb), 31 } 32 33 ubuntuLatest := images.Image{ 34 Name: "docker.io/library/ubuntu:latest", 35 Target: desc(10), 36 } 37 ubuntuLatestWithDigest := images.Image{ 38 Name: "docker.io/library/ubuntu:latest@" + digestFor(10).String(), 39 Target: desc(10), 40 } 41 ubuntuLatestWithOldDigest := images.Image{ 42 Name: "docker.io/library/ubuntu:latest@" + digestFor(11).String(), 43 Target: desc(11), 44 } 45 ambiguousShortName := images.Image{ 46 Name: "docker.io/library/abcdef:latest", 47 Target: desc(12), 48 } 49 ambiguousShortNameWithDigest := images.Image{ 50 Name: "docker.io/library/abcdef:latest@" + digestFor(12).String(), 51 Target: desc(12), 52 } 53 shortNameIsHashAlgorithm := images.Image{ 54 Name: "docker.io/library/sha256:defcab", 55 Target: desc(13), 56 } 57 58 testImages := []images.Image{ 59 ubuntuLatest, 60 ubuntuLatestWithDigest, 61 ubuntuLatestWithOldDigest, 62 ambiguousShortName, 63 ambiguousShortNameWithDigest, 64 shortNameIsHashAlgorithm, 65 { 66 Name: "docker.io/test/volatile:retried", 67 Target: desc(14), 68 }, 69 { 70 Name: "docker.io/test/volatile:inconsistent", 71 Target: desc(15), 72 }, 73 } 74 for _, img := range testImages { 75 if _, err := service.images.Create(ctx, img); err != nil { 76 t.Fatalf("failed to create image %q: %v", img.Name, err) 77 } 78 } 79 80 for _, tc := range []struct { 81 lookup string 82 img *images.Image 83 all []images.Image 84 err error 85 }{ 86 { 87 // Get ubuntu images with default "latest" tag 88 lookup: "ubuntu", 89 img: &ubuntuLatest, 90 all: []images.Image{ubuntuLatest, ubuntuLatestWithDigest}, 91 }, 92 { 93 // Get all images by image id 94 lookup: ubuntuLatest.Target.Digest.String(), 95 img: nil, 96 all: []images.Image{ubuntuLatest, ubuntuLatestWithDigest}, 97 }, 98 { 99 // Fail to lookup reference with no tag, reference has both tag and digest 100 lookup: "ubuntu@" + ubuntuLatestWithOldDigest.Target.Digest.String(), 101 img: nil, 102 all: []images.Image{ubuntuLatestWithOldDigest}, 103 }, 104 { 105 // Get all image with both tag and digest 106 lookup: "ubuntu:latest@" + ubuntuLatestWithOldDigest.Target.Digest.String(), 107 img: &ubuntuLatestWithOldDigest, 108 all: []images.Image{ubuntuLatestWithOldDigest}, 109 }, 110 { 111 // Fail to lookup reference with no tag for digest that doesn't exist 112 lookup: "ubuntu@" + digestFor(20).String(), 113 err: dockerimages.ErrImageDoesNotExist{Ref: nameDigest("ubuntu", digestFor(20))}, 114 }, 115 { 116 // Fail to lookup reference with nonexistent tag 117 lookup: "ubuntu:nonexistent", 118 err: dockerimages.ErrImageDoesNotExist{Ref: nameTag("ubuntu", "nonexistent")}, 119 }, 120 { 121 // Get abcdef image which also matches short image id 122 lookup: "abcdef", 123 img: &ambiguousShortName, 124 all: []images.Image{ambiguousShortName, ambiguousShortNameWithDigest}, 125 }, 126 { 127 // Fail to lookup image named "sha256" with tag that doesn't exist 128 lookup: "sha256:abcdef", 129 err: dockerimages.ErrImageDoesNotExist{Ref: nameTag("sha256", "abcdef")}, 130 }, 131 { 132 // Lookup with shortened image id 133 lookup: ambiguousShortName.Target.Digest.Encoded()[:8], 134 img: nil, 135 all: []images.Image{ambiguousShortName, ambiguousShortNameWithDigest}, 136 }, 137 { 138 // Lookup an actual image named "sha256" in the default namespace 139 lookup: "sha256:defcab", 140 img: &shortNameIsHashAlgorithm, 141 all: []images.Image{shortNameIsHashAlgorithm}, 142 }, 143 } { 144 tc := tc 145 t.Run(tc.lookup, func(t *testing.T) { 146 t.Parallel() 147 img, all, err := service.resolveAllReferences(ctx, tc.lookup) 148 if tc.err == nil { 149 assert.NilError(t, err) 150 } else { 151 assert.Error(t, err, tc.err.Error()) 152 } 153 if tc.img == nil { 154 assert.Assert(t, is.Nil(img)) 155 } else { 156 assert.Assert(t, img != nil) 157 assert.Check(t, is.Equal(img.Name, tc.img.Name)) 158 assert.Check(t, is.Equal(img.Target.Digest, tc.img.Target.Digest)) 159 } 160 161 assert.Assert(t, is.Len(tc.all, len(all))) 162 163 // Order should match 164 for i := range all { 165 assert.Check(t, is.Equal(all[i].Name, tc.all[i].Name), "image[%d]", i) 166 assert.Check(t, is.Equal(all[i].Target.Digest, tc.all[i].Target.Digest), "image[%d]", i) 167 } 168 }) 169 } 170 171 t.Run("fail-inconsistency", func(t *testing.T) { 172 service := &ImageService{ 173 images: &mutateOnGetImageStore{ 174 Store: service.images, 175 getMutations: []images.Image{ 176 { 177 Name: "docker.io/test/volatile:inconsistent", 178 Target: desc(18), 179 }, 180 { 181 Name: "docker.io/test/volatile:inconsistent", 182 Target: desc(19), 183 }, 184 { 185 Name: "docker.io/test/volatile:inconsistent", 186 Target: desc(20), 187 }, 188 { 189 Name: "docker.io/test/volatile:inconsistent", 190 Target: desc(21), 191 }, 192 { 193 Name: "docker.io/test/volatile:inconsistent", 194 Target: desc(22), 195 }, 196 }, 197 t: t, 198 }, 199 } 200 201 _, _, err := service.resolveAllReferences(ctx, "test/volatile:inconsistent") 202 assert.ErrorIs(t, err, errInconsistentData) 203 }) 204 205 t.Run("retry-inconsistency", func(t *testing.T) { 206 service := &ImageService{ 207 images: &mutateOnGetImageStore{ 208 Store: service.images, 209 getMutations: []images.Image{ 210 { 211 Name: "docker.io/test/volatile:retried", 212 Target: desc(16), 213 }, 214 { 215 Name: "docker.io/test/volatile:retried", 216 Target: desc(17), 217 }, 218 }, 219 t: t, 220 }, 221 } 222 223 img, all, err := service.resolveAllReferences(ctx, "test/volatile:retried") 224 assert.NilError(t, err) 225 226 assert.Assert(t, img != nil) 227 assert.Check(t, is.Equal(img.Name, "docker.io/test/volatile:retried")) 228 assert.Check(t, is.Equal(img.Target.Digest, digestFor(17))) 229 assert.Assert(t, is.Len(all, 1)) 230 assert.Check(t, is.Equal(all[0].Name, "docker.io/test/volatile:retried")) 231 assert.Check(t, is.Equal(all[0].Target.Digest, digestFor(17))) 232 }) 233 } 234 235 type mutateOnGetImageStore struct { 236 images.Store 237 getMutations []images.Image 238 t *testing.T 239 } 240 241 func (m *mutateOnGetImageStore) Get(ctx context.Context, name string) (images.Image, error) { 242 img, err := m.Store.Get(ctx, name) 243 if len(m.getMutations) > 0 { 244 m.Store.Update(ctx, m.getMutations[0]) 245 m.getMutations = m.getMutations[1:] 246 m.t.Logf("Get %s", name) 247 } 248 return img, err 249 } 250 251 func nameDigest(name string, dgst digest.Digest) reference.Reference { 252 named, _ := reference.WithName(name) 253 digested, _ := reference.WithDigest(named, dgst) 254 return digested 255 } 256 257 func nameTag(name, tag string) reference.Reference { 258 named, _ := reference.WithName(name) 259 tagged, _ := reference.WithTag(named, tag) 260 return tagged 261 } 262 263 func desc(size int64) ocispec.Descriptor { 264 return ocispec.Descriptor{ 265 Digest: digestFor(size), 266 Size: size, 267 MediaType: ocispec.MediaTypeImageIndex, 268 } 269 270 } 271 272 func digestFor(i int64) digest.Digest { 273 r := rand.New(rand.NewSource(i)) 274 dgstr := digest.SHA256.Digester() 275 _, err := io.Copy(dgstr.Hash(), io.LimitReader(r, i)) 276 if err != nil { 277 panic(err) 278 } 279 return dgstr.Digest() 280 } 281 282 func newTestDB(ctx context.Context, t *testing.T) *metadata.DB { 283 t.Helper() 284 285 p := filepath.Join(t.TempDir(), "metadata") 286 bdb, err := bbolt.Open(p, 0600, &bbolt.Options{}) 287 if err != nil { 288 t.Fatal(err) 289 } 290 t.Cleanup(func() { bdb.Close() }) 291 292 mdb := metadata.NewDB(bdb, nil, nil) 293 if err := mdb.Init(ctx); err != nil { 294 t.Fatal(err) 295 } 296 297 return mdb 298 }