1package schema2 2 3import ( 4 "context" 5 "reflect" 6 "testing" 7 8 "github.com/docker/distribution" 9 "github.com/opencontainers/go-digest" 10) 11 12type mockBlobService struct { 13 descriptors map[digest.Digest]distribution.Descriptor 14} 15 16func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { 17 if descriptor, ok := bs.descriptors[dgst]; ok { 18 return descriptor, nil 19 } 20 return distribution.Descriptor{}, distribution.ErrBlobUnknown 21} 22 23func (bs *mockBlobService) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) { 24 panic("not implemented") 25} 26 27func (bs *mockBlobService) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) { 28 panic("not implemented") 29} 30 31func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) { 32 d := distribution.Descriptor{ 33 Digest: digest.FromBytes(p), 34 Size: int64(len(p)), 35 MediaType: "application/octet-stream", 36 } 37 bs.descriptors[d.Digest] = d 38 return d, nil 39} 40 41func (bs *mockBlobService) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) { 42 panic("not implemented") 43} 44 45func (bs *mockBlobService) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) { 46 panic("not implemented") 47} 48 49func TestBuilder(t *testing.T) { 50 imgJSON := []byte(`{ 51 "architecture": "amd64", 52 "config": { 53 "AttachStderr": false, 54 "AttachStdin": false, 55 "AttachStdout": false, 56 "Cmd": [ 57 "/bin/sh", 58 "-c", 59 "echo hi" 60 ], 61 "Domainname": "", 62 "Entrypoint": null, 63 "Env": [ 64 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 65 "derived=true", 66 "asdf=true" 67 ], 68 "Hostname": "23304fc829f9", 69 "Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246", 70 "Labels": {}, 71 "OnBuild": [], 72 "OpenStdin": false, 73 "StdinOnce": false, 74 "Tty": false, 75 "User": "", 76 "Volumes": null, 77 "WorkingDir": "" 78 }, 79 "container": "e91032eb0403a61bfe085ff5a5a48e3659e5a6deae9f4d678daa2ae399d5a001", 80 "container_config": { 81 "AttachStderr": false, 82 "AttachStdin": false, 83 "AttachStdout": false, 84 "Cmd": [ 85 "/bin/sh", 86 "-c", 87 "#(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]" 88 ], 89 "Domainname": "", 90 "Entrypoint": null, 91 "Env": [ 92 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 93 "derived=true", 94 "asdf=true" 95 ], 96 "Hostname": "23304fc829f9", 97 "Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246", 98 "Labels": {}, 99 "OnBuild": [], 100 "OpenStdin": false, 101 "StdinOnce": false, 102 "Tty": false, 103 "User": "", 104 "Volumes": null, 105 "WorkingDir": "" 106 }, 107 "created": "2015-11-04T23:06:32.365666163Z", 108 "docker_version": "1.9.0-dev", 109 "history": [ 110 { 111 "created": "2015-10-31T22:22:54.690851953Z", 112 "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /" 113 }, 114 { 115 "created": "2015-10-31T22:22:55.613815829Z", 116 "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]" 117 }, 118 { 119 "created": "2015-11-04T23:06:30.934316144Z", 120 "created_by": "/bin/sh -c #(nop) ENV derived=true", 121 "empty_layer": true 122 }, 123 { 124 "created": "2015-11-04T23:06:31.192097572Z", 125 "created_by": "/bin/sh -c #(nop) ENV asdf=true", 126 "empty_layer": true 127 }, 128 { 129 "created": "2015-11-04T23:06:32.083868454Z", 130 "created_by": "/bin/sh -c dd if=/dev/zero of=/file bs=1024 count=1024" 131 }, 132 { 133 "created": "2015-11-04T23:06:32.365666163Z", 134 "created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]", 135 "empty_layer": true 136 } 137 ], 138 "os": "linux", 139 "rootfs": { 140 "diff_ids": [ 141 "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1", 142 "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef", 143 "sha256:13f53e08df5a220ab6d13c58b2bf83a59cbdc2e04d0a3f041ddf4b0ba4112d49" 144 ], 145 "type": "layers" 146 } 147}`) 148 configDigest := digest.FromBytes(imgJSON) 149 150 descriptors := []distribution.Descriptor{ 151 { 152 Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"), 153 Size: 5312, 154 MediaType: MediaTypeLayer, 155 }, 156 { 157 Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"), 158 Size: 235231, 159 MediaType: MediaTypeLayer, 160 }, 161 { 162 Digest: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"), 163 Size: 639152, 164 MediaType: MediaTypeLayer, 165 }, 166 } 167 168 bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)} 169 builder := NewManifestBuilder(bs, MediaTypeImageConfig, imgJSON) 170 171 for _, d := range descriptors { 172 if err := builder.AppendReference(d); err != nil { 173 t.Fatalf("AppendReference returned error: %v", err) 174 } 175 } 176 177 built, err := builder.Build(context.Background()) 178 if err != nil { 179 t.Fatalf("Build returned error: %v", err) 180 } 181 182 // Check that the config was put in the blob store 183 _, err = bs.Stat(context.Background(), configDigest) 184 if err != nil { 185 t.Fatal("config was not put in the blob store") 186 } 187 188 manifest := built.(*DeserializedManifest).Manifest 189 190 if manifest.Versioned.SchemaVersion != 2 { 191 t.Fatal("SchemaVersion != 2") 192 } 193 194 target := manifest.Target() 195 if target.Digest != configDigest { 196 t.Fatalf("unexpected digest in target: %s", target.Digest.String()) 197 } 198 if target.MediaType != MediaTypeImageConfig { 199 t.Fatalf("unexpected media type in target: %s", target.MediaType) 200 } 201 if target.Size != 3153 { 202 t.Fatalf("unexpected size in target: %d", target.Size) 203 } 204 205 references := manifest.References() 206 expected := append([]distribution.Descriptor{manifest.Target()}, descriptors...) 207 if !reflect.DeepEqual(references, expected) { 208 t.Fatal("References() does not match the descriptors added") 209 } 210} 211