github.com/npaton/distribution@v2.3.1-rc.0+incompatible/manifest/schema1/config_builder_test.go (about) 1 package schema1 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "io" 7 "reflect" 8 "testing" 9 10 "github.com/docker/distribution" 11 "github.com/docker/distribution/context" 12 "github.com/docker/distribution/digest" 13 "github.com/docker/distribution/reference" 14 "github.com/docker/libtrust" 15 ) 16 17 type mockBlobService struct { 18 descriptors map[digest.Digest]distribution.Descriptor 19 } 20 21 func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { 22 if descriptor, ok := bs.descriptors[dgst]; ok { 23 return descriptor, nil 24 } 25 return distribution.Descriptor{}, distribution.ErrBlobUnknown 26 } 27 28 func (bs *mockBlobService) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) { 29 panic("not implemented") 30 } 31 32 func (bs *mockBlobService) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) { 33 panic("not implemented") 34 } 35 36 func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) { 37 d := distribution.Descriptor{ 38 Digest: digest.FromBytes(p), 39 Size: int64(len(p)), 40 MediaType: mediaType, 41 } 42 bs.descriptors[d.Digest] = d 43 return d, nil 44 } 45 46 func (bs *mockBlobService) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) { 47 panic("not implemented") 48 } 49 50 func (bs *mockBlobService) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) { 51 panic("not implemented") 52 } 53 54 func TestEmptyTar(t *testing.T) { 55 // Confirm that gzippedEmptyTar expands to 1024 NULL bytes. 56 var decompressed [2048]byte 57 gzipReader, err := gzip.NewReader(bytes.NewReader(gzippedEmptyTar)) 58 if err != nil { 59 t.Fatalf("NewReader returned error: %v", err) 60 } 61 n, err := gzipReader.Read(decompressed[:]) 62 if n != 1024 { 63 t.Fatalf("read returned %d bytes; expected 1024", n) 64 } 65 n, err = gzipReader.Read(decompressed[1024:]) 66 if n != 0 { 67 t.Fatalf("read returned %d bytes; expected 0", n) 68 } 69 if err != io.EOF { 70 t.Fatal("read did not return io.EOF") 71 } 72 gzipReader.Close() 73 for _, b := range decompressed[:1024] { 74 if b != 0 { 75 t.Fatal("nonzero byte in decompressed tar") 76 } 77 } 78 79 // Confirm that digestSHA256EmptyTar is the digest of gzippedEmptyTar. 80 dgst := digest.FromBytes(gzippedEmptyTar) 81 if dgst != digestSHA256GzippedEmptyTar { 82 t.Fatalf("digest mismatch for empty tar: expected %s got %s", digestSHA256GzippedEmptyTar, dgst) 83 } 84 } 85 86 func TestConfigBuilder(t *testing.T) { 87 imgJSON := `{ 88 "architecture": "amd64", 89 "config": { 90 "AttachStderr": false, 91 "AttachStdin": false, 92 "AttachStdout": false, 93 "Cmd": [ 94 "/bin/sh", 95 "-c", 96 "echo hi" 97 ], 98 "Domainname": "", 99 "Entrypoint": null, 100 "Env": [ 101 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 102 "derived=true", 103 "asdf=true" 104 ], 105 "Hostname": "23304fc829f9", 106 "Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246", 107 "Labels": {}, 108 "OnBuild": [], 109 "OpenStdin": false, 110 "StdinOnce": false, 111 "Tty": false, 112 "User": "", 113 "Volumes": null, 114 "WorkingDir": "" 115 }, 116 "container": "e91032eb0403a61bfe085ff5a5a48e3659e5a6deae9f4d678daa2ae399d5a001", 117 "container_config": { 118 "AttachStderr": false, 119 "AttachStdin": false, 120 "AttachStdout": false, 121 "Cmd": [ 122 "/bin/sh", 123 "-c", 124 "#(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]" 125 ], 126 "Domainname": "", 127 "Entrypoint": null, 128 "Env": [ 129 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 130 "derived=true", 131 "asdf=true" 132 ], 133 "Hostname": "23304fc829f9", 134 "Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246", 135 "Labels": {}, 136 "OnBuild": [], 137 "OpenStdin": false, 138 "StdinOnce": false, 139 "Tty": false, 140 "User": "", 141 "Volumes": null, 142 "WorkingDir": "" 143 }, 144 "created": "2015-11-04T23:06:32.365666163Z", 145 "docker_version": "1.9.0-dev", 146 "history": [ 147 { 148 "created": "2015-10-31T22:22:54.690851953Z", 149 "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /" 150 }, 151 { 152 "created": "2015-10-31T22:22:55.613815829Z", 153 "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]" 154 }, 155 { 156 "created": "2015-11-04T23:06:30.934316144Z", 157 "created_by": "/bin/sh -c #(nop) ENV derived=true", 158 "empty_layer": true 159 }, 160 { 161 "created": "2015-11-04T23:06:31.192097572Z", 162 "created_by": "/bin/sh -c #(nop) ENV asdf=true", 163 "empty_layer": true 164 }, 165 { 166 "created": "2015-11-04T23:06:32.083868454Z", 167 "created_by": "/bin/sh -c dd if=/dev/zero of=/file bs=1024 count=1024" 168 }, 169 { 170 "created": "2015-11-04T23:06:32.365666163Z", 171 "created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]", 172 "empty_layer": true 173 } 174 ], 175 "os": "linux", 176 "rootfs": { 177 "diff_ids": [ 178 "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1", 179 "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef", 180 "sha256:13f53e08df5a220ab6d13c58b2bf83a59cbdc2e04d0a3f041ddf4b0ba4112d49" 181 ], 182 "type": "layers" 183 } 184 }` 185 186 descriptors := []distribution.Descriptor{ 187 {Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, 188 {Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")}, 189 {Digest: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, 190 } 191 192 pk, err := libtrust.GenerateECP256PrivateKey() 193 if err != nil { 194 t.Fatalf("could not generate key for testing: %v", err) 195 } 196 197 bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)} 198 199 ref, err := reference.ParseNamed("testrepo:testtag") 200 if err != nil { 201 t.Fatalf("could not parse reference: %v", err) 202 } 203 204 builder := NewConfigManifestBuilder(bs, pk, ref, []byte(imgJSON)) 205 206 for _, d := range descriptors { 207 if err := builder.AppendReference(d); err != nil { 208 t.Fatalf("AppendReference returned error: %v", err) 209 } 210 } 211 212 signed, err := builder.Build(context.Background()) 213 if err != nil { 214 t.Fatalf("Build returned error: %v", err) 215 } 216 217 // Check that the gzipped empty layer tar was put in the blob store 218 _, err = bs.Stat(context.Background(), digestSHA256GzippedEmptyTar) 219 if err != nil { 220 t.Fatal("gzipped empty tar was not put in the blob store") 221 } 222 223 manifest := signed.(*SignedManifest).Manifest 224 225 if manifest.Versioned.SchemaVersion != 1 { 226 t.Fatal("SchemaVersion != 1") 227 } 228 if manifest.Name != "testrepo" { 229 t.Fatal("incorrect name in manifest") 230 } 231 if manifest.Tag != "testtag" { 232 t.Fatal("incorrect tag in manifest") 233 } 234 if manifest.Architecture != "amd64" { 235 t.Fatal("incorrect arch in manifest") 236 } 237 238 expectedFSLayers := []FSLayer{ 239 {BlobSum: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, 240 {BlobSum: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, 241 {BlobSum: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, 242 {BlobSum: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, 243 {BlobSum: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")}, 244 {BlobSum: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, 245 } 246 247 if len(manifest.FSLayers) != len(expectedFSLayers) { 248 t.Fatalf("wrong number of FSLayers: %d", len(manifest.FSLayers)) 249 } 250 if !reflect.DeepEqual(manifest.FSLayers, expectedFSLayers) { 251 t.Fatal("wrong FSLayers list") 252 } 253 254 expectedV1Compatibility := []string{ 255 `{"architecture":"amd64","config":{"AttachStderr":false,"AttachStdin":false,"AttachStdout":false,"Cmd":["/bin/sh","-c","echo hi"],"Domainname":"","Entrypoint":null,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","derived=true","asdf=true"],"Hostname":"23304fc829f9","Image":"sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246","Labels":{},"OnBuild":[],"OpenStdin":false,"StdinOnce":false,"Tty":false,"User":"","Volumes":null,"WorkingDir":""},"container":"e91032eb0403a61bfe085ff5a5a48e3659e5a6deae9f4d678daa2ae399d5a001","container_config":{"AttachStderr":false,"AttachStdin":false,"AttachStdout":false,"Cmd":["/bin/sh","-c","#(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]"],"Domainname":"","Entrypoint":null,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","derived=true","asdf=true"],"Hostname":"23304fc829f9","Image":"sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246","Labels":{},"OnBuild":[],"OpenStdin":false,"StdinOnce":false,"Tty":false,"User":"","Volumes":null,"WorkingDir":""},"created":"2015-11-04T23:06:32.365666163Z","docker_version":"1.9.0-dev","id":"0850bfdeb7b060b1004a09099846c2f023a3f2ecbf33f56b4774384b00ce0323","os":"linux","parent":"74cf9c92699240efdba1903c2748ef57105d5bedc588084c4e88f3bb1c3ef0b0","throwaway":true}`, 256 `{"id":"74cf9c92699240efdba1903c2748ef57105d5bedc588084c4e88f3bb1c3ef0b0","parent":"178be37afc7c49e951abd75525dbe0871b62ad49402f037164ee6314f754599d","created":"2015-11-04T23:06:32.083868454Z","container_config":{"Cmd":["/bin/sh -c dd if=/dev/zero of=/file bs=1024 count=1024"]}}`, 257 `{"id":"178be37afc7c49e951abd75525dbe0871b62ad49402f037164ee6314f754599d","parent":"b449305a55a283538c4574856a8b701f2a3d5ec08ef8aec47f385f20339a4866","created":"2015-11-04T23:06:31.192097572Z","container_config":{"Cmd":["/bin/sh -c #(nop) ENV asdf=true"]},"throwaway":true}`, 258 `{"id":"b449305a55a283538c4574856a8b701f2a3d5ec08ef8aec47f385f20339a4866","parent":"9e3447ca24cb96d86ebd5960cb34d1299b07e0a0e03801d90b9969a2c187dd6e","created":"2015-11-04T23:06:30.934316144Z","container_config":{"Cmd":["/bin/sh -c #(nop) ENV derived=true"]},"throwaway":true}`, 259 `{"id":"9e3447ca24cb96d86ebd5960cb34d1299b07e0a0e03801d90b9969a2c187dd6e","parent":"3690474eb5b4b26fdfbd89c6e159e8cc376ca76ef48032a30fa6aafd56337880","created":"2015-10-31T22:22:55.613815829Z","container_config":{"Cmd":["/bin/sh -c #(nop) CMD [\"sh\"]"]}}`, 260 `{"id":"3690474eb5b4b26fdfbd89c6e159e8cc376ca76ef48032a30fa6aafd56337880","created":"2015-10-31T22:22:54.690851953Z","container_config":{"Cmd":["/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"]}}`, 261 } 262 263 if len(manifest.History) != len(expectedV1Compatibility) { 264 t.Fatalf("wrong number of history entries: %d", len(manifest.History)) 265 } 266 for i := range expectedV1Compatibility { 267 if manifest.History[i].V1Compatibility != expectedV1Compatibility[i] { 268 t.Errorf("wrong V1Compatibility %d. expected:\n%s\ngot:\n%s", i, expectedV1Compatibility[i], manifest.History[i].V1Compatibility) 269 } 270 } 271 }