github.com/willmtemple/docker@v1.7.0-rc2/graph/manifest_test.go (about) 1 package graph 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "testing" 10 11 "github.com/docker/distribution/digest" 12 "github.com/docker/docker/image" 13 "github.com/docker/docker/pkg/tarsum" 14 "github.com/docker/docker/registry" 15 "github.com/docker/docker/runconfig" 16 "github.com/docker/docker/utils" 17 "github.com/docker/libtrust" 18 ) 19 20 const ( 21 testManifestImageName = "testapp" 22 testManifestImageID = "d821b739e8834ec89ac4469266c3d11515da88fdcbcbdddcbcddb636f54fdde9" 23 testManifestImageIDShort = "d821b739e883" 24 testManifestTag = "manifesttest" 25 ) 26 27 func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) { 28 manifest := ®istry.ManifestData{ 29 Name: remoteName, 30 Tag: tag, 31 SchemaVersion: 1, 32 } 33 localRepo, err := s.Get(localName) 34 if err != nil { 35 return nil, err 36 } 37 if localRepo == nil { 38 return nil, fmt.Errorf("Repo does not exist: %s", localName) 39 } 40 41 // Get the top-most layer id which the tag points to 42 layerId, exists := localRepo[tag] 43 if !exists { 44 return nil, fmt.Errorf("Tag does not exist for %s: %s", localName, tag) 45 } 46 layersSeen := make(map[string]bool) 47 48 layer, err := s.graph.Get(layerId) 49 if err != nil { 50 return nil, err 51 } 52 manifest.Architecture = layer.Architecture 53 manifest.FSLayers = make([]*registry.FSLayer, 0, 4) 54 manifest.History = make([]*registry.ManifestHistory, 0, 4) 55 var metadata runconfig.Config 56 if layer.Config != nil { 57 metadata = *layer.Config 58 } 59 60 for ; layer != nil; layer, err = layer.GetParent() { 61 if err != nil { 62 return nil, err 63 } 64 65 if layersSeen[layer.ID] { 66 break 67 } 68 if layer.Config != nil && metadata.Image != layer.ID { 69 err = runconfig.Merge(&metadata, layer.Config) 70 if err != nil { 71 return nil, err 72 } 73 } 74 75 checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID)) 76 if err != nil { 77 return nil, fmt.Errorf("Error getting image checksum: %s", err) 78 } 79 if tarsum.VersionLabelForChecksum(checksum) != tarsum.Version1.String() { 80 archive, err := layer.TarLayer() 81 if err != nil { 82 return nil, err 83 } 84 85 defer archive.Close() 86 87 tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1) 88 if err != nil { 89 return nil, err 90 } 91 if _, err := io.Copy(ioutil.Discard, tarSum); err != nil { 92 return nil, err 93 } 94 95 checksum = tarSum.Sum(nil) 96 97 // Save checksum value 98 if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), checksum); err != nil { 99 return nil, err 100 } 101 } 102 103 jsonData, err := layer.RawJson() 104 if err != nil { 105 return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err) 106 } 107 108 manifest.FSLayers = append(manifest.FSLayers, ®istry.FSLayer{BlobSum: checksum}) 109 110 layersSeen[layer.ID] = true 111 112 manifest.History = append(manifest.History, ®istry.ManifestHistory{V1Compatibility: string(jsonData)}) 113 } 114 115 manifestBytes, err := json.MarshalIndent(manifest, "", " ") 116 if err != nil { 117 return nil, err 118 } 119 120 return manifestBytes, nil 121 } 122 123 func TestManifestTarsumCache(t *testing.T) { 124 tmp, err := utils.TestDirectory("") 125 if err != nil { 126 t.Fatal(err) 127 } 128 defer os.RemoveAll(tmp) 129 store := mkTestTagStore(tmp, t) 130 defer store.graph.driver.Cleanup() 131 132 archive, err := fakeTar() 133 if err != nil { 134 t.Fatal(err) 135 } 136 img := &image.Image{ID: testManifestImageID} 137 if err := store.graph.Register(img, archive); err != nil { 138 t.Fatal(err) 139 } 140 if err := store.Tag(testManifestImageName, testManifestTag, testManifestImageID, false); err != nil { 141 t.Fatal(err) 142 } 143 144 if cs, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)); err != nil { 145 t.Fatal(err) 146 } else if cs != "" { 147 t.Fatalf("Non-empty checksum file after register") 148 } 149 150 // Generate manifest 151 payload, err := store.newManifest(testManifestImageName, testManifestImageName, testManifestTag) 152 if err != nil { 153 t.Fatal(err) 154 } 155 156 manifestChecksum, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)) 157 if err != nil { 158 t.Fatal(err) 159 } 160 161 var manifest registry.ManifestData 162 if err := json.Unmarshal(payload, &manifest); err != nil { 163 t.Fatalf("error unmarshalling manifest: %s", err) 164 } 165 166 if len(manifest.FSLayers) != 1 { 167 t.Fatalf("Unexpected number of layers, expecting 1: %d", len(manifest.FSLayers)) 168 } 169 170 if manifest.FSLayers[0].BlobSum != manifestChecksum { 171 t.Fatalf("Unexpected blob sum, expecting %q, got %q", manifestChecksum, manifest.FSLayers[0].BlobSum) 172 } 173 174 if len(manifest.History) != 1 { 175 t.Fatalf("Unexpected number of layer history, expecting 1: %d", len(manifest.History)) 176 } 177 178 v1compat, err := img.RawJson() 179 if err != nil { 180 t.Fatal(err) 181 } 182 if manifest.History[0].V1Compatibility != string(v1compat) { 183 t.Fatalf("Unexpected json value\nExpected:\n%s\nActual:\n%s", v1compat, manifest.History[0].V1Compatibility) 184 } 185 } 186 187 // TestManifestDigestCheck ensures that loadManifest properly verifies the 188 // remote and local digest. 189 func TestManifestDigestCheck(t *testing.T) { 190 tmp, err := utils.TestDirectory("") 191 if err != nil { 192 t.Fatal(err) 193 } 194 defer os.RemoveAll(tmp) 195 store := mkTestTagStore(tmp, t) 196 defer store.graph.driver.Cleanup() 197 198 archive, err := fakeTar() 199 if err != nil { 200 t.Fatal(err) 201 } 202 img := &image.Image{ID: testManifestImageID} 203 if err := store.graph.Register(img, archive); err != nil { 204 t.Fatal(err) 205 } 206 if err := store.Tag(testManifestImageName, testManifestTag, testManifestImageID, false); err != nil { 207 t.Fatal(err) 208 } 209 210 if cs, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)); err != nil { 211 t.Fatal(err) 212 } else if cs != "" { 213 t.Fatalf("Non-empty checksum file after register") 214 } 215 216 // Generate manifest 217 payload, err := store.newManifest(testManifestImageName, testManifestImageName, testManifestTag) 218 if err != nil { 219 t.Fatalf("unexpected error generating test manifest: %v", err) 220 } 221 222 pk, err := libtrust.GenerateECP256PrivateKey() 223 if err != nil { 224 t.Fatalf("unexpected error generating private key: %v", err) 225 } 226 227 sig, err := libtrust.NewJSONSignature(payload) 228 if err != nil { 229 t.Fatalf("error creating signature: %v", err) 230 } 231 232 if err := sig.Sign(pk); err != nil { 233 t.Fatalf("error signing manifest bytes: %v", err) 234 } 235 236 signedBytes, err := sig.PrettySignature("signatures") 237 if err != nil { 238 t.Fatalf("error getting signed bytes: %v", err) 239 } 240 241 dgst, err := digest.FromBytes(payload) 242 if err != nil { 243 t.Fatalf("error getting digest of manifest: %v", err) 244 } 245 246 // use this as the "bad" digest 247 zeroDigest, err := digest.FromBytes([]byte{}) 248 if err != nil { 249 t.Fatalf("error making zero digest: %v", err) 250 } 251 252 // Remote and local match, everything should look good 253 local, _, _, err := store.loadManifest(signedBytes, dgst.String(), dgst) 254 if err != nil { 255 t.Fatalf("unexpected error verifying local and remote digest: %v", err) 256 } 257 258 if local != dgst { 259 t.Fatalf("local digest not correctly calculated: %v", err) 260 } 261 262 // remote and no local, since pulling by tag 263 local, _, _, err = store.loadManifest(signedBytes, "tag", dgst) 264 if err != nil { 265 t.Fatalf("unexpected error verifying tag pull and remote digest: %v", err) 266 } 267 268 if local != dgst { 269 t.Fatalf("local digest not correctly calculated: %v", err) 270 } 271 272 // remote and differing local, this is the most important to fail 273 local, _, _, err = store.loadManifest(signedBytes, zeroDigest.String(), dgst) 274 if err == nil { 275 t.Fatalf("error expected when verifying with differing local digest") 276 } 277 278 // no remote, no local (by tag) 279 local, _, _, err = store.loadManifest(signedBytes, "tag", "") 280 if err != nil { 281 t.Fatalf("unexpected error verifying manifest without remote digest: %v", err) 282 } 283 284 if local != dgst { 285 t.Fatalf("local digest not correctly calculated: %v", err) 286 } 287 288 // no remote, with local 289 local, _, _, err = store.loadManifest(signedBytes, dgst.String(), "") 290 if err != nil { 291 t.Fatalf("unexpected error verifying manifest without remote digest: %v", err) 292 } 293 294 if local != dgst { 295 t.Fatalf("local digest not correctly calculated: %v", err) 296 } 297 298 // bad remote, we fail the check. 299 local, _, _, err = store.loadManifest(signedBytes, dgst.String(), zeroDigest) 300 if err == nil { 301 t.Fatalf("error expected when verifying with differing remote digest") 302 } 303 }