Skip to content

Commit

Permalink
Update embedded Docker reference, if any, in copy.Image
Browse files Browse the repository at this point in the history
Add EmbbeddedDockerReference to types.ManifestUpdateOptions, and use it
in copy.Image if necessary.  Pushing signed schema1 images to a different
location is impossible (skopeo users can use --remove-signatures), but
signing an unsigned schema1 image while pushing it does work.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
  • Loading branch information
mtrmac committed May 9, 2017
1 parent a851f77 commit 8282ebf
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 2 deletions.
22 changes: 22 additions & 0 deletions copy/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
manifestUpdates := types.ManifestUpdateOptions{}
manifestUpdates.InformationOnly.Destination = dest

if err := updateEmbeddedDockerReference(&manifestUpdates, dest, src, canModifyManifest); err != nil {
return err
}

// We compute preferredManifestMIMEType only to show it in error messages.
// Without having to add this context in an error message, we would be happy enough to know only that no conversion is needed.
preferredManifestMIMEType, otherManifestMIMETypeCandidates, err := determineManifestConversion(&manifestUpdates, src, destSupportedManifestMIMETypes, canModifyManifest)
Expand Down Expand Up @@ -273,6 +277,24 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
return nil
}

// updateEmbeddedDockerReference handles the Docker reference embedded in Docker schema1 manifests.
func updateEmbeddedDockerReference(manifestUpdates *types.ManifestUpdateOptions, dest types.ImageDestination, src types.Image, canModifyManifest bool) error {
destRef := dest.Reference().DockerReference()
if destRef == nil {
return nil // Destination does not care about Docker references
}
if !src.EmbeddedDockerReferenceConflicts(destRef) {
return nil // No reference embedded in the manifest, or it matches destRef already.
}

if !canModifyManifest {
return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would invalidate existing signatures. Explicitly enable signature removal to proceed anyway",
transports.ImageName(dest.Reference()), destRef.String())
}
manifestUpdates.EmbeddedDockerReference = destRef
return nil
}

// copyLayers copies layers from src/rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
func (ic *imageCopier) copyLayers() error {
srcInfos := ic.src.LayerInfos()
Expand Down
8 changes: 8 additions & 0 deletions image/docker_schema1.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (typ
copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest
}
}
if options.EmbeddedDockerReference != nil {
copy.Name = reference.Path(options.EmbeddedDockerReference)
if tagged, isTagged := options.EmbeddedDockerReference.(reference.NamedTagged); isTagged {
copy.Tag = tagged.Tag()
} else {
copy.Tag = ""
}
}

switch options.ManifestMIMEType {
case "": // No conversion, OK
Expand Down
1 change: 1 addition & 0 deletions image/docker_schema2.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (typ
copy.LayersDescriptors[i].URLs = info.URLs
}
}
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1 to schema2, but we really don't care.

switch options.ManifestMIMEType {
case "": // No conversion, OK
Expand Down
13 changes: 13 additions & 0 deletions image/docker_schema2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,19 @@ func TestManifestSchema2UpdatedImage(t *testing.T) {
})
assert.Error(t, err)

// EmbeddedDockerReference:
// … is ignored
embeddedRef, err := reference.ParseNormalizedNamed("busybox")
require.NoError(t, err)
res, err = original.UpdatedImage(types.ManifestUpdateOptions{
EmbeddedDockerReference: embeddedRef,
})
require.NoError(t, err)
nonEmbeddedRef, err := reference.ParseNormalizedNamed("notbusybox:notlatest")
require.NoError(t, err)
conflicts := res.EmbeddedDockerReferenceConflicts(nonEmbeddedRef)
assert.False(t, conflicts)

// ManifestMIMEType:
// Only smoke-test the valid conversions, detailed tests are below. (This also verifies that “original” is not affected.)
for _, mime := range []string{
Expand Down
1 change: 1 addition & 0 deletions image/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.
copy.LayersDescriptors[i].Size = info.Size
}
}
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care.

switch options.ManifestMIMEType {
case "": // No conversion, OK
Expand Down
13 changes: 13 additions & 0 deletions image/oci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,19 @@ func TestManifestOCI1UpdatedImage(t *testing.T) {
})
assert.Error(t, err)

// EmbeddedDockerReference:
// … is ignored
embeddedRef, err := reference.ParseNormalizedNamed("busybox")
require.NoError(t, err)
res, err = original.UpdatedImage(types.ManifestUpdateOptions{
EmbeddedDockerReference: embeddedRef,
})
require.NoError(t, err)
nonEmbeddedRef, err := reference.ParseNormalizedNamed("notbusybox:notlatest")
require.NoError(t, err)
conflicts := res.EmbeddedDockerReferenceConflicts(nonEmbeddedRef)
assert.False(t, conflicts)

// ManifestMIMEType:
// Only smoke-test the valid conversions, detailed tests are below. (This also verifies that “original” is not affected.)
for _, mime := range []string{
Expand Down
5 changes: 3 additions & 2 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,9 @@ type Image interface {

// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest
type ManifestUpdateOptions struct {
LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls) which should replace the originals, in order (the root layer first, and then successive layered layers)
ManifestMIMEType string
LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls) which should replace the originals, in order (the root layer first, and then successive layered layers)
EmbeddedDockerReference reference.Named
ManifestMIMEType string
// The values below are NOT requests to modify the image; they provide optional context which may or may not be used.
InformationOnly ManifestUpdateInformation
}
Expand Down

0 comments on commit 8282ebf

Please sign in to comment.