mirror of
https://github.com/containers/buildah.git
synced 2025-08-09 10:22:49 +03:00
Make shallowCopy() not use a temporary image
Modify shallowCopy() to not use a temporary image. Assume that the big data items that we formerly added to the temporary image are small enough that we can just hang on to them. Write everything to the destination reference instead of a temporary image, read it all back using the low level APIs, delete the image, and then recreate it using the new layer and the saved items and names. This lets us lift the requirement that we shallowCopy only to images with names, so that build-using-dockerfile will work without them again. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com> Closes: #150 Approved by: rhatdan
This commit is contained in:
committed by
Atomic Bot
parent
02f5235773
commit
cd6b5870e2
110
commit.go
110
commit.go
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
"github.com/containers/storage/pkg/stringid"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/projectatomic/buildah/util"
|
"github.com/projectatomic/buildah/util"
|
||||||
@@ -82,38 +81,23 @@ type PushOptions struct {
|
|||||||
// We assume that "dest" is a reference to a local image (specifically, a containers/image/storage.storageReference),
|
// We assume that "dest" is a reference to a local image (specifically, a containers/image/storage.storageReference),
|
||||||
// and will fail if it isn't.
|
// and will fail if it isn't.
|
||||||
func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReference, systemContext *types.SystemContext) error {
|
func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReference, systemContext *types.SystemContext) error {
|
||||||
|
var names []string
|
||||||
// Read the target image name.
|
// Read the target image name.
|
||||||
if dest.DockerReference() == nil {
|
if dest.DockerReference() != nil {
|
||||||
return errors.New("can't write to an unnamed image")
|
names = []string{dest.DockerReference().String()}
|
||||||
}
|
}
|
||||||
names, err := util.ExpandTags([]string{dest.DockerReference().String()})
|
// Open the source for reading and the new image for writing.
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Make a temporary image reference.
|
|
||||||
tmpName := stringid.GenerateRandomID() + "-tmp-" + Package + "-commit"
|
|
||||||
tmpRef, err := is.Transport.ParseStoreReference(b.store, tmpName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err2 := tmpRef.DeleteImage(systemContext); err2 != nil {
|
|
||||||
logrus.Debugf("error deleting temporary image %q: %v", tmpName, err2)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Open the source for reading and a temporary image for writing.
|
|
||||||
srcImage, err := src.NewImage(systemContext)
|
srcImage, err := src.NewImage(systemContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error reading configuration to write to image %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error reading configuration to write to image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
defer srcImage.Close()
|
defer srcImage.Close()
|
||||||
tmpImage, err := tmpRef.NewImageDestination(systemContext)
|
destImage, err := dest.NewImageDestination(systemContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error opening temporary copy of image %q for writing", transports.ImageName(dest))
|
return errors.Wrapf(err, "error opening image %q for writing", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
defer tmpImage.Close()
|
|
||||||
// Write an empty filesystem layer, because the image layer requires at least one.
|
// Write an empty filesystem layer, because the image layer requires at least one.
|
||||||
_, err = tmpImage.PutBlob(bytes.NewReader(gzippedEmptyLayer), types.BlobInfo{Size: int64(len(gzippedEmptyLayer))})
|
_, err = destImage.PutBlob(bytes.NewReader(gzippedEmptyLayer), types.BlobInfo{Size: int64(len(gzippedEmptyLayer))})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error writing dummy layer for image %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error writing dummy layer for image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
@@ -126,12 +110,12 @@ func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReferenc
|
|||||||
return errors.Errorf("error reading new configuration for image %q: it's empty", transports.ImageName(dest))
|
return errors.Errorf("error reading new configuration for image %q: it's empty", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
logrus.Debugf("read configuration blob %q", string(config))
|
logrus.Debugf("read configuration blob %q", string(config))
|
||||||
// Write the configuration to the temporary image.
|
// Write the configuration to the new image.
|
||||||
configBlobInfo := types.BlobInfo{
|
configBlobInfo := types.BlobInfo{
|
||||||
Digest: digest.Canonical.FromBytes(config),
|
Digest: digest.Canonical.FromBytes(config),
|
||||||
Size: int64(len(config)),
|
Size: int64(len(config)),
|
||||||
}
|
}
|
||||||
_, err = tmpImage.PutBlob(bytes.NewReader(config), configBlobInfo)
|
_, err = destImage.PutBlob(bytes.NewReader(config), configBlobInfo)
|
||||||
if err != nil && len(config) > 0 {
|
if err != nil && len(config) > 0 {
|
||||||
return errors.Wrapf(err, "error writing image configuration for temporary copy of %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error writing image configuration for temporary copy of %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
@@ -140,24 +124,42 @@ func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReferenc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error reading new manifest for image %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error reading new manifest for image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
// Write the manifest to the temporary image.
|
// Write the manifest to the new image.
|
||||||
err = tmpImage.PutManifest(manifest)
|
err = destImage.PutManifest(manifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error writing new manifest to temporary copy of image %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error writing new manifest to image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
// Save the temporary image.
|
// Save the new image.
|
||||||
err = tmpImage.Commit()
|
err = destImage.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error committing new image %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error committing new image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
// Locate the temporary image in the lower-level API. Read its item names.
|
err = destImage.Close()
|
||||||
tmpImg, err := is.Transport.GetStoreImage(b.store, tmpRef)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error locating temporary image %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error closing new image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
items, err := b.store.ListImageBigData(tmpImg.ID)
|
// Locate the new image in the lower-level API. Extract its items.
|
||||||
|
destImg, err := is.Transport.GetStoreImage(b.store, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error reading list of named data for image %q", tmpImg.ID)
|
return errors.Wrapf(err, "error locating new image %q", transports.ImageName(dest))
|
||||||
|
}
|
||||||
|
items, err := b.store.ListImageBigData(destImg.ID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading list of named data for image %q", destImg.ID)
|
||||||
|
}
|
||||||
|
bigdata := make(map[string][]byte)
|
||||||
|
for _, itemName := range items {
|
||||||
|
var data []byte
|
||||||
|
data, err = b.store.ImageBigData(destImg.ID, itemName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading named data %q for image %q", itemName, destImg.ID)
|
||||||
|
}
|
||||||
|
bigdata[itemName] = data
|
||||||
|
}
|
||||||
|
// Delete the image so that we can recreate it.
|
||||||
|
_, err = b.store.DeleteImage(destImg.ID, true)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error deleting image %q for rewriting", destImg.ID)
|
||||||
}
|
}
|
||||||
// Look up the container's read-write layer.
|
// Look up the container's read-write layer.
|
||||||
container, err := b.store.Container(b.ContainerID)
|
container, err := b.store.Container(b.ContainerID)
|
||||||
@@ -176,7 +178,7 @@ func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReferenc
|
|||||||
// Extract the read-write layer's contents.
|
// Extract the read-write layer's contents.
|
||||||
layerDiff, err := b.store.Diff(parentLayer, container.LayerID)
|
layerDiff, err := b.store.Diff(parentLayer, container.LayerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error reading layer from source image %q", transports.ImageName(src))
|
return errors.Wrapf(err, "error reading layer %q from source image %q", container.LayerID, transports.ImageName(src))
|
||||||
}
|
}
|
||||||
defer layerDiff.Close()
|
defer layerDiff.Close()
|
||||||
// Write a copy of the layer for the new image to reference.
|
// Write a copy of the layer for the new image to reference.
|
||||||
@@ -184,8 +186,8 @@ func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReferenc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating new read-only layer from container %q", b.ContainerID)
|
return errors.Wrapf(err, "error creating new read-only layer from container %q", b.ContainerID)
|
||||||
}
|
}
|
||||||
// Create a low-level image record that uses the new layer.
|
// Create a low-level image record that uses the new layer, discarding the old metadata.
|
||||||
image, err := b.store.CreateImage("", []string{}, layer.ID, "", nil)
|
image, err := b.store.CreateImage(destImg.ID, []string{}, layer.ID, "{}", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err2 := b.store.DeleteLayer(layer.ID)
|
err2 := b.store.DeleteLayer(layer.ID)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
@@ -193,7 +195,7 @@ func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReferenc
|
|||||||
}
|
}
|
||||||
return errors.Wrapf(err, "error creating new low-level image %q", transports.ImageName(dest))
|
return errors.Wrapf(err, "error creating new low-level image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
logrus.Debugf("created image ID %q", image.ID)
|
logrus.Debugf("(re-)created image ID %q using layer %q", image.ID, layer.ID)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, err2 := b.store.DeleteImage(image.ID, true)
|
_, err2 := b.store.DeleteImage(image.ID, true)
|
||||||
@@ -202,30 +204,22 @@ func (b *Builder) shallowCopy(dest types.ImageReference, src types.ImageReferenc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
// Copy the configuration and manifest, which are big data items, along with whatever else is there.
|
// Store the configuration and manifest, which are big data items, along with whatever else is there.
|
||||||
for _, item := range items {
|
for itemName, data := range bigdata {
|
||||||
var data []byte
|
err = b.store.SetImageBigData(image.ID, itemName, data)
|
||||||
data, err = b.store.ImageBigData(tmpImg.ID, item)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error copying data item %q", item)
|
return errors.Wrapf(err, "error saving data item %q", itemName)
|
||||||
}
|
}
|
||||||
err = b.store.SetImageBigData(image.ID, item, data)
|
logrus.Debugf("saved data item %q to %q", itemName, image.ID)
|
||||||
|
}
|
||||||
|
// Add the target name(s) to the new image.
|
||||||
|
if len(names) > 0 {
|
||||||
|
err = util.AddImageNames(b.store, image, names)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error copying data item %q", item)
|
return errors.Wrapf(err, "error assigning names %v to new image", names)
|
||||||
}
|
}
|
||||||
logrus.Debugf("copied data item %q to %q", item, image.ID)
|
logrus.Debugf("assigned names %v to image %q", names, image.ID)
|
||||||
}
|
}
|
||||||
// Set low-level metadata in the new image so that the image library will accept it as a real image.
|
|
||||||
err = b.store.SetMetadata(image.ID, "{}")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error assigning metadata to new image %q", transports.ImageName(dest))
|
|
||||||
}
|
|
||||||
// Move the target name(s) from the temporary image to the new image.
|
|
||||||
err = util.AddImageNames(b.store, image, names)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error assigning names %v to new image", names)
|
|
||||||
}
|
|
||||||
logrus.Debugf("assigned names %v to image %q", names, image.ID)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user