Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 23 additions & 25 deletions copy/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,26 +109,26 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
if err != nil {
return fmt.Errorf("Error initializing source %s: %v", transports.ImageName(srcRef), err)
}
src := image.FromSource(rawSource)
defer src.Close()

multiImage, err := src.IsMultiImage()
if err != nil {
return err
}
if multiImage {
return fmt.Errorf("can not copy %s: manifest contains multiple images", transports.ImageName(srcRef))
}
unparsedImage := image.UnparsedFromSource(rawSource)
defer func() {
if unparsedImage != nil {
unparsedImage.Close()
}
}()

// Please keep this policy check BEFORE reading any other information about the image.
if allowed, err := policyContext.IsRunningImageAllowed(src); !allowed || err != nil { // Be paranoid and fail if either return value indicates so.
if allowed, err := policyContext.IsRunningImageAllowed(unparsedImage); !allowed || err != nil { // Be paranoid and fail if either return value indicates so.
return fmt.Errorf("Source image rejected: %v", err)
}

writeReport("Getting image source manifest\n")
manifest, _, err := src.Manifest()
src, err := image.FromUnparsedImage(unparsedImage)
if err != nil {
return fmt.Errorf("Error reading manifest: %v", err)
return fmt.Errorf("Error initializing image from source %s: %v", transports.ImageName(srcRef), err)
}
unparsedImage = nil
defer src.Close()

if src.IsMultiImage() {
return fmt.Errorf("can not copy %s: manifest contains multiple images", transports.ImageName(srcRef))
}

var sigs [][]byte
Expand All @@ -150,11 +150,7 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
}
canModifyManifest := len(sigs) == 0

writeReport("Getting image source configuration\n")
srcConfigInfo, err := src.ConfigInfo()
if err != nil {
return fmt.Errorf("Error parsing manifest: %v", err)
}
srcConfigInfo := src.ConfigInfo()
if srcConfigInfo.Digest != "" {
writeReport("Uploading blob %s\n", srcConfigInfo.Digest)
destConfigInfo, err := copyBlob(dest, rawSource, srcConfigInfo, false, reportWriter)
Expand All @@ -166,10 +162,7 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
}
}

srcLayerInfos, err := src.LayerInfos()
if err != nil {
return fmt.Errorf("Error parsing manifest: %v", err)
}
srcLayerInfos := src.LayerInfos()
destLayerInfos := []types.BlobInfo{}
copiedLayers := map[string]types.BlobInfo{}
for _, srcLayer := range srcLayerInfos {
Expand All @@ -190,15 +183,20 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
manifestUpdates.LayerInfos = destLayerInfos
}

pendingImage := src
if !reflect.DeepEqual(manifestUpdates, types.ManifestUpdateOptions{}) {
if !canModifyManifest {
return fmt.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden")
}
manifest, err = src.UpdatedManifest(manifestUpdates)
pendingImage, err = src.UpdatedImage(manifestUpdates)
if err != nil {
return fmt.Errorf("Error creating an updated manifest: %v", err)
}
}
manifest, _, err := pendingImage.Manifest()
if err != nil {
return fmt.Errorf("Error reading manifest: %v", err)
}

