diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 59ed9025c2..e3a459933c 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -14,6 +14,7 @@ import ( "github.com/docker/distribution/digest" "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/registry/api/errcode" + "github.com/docker/distribution/registry/client" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/image" @@ -61,18 +62,12 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) { func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { var layersDownloaded bool if !reference.IsNameOnly(ref) { - var err error layersDownloaded, err = p.pullV2Tag(ctx, ref) if err != nil { return err } } else { - manSvc, err := p.repo.Manifests(ctx) - if err != nil { - return err - } - - tags, err := manSvc.Tags() + tags, err := p.repo.Tags(ctx).All(ctx) if err != nil { // If this repository doesn't exist on V2, we should // permit a fallback to V1. @@ -84,8 +79,6 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e // error later on. p.confirmedV2 = true - // This probably becomes a lot nicer after the manifest - // refactor... for _, tag := range tags { tagRef, err := reference.WithTag(ref, tag) if err != nil { @@ -203,58 +196,92 @@ func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) { } func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) { - tagOrDigest := "" - if tagged, isTagged := ref.(reference.NamedTagged); isTagged { - tagOrDigest = tagged.Tag() - } else if digested, isCanonical := ref.(reference.Canonical); isCanonical { - tagOrDigest = digested.Digest().String() - } else { - return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String()) - } - - logrus.Debugf("Pulling ref from V2 registry: %s:%s", ref.FullName(), tagOrDigest) - manSvc, err := p.repo.Manifests(ctx) if err != nil { return false, err } - unverifiedManifest, err := manSvc.GetByTag(tagOrDigest) - if err != nil { - // If this manifest did not exist, we should allow a possible - // fallback to the v1 protocol, because dual-version setups may - // not host all manifests with the v2 protocol. We may also get - // a "not authorized" error if the manifest doesn't exist. - return false, allowV1Fallback(err) + var ( + manifest distribution.Manifest + tagOrDigest string // Used for logging/progress only + ) + if tagged, isTagged := ref.(reference.NamedTagged); isTagged { + // NOTE: not using TagService.Get, since it uses HEAD requests + // against the manifests endpoint, which are not supported by + // all registry versions. + manifest, err = manSvc.Get(ctx, "", client.WithTag(tagged.Tag())) + if err != nil { + return false, allowV1Fallback(err) + } + tagOrDigest = tagged.Tag() + } else if digested, isDigested := ref.(reference.Canonical); isDigested { + manifest, err = manSvc.Get(ctx, digested.Digest()) + if err != nil { + return false, err + } + tagOrDigest = digested.Digest().String() + } else { + return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String()) } - if unverifiedManifest == nil { + + if manifest == nil { return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest) } - // If GetByTag succeeded, we can be confident that the registry on + // If manSvc.Get succeeded, we can be confident that the registry on // the other side speaks the v2 protocol. p.confirmedV2 = true + logrus.Debugf("Pulling ref from V2 registry: %s", ref.String()) + progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Name()) + + var imageID image.ID + + switch v := manifest.(type) { + case *schema1.SignedManifest: + imageID, err = p.pullSchema1(ctx, ref, v) + if err != nil { + return false, err + } + default: + return false, errors.New("unsupported manifest format") + } + + oldTagImageID, err := p.config.ReferenceStore.Get(ref) + if err == nil && oldTagImageID == imageID { + return false, nil + } + + if canonical, ok := ref.(reference.Canonical); ok { + if err = p.config.ReferenceStore.AddDigest(canonical, imageID, true); err != nil { + return false, err + } + } else if err = p.config.ReferenceStore.AddTag(ref, imageID, true); err != nil { + return false, err + } + + return true, nil +} + +func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Named, unverifiedManifest *schema1.SignedManifest) (imageID image.ID, err error) { var verifiedManifest *schema1.Manifest verifiedManifest, err = verifyManifest(unverifiedManifest, ref) if err != nil { - return false, err + return "", err } rootFS := image.NewRootFS() if err := detectBaseLayer(p.config.ImageStore, verifiedManifest, rootFS); err != nil { - return false, err + return "", err } // remove duplicate layers and check parent chain validity err = fixManifestLayers(verifiedManifest) if err != nil { - return false, err + return "", err } - progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Name()) - var descriptors []xfer.DownloadDescriptor // Image history converted to the new format @@ -269,12 +296,12 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat ThrowAway bool `json:"throwaway,omitempty"` } if err := json.Unmarshal([]byte(verifiedManifest.History[i].V1Compatibility), &throwAway); err != nil { - return false, err + return "", err } h, err := v1.HistoryFromConfig([]byte(verifiedManifest.History[i].V1Compatibility), throwAway.ThrowAway) if err != nil { - return false, err + return "", err } history = append(history, h) @@ -293,43 +320,30 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput) if err != nil { - return false, err + return "", err } defer release() config, err := v1.MakeConfigFromV1Config([]byte(verifiedManifest.History[0].V1Compatibility), &resultRootFS, history) if err != nil { - return false, err + return "", err } - imageID, err := p.config.ImageStore.Create(config) + imageID, err = p.config.ImageStore.Create(config) if err != nil { - return false, err + return "", err } manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo) if err != nil { - return false, err + return "", err } if manifestDigest != "" { progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String()) } - oldTagImageID, err := p.config.ReferenceStore.Get(ref) - if err == nil && oldTagImageID == imageID { - return false, nil - } - - if canonical, ok := ref.(reference.Canonical); ok { - if err = p.config.ReferenceStore.AddDigest(canonical, imageID, true); err != nil { - return false, err - } - } else if err = p.config.ReferenceStore.AddTag(ref, imageID, true); err != nil { - return false, err - } - - return true, nil + return imageID, nil } // allowV1Fallback checks if the error is a possible reason to fallback to v1 @@ -362,13 +376,7 @@ func verifyManifest(signedManifest *schema1.SignedManifest, ref reference.Named) if err != nil { return nil, err } - payload, err := signedManifest.Payload() - if err != nil { - // If this failed, the signatures section was corrupted - // or missing. Treat the entire manifest as the payload. - payload = signedManifest.Raw - } - if _, err := verifier.Write(payload); err != nil { + if _, err := verifier.Write(signedManifest.Canonical); err != nil { return nil, err } if !verifier.Verified() { @@ -376,15 +384,8 @@ func verifyManifest(signedManifest *schema1.SignedManifest, ref reference.Named) logrus.Error(err) return nil, err } - - var verifiedManifest schema1.Manifest - if err = json.Unmarshal(payload, &verifiedManifest); err != nil { - return nil, err - } - m = &verifiedManifest - } else { - m = &signedManifest.Manifest } + m = &signedManifest.Manifest if m.SchemaVersion != 1 { return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, ref.String()) diff --git a/distribution/push_v2.go b/distribution/push_v2.go index bf68c94c48..a94db2a47b 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -194,7 +194,9 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Associat if err != nil { return err } - return manSvc.Put(signed) + _, err = manSvc.Put(ctx, signed) + // FIXME create a tag + return err } type v2PushDescriptor struct { @@ -370,10 +372,7 @@ func CreateV2Manifest(name, tag string, img *image.Image, fsLayers map[layer.Dif if !present { return nil, fmt.Errorf("missing layer in CreateV2Manifest: %s", diffID.String()) } - dgst, err := digest.FromBytes([]byte(fsLayer.Hex() + " " + parent)) - if err != nil { - return nil, err - } + dgst := digest.FromBytes([]byte(fsLayer.Hex() + " " + parent)) v1ID := dgst.Hex() v1Compatibility := v1Compatibility{ @@ -414,11 +413,8 @@ func CreateV2Manifest(name, tag string, img *image.Image, fsLayers map[layer.Dif return nil, fmt.Errorf("missing layer in CreateV2Manifest: %s", diffID.String()) } - dgst, err := digest.FromBytes([]byte(fsLayer.Hex() + " " + parent + " " + string(img.RawJSON()))) - if err != nil { - return nil, err - } fsLayerList[0] = schema1.FSLayer{BlobSum: fsLayer} + dgst := digest.FromBytes([]byte(fsLayer.Hex() + " " + parent + " " + string(img.RawJSON()))) // Top-level v1compatibility string should be a modified version of the // image config. diff --git a/distribution/registry.go b/distribution/registry.go index d39a44411f..d01be73c45 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -8,7 +8,6 @@ import ( "strings" "time" - "github.com/Sirupsen/logrus" "github.com/docker/distribution" "github.com/docker/distribution/digest" "github.com/docker/distribution/manifest/schema1" @@ -126,17 +125,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end } func digestFromManifest(m *schema1.SignedManifest, name reference.Named) (digest.Digest, int, error) { - payload, err := m.Payload() - if err != nil { - // If this failed, the signatures section was corrupted - // or missing. Treat the entire manifest as the payload. - payload = m.Raw - } - manifestDigest, err := digest.FromBytes(payload) - if err != nil { - logrus.Infof("Could not compute manifest digest for %s:%s : %v", name.Name(), m.Tag, err) - } - return manifestDigest, len(payload), nil + return digest.FromBytes(m.Canonical), len(m.Canonical), nil } type existingTokenHandler struct { diff --git a/distribution/xfer/download_test.go b/distribution/xfer/download_test.go index 5d42703e63..587b2b24ab 100644 --- a/distribution/xfer/download_test.go +++ b/distribution/xfer/download_test.go @@ -65,12 +65,7 @@ func createChainIDFromParent(parent layer.ChainID, dgsts ...layer.DiffID) layer. return createChainIDFromParent(layer.ChainID(dgsts[0]), dgsts[1:]...) } // H = "H(n-1) SHA256(n)" - dgst, err := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) - if err != nil { - // Digest calculation is not expected to throw an error, - // any error at this point is a program error - panic(err) - } + dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) return createChainIDFromParent(layer.ChainID(dgst), dgsts[1:]...) } @@ -92,11 +87,7 @@ func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID) (la if err != nil { return nil, err } - diffID, err := digest.FromBytes(l.layerData.Bytes()) - if err != nil { - return nil, err - } - l.diffID = layer.DiffID(diffID) + l.diffID = layer.DiffID(digest.FromBytes(l.layerData.Bytes())) l.chainID = createChainIDFromParent(parentID, l.diffID) ls.layers[l.chainID] = l diff --git a/distribution/xfer/upload_test.go b/distribution/xfer/upload_test.go index df5b2ba9c0..70ca76c08d 100644 --- a/distribution/xfer/upload_test.go +++ b/distribution/xfer/upload_test.go @@ -62,7 +62,7 @@ func (u *mockUploadDescriptor) Upload(ctx context.Context, progressOutput progre // For the mock implementation, use SHA256(DiffID) as the returned // digest. - return digest.FromBytes([]byte(u.diffID.String())) + return digest.FromBytes([]byte(u.diffID.String())), nil } func uploadDescriptors(currentUploads *int32) []UploadDescriptor { diff --git a/image/fs.go b/image/fs.go index 7c1c4c22d6..72c9ab424b 100644 --- a/image/fs.go +++ b/image/fs.go @@ -101,11 +101,7 @@ func (s *fs) get(id ID) ([]byte, error) { } // todo: maybe optional - validated, err := digest.FromBytes(content) - if err != nil { - return nil, err - } - if ID(validated) != id { + if ID(digest.FromBytes(content)) != id { return nil, fmt.Errorf("failed to verify image: %v", id) } @@ -121,11 +117,7 @@ func (s *fs) Set(data []byte) (ID, error) { return "", fmt.Errorf("Invalid empty data") } - dgst, err := digest.FromBytes(data) - if err != nil { - return "", err - } - id := ID(dgst) + id := ID(digest.FromBytes(data)) filePath := s.contentFile(id) tempFilePath := s.contentFile(id) + ".tmp" if err := ioutil.WriteFile(tempFilePath, data, 0600); err != nil { diff --git a/image/fs_test.go b/image/fs_test.go index 35ea3c25ea..1a6f849f6b 100644 --- a/image/fs_test.go +++ b/image/fs_test.go @@ -67,10 +67,7 @@ func TestFSInvalidSet(t *testing.T) { t.Fatal(err) } - id, err := digest.FromBytes([]byte("foobar")) - if err != nil { - t.Fatal(err) - } + id := digest.FromBytes([]byte("foobar")) err = os.Mkdir(filepath.Join(tmpdir, contentDirName, string(id.Algorithm()), id.Hex()), 0700) if err != nil { t.Fatal(err) @@ -160,11 +157,7 @@ func testMetadataGetSet(t *testing.T, store StoreBackend) { t.Fatal("Expected error for getting metadata for unknown key") } - id3, err := digest.FromBytes([]byte("baz")) - if err != nil { - t.Fatal(err) - } - + id3 := digest.FromBytes([]byte("baz")) err = store.SetMetadata(ID(id3), "tkey", []byte("tval")) if err == nil { t.Fatal("Expected error for setting metadata for unknown ID.") diff --git a/image/rootfs_windows.go b/image/rootfs_windows.go index 45db04bfda..10b854978c 100644 --- a/image/rootfs_windows.go +++ b/image/rootfs_windows.go @@ -27,7 +27,7 @@ func (r *RootFS) BaseLayerID() string { // ChainID returns the ChainID for the top layer in RootFS. func (r *RootFS) ChainID() layer.ChainID { - baseDiffID, _ := digest.FromBytes([]byte(r.BaseLayerID())) // can never error + baseDiffID := digest.FromBytes([]byte(r.BaseLayerID())) return layer.CreateChainID(append([]layer.DiffID{layer.DiffID(baseDiffID)}, r.DiffIDs...)) } diff --git a/image/v1/imagev1.go b/image/v1/imagev1.go index 4a67c017b3..cdea0e7270 100644 --- a/image/v1/imagev1.go +++ b/image/v1/imagev1.go @@ -63,7 +63,7 @@ func CreateID(v1Image image.V1Image, layerID layer.ChainID, parent digest.Digest } logrus.Debugf("CreateV1ID %s", configJSON) - return digest.FromBytes(configJSON) + return digest.FromBytes(configJSON), nil } // MakeConfigFromV1Config creates an image config from the legacy V1 config format. diff --git a/layer/filestore_test.go b/layer/filestore_test.go index 4dae3f8300..7b55e4d9ec 100644 --- a/layer/filestore_test.go +++ b/layer/filestore_test.go @@ -15,12 +15,8 @@ import ( func randomLayerID(seed int64) ChainID { r := rand.New(rand.NewSource(seed)) - dgst, err := digest.FromBytes([]byte(fmt.Sprintf("%d", r.Int63()))) - if err != nil { - panic(err) - } - return ChainID(dgst) + return ChainID(digest.FromBytes([]byte(fmt.Sprintf("%d", r.Int63())))) } func newFileMetadataStore(t *testing.T) (*fileMetadataStore, string, func()) { diff --git a/layer/layer.go b/layer/layer.go index 0c6d60c575..ef2ac7adc5 100644 --- a/layer/layer.go +++ b/layer/layer.go @@ -233,12 +233,7 @@ func createChainIDFromParent(parent ChainID, dgsts ...DiffID) ChainID { return createChainIDFromParent(ChainID(dgsts[0]), dgsts[1:]...) } // H = "H(n-1) SHA256(n)" - dgst, err := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) - if err != nil { - // Digest calculation is not expected to throw an error, - // any error at this point is a program error - panic(err) - } + dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) return createChainIDFromParent(ChainID(dgst), dgsts[1:]...) } diff --git a/layer/layer_test.go b/layer/layer_test.go index 5a96b7c209..a0ecb53d22 100644 --- a/layer/layer_test.go +++ b/layer/layer_test.go @@ -548,10 +548,7 @@ func TestTarStreamStability(t *testing.T) { } func assertLayerDiff(t *testing.T, expected []byte, layer Layer) { - expectedDigest, err := digest.FromBytes(expected) - if err != nil { - t.Fatal(err) - } + expectedDigest := digest.FromBytes(expected) if digest.Digest(layer.DiffID()) != expectedDigest { t.Fatalf("Mismatched diff id for %s, got %s, expected %s", layer.ChainID(), layer.DiffID(), expected) @@ -573,10 +570,7 @@ func assertLayerDiff(t *testing.T, expected []byte, layer Layer) { t.Fatalf("Mismatched tar stream size for %s, got %d, expected %d", layer.ChainID(), len(actual), len(expected)) } - actualDigest, err := digest.FromBytes(actual) - if err != nil { - t.Fatal(err) - } + actualDigest := digest.FromBytes(actual) if actualDigest != expectedDigest { logByteDiff(t, actual, expected) diff --git a/layer/layer_windows.go b/layer/layer_windows.go index e6396fa997..e20311a091 100644 --- a/layer/layer_windows.go +++ b/layer/layer_windows.go @@ -37,10 +37,7 @@ func GetLayerPath(s Store, layer ChainID) (string, error) { func (ls *layerStore) RegisterDiffID(graphID string, size int64) (Layer, error) { var err error // this is used for cleanup in existingLayer case - diffID, err := digest.FromBytes([]byte(graphID)) - if err != nil { - return nil, err - } + diffID := digest.FromBytes([]byte(graphID)) // Create new roLayer layer := &roLayer{