if options != nil && options.SignBy != "" {
mech, err := signature.NewGPGSigningMechanism()
Expand Down
6 changes: 4 additions & 2 deletions directory/directory_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,13 @@ func (ref dirReference) PolicyConfigurationNamespaces() []string {
return res
}

// NewImage returns a types.Image for this reference.
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
// The caller must call .Close() on the returned Image.
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
func (ref dirReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
src := newImageSource(ref)
return image.FromSource(src), nil
return image.FromSource(src)
}

// NewImageSource returns a types.ImageSource for this reference,
Expand Down
9 changes: 9 additions & 0 deletions directory/directory_transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ func TestReferencePolicyConfigurationNamespaces(t *testing.T) {
func TestReferenceNewImage(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)

dest, err := ref.NewImageDestination(nil)
require.NoError(t, err)
defer dest.Close()
err = dest.PutManifest([]byte(`{"schemaVersion":2}`))
assert.NoError(t, err)
err = dest.Commit()
assert.NoError(t, err)

img, err := ref.NewImage(nil)
assert.NoError(t, err)
defer img.Close()
Expand Down
6 changes: 5 additions & 1 deletion docker/docker_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ func newImage(ctx *types.SystemContext, ref dockerReference) (types.Image, error
if err != nil {
return nil, err
}
return &Image{Image: image.FromSource(s), src: s}, nil
img, err := image.FromSource(s)
if err != nil {
return nil, err
}
return &Image{Image: img, src: s}, nil
}

// SourceRefFullName returns a fully expanded name for the repository this image is in.
Expand Down
4 changes: 3 additions & 1 deletion docker/docker_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,10 @@ func (ref dockerReference) PolicyConfigurationNamespaces() []string {
return policyconfiguration.DockerReferenceNamespaces(ref.ref)
}

// NewImage returns a types.Image for this reference.
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
// The caller must call .Close() on the returned Image.
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
func (ref dockerReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
return newImage(ctx, ref)
}
Expand Down
34 changes: 24 additions & 10 deletions image/docker_schema1.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,27 @@ func manifestSchema1FromManifest(manifest []byte) (genericManifest, error) {
return mschema1, nil
}

func (m *manifestSchema1) serialize() ([]byte, error) {
// docker/distribution requires a signature even if the incoming data uses the nominally unsigned DockerV2Schema1MediaType.
unsigned, err := json.Marshal(*m)
if err != nil {
return nil, err
}
return manifest.AddDummyV2S1Signature(unsigned)
}

func (m *manifestSchema1) manifestMIMEType() string {
return manifest.DockerV2Schema1SignedMediaType
}

// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
func (m *manifestSchema1) ConfigInfo() types.BlobInfo {
return types.BlobInfo{}
}

// LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers).
// The Digest field is guaranteed to be provided; Size may be -1.
// WARNING: The list may contain duplicates, and they are semantically relevant.
func (m *manifestSchema1) LayerInfos() []types.BlobInfo {
layers := make([]types.BlobInfo, len(m.FSLayers))
for i, layer := range m.FSLayers { // NOTE: This includes empty layers (where m.History.V1Compatibility->ThrowAway)
Expand All @@ -59,13 +76,13 @@ func (m *manifestSchema1) LayerInfos() []types.BlobInfo {
return layers
}

func (m *manifestSchema1) Config() ([]byte, error) {
func (m *manifestSchema1) config() ([]byte, error) {
return []byte(m.History[0].V1Compatibility), nil
}

func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) {
func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) {
v1 := &v1Image{}
config, err := m.Config()
config, err := m.config()
if err != nil {
return nil, err
}
Expand All @@ -82,7 +99,9 @@ func (m *manifestSchema1) ImageInspectInfo() (*types.ImageInspectInfo, error) {
}, nil
}

func (m *manifestSchema1) UpdatedManifest(options types.ManifestUpdateOptions) ([]byte, error) {
// UpdatedImage returns a types.Image modified according to options.
// This does not change the state of the original Image object.
func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
copy := *m
if options.LayerInfos != nil {
// Our LayerInfos includes empty layers (where m.History.V1Compatibility->ThrowAway), so expect them to be included here as well.
Expand All @@ -96,12 +115,7 @@ func (m *manifestSchema1) UpdatedManifest(options types.ManifestUpdateOptions) (
copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest
}
}
// docker/distribution requires a signature even if the incoming data uses the nominally unsigned DockerV2Schema1MediaType.
unsigned, err := json.Marshal(copy)
if err != nil {
return nil, err
}
return manifest.AddDummyV2S1Signature(unsigned)
return memoryImageFromManifest(&copy), nil
}

// fixManifestLayers, after validating the supplied manifest
Expand Down
24 changes: 19 additions & 5 deletions image/docker_schema2.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,22 @@ func manifestSchema2FromManifest(src types.ImageSource, manifest []byte) (generi
return &v2s2, nil
}

func (m *manifestSchema2) serialize() ([]byte, error) {
return json.Marshal(*m)
}

func (m *manifestSchema2) manifestMIMEType() string {
return m.MediaType
}

// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
func (m *manifestSchema2) ConfigInfo() types.BlobInfo {
return types.BlobInfo{Digest: m.ConfigDescriptor.Digest, Size: m.ConfigDescriptor.Size}
}

// LayerInfos returns a list of BlobInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers).
// The Digest field is guaranteed to be provided; Size may be -1.
// WARNING: The list may contain duplicates, and they are semantically relevant.
func (m *manifestSchema2) LayerInfos() []types.BlobInfo {
blobs := []types.BlobInfo{}
for _, layer := range m.LayersDescriptors {
Expand All @@ -42,7 +54,7 @@ func (m *manifestSchema2) LayerInfos() []types.BlobInfo {
return blobs
}

func (m *manifestSchema2) Config() ([]byte, error) {
func (m *manifestSchema2) config() ([]byte, error) {
rawConfig, _, err := m.src.GetBlob(m.ConfigDescriptor.Digest)
if err != nil {
return nil, err
Expand All @@ -52,8 +64,8 @@ func (m *manifestSchema2) Config() ([]byte, error) {
return config, err
}

func (m *manifestSchema2) ImageInspectInfo() (*types.ImageInspectInfo, error) {
config, err := m.Config()
func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) {
config, err := m.config()
if err != nil {
return nil, err
}
Expand All @@ -70,7 +82,9 @@ func (m *manifestSchema2) ImageInspectInfo() (*types.ImageInspectInfo, error) {
}, nil
}

func (m *manifestSchema2) UpdatedManifest(options types.ManifestUpdateOptions) ([]byte, error) {
// UpdatedImage returns a types.Image modified according to options.
// This does not change the state of the original Image object.
func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (types.Image, error) {
copy := *m
if options.LayerInfos != nil {
if len(copy.LayersDescriptors) != len(options.LayerInfos) {
Expand All @@ -81,5 +95,5 @@ func (m *manifestSchema2) UpdatedManifest(options types.ManifestUpdateOptions) (
copy.LayersDescriptors[i].Size = info.Size
}
}
return json.Marshal(copy)
return memoryImageFromManifest(&copy), nil
}
Loading