commit: commit on every instruction, but not always with layers

When building an image with multiple layers, go back to committing
images for instructions for which we previously wouldn't bother
committing an image, but create them without adding a new layer.

This violates some assumptions that we currently make elsewhere, as it's
possible for an image that's derived from a base image to add no layers
relative to the base image, when previously it was always the case that
we'd add at least one whenever we committed it.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #1539
Approved by: rhatdan
This commit is contained in:
Nalin Dahyabhai 2019-04-08 22:59:52 -04:00 committed by Atomic Bot
parent 6306386e07
commit bc53b5d980
10 changed files with 222 additions and 108 deletions

View File

@ -13,6 +13,7 @@ import (
"github.com/containers/image/types"
"github.com/containers/storage"
digest "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@ -206,65 +207,154 @@ func defaultFormat() string {
// imageIsParent goes through the layers in the store and checks if i.TopLayer is
// the parent of any other layer in store. Double check that image with that
// layer exists as well.
func imageIsParent(store storage.Store, topLayer string) (bool, error) {
children, err := getChildren(store, topLayer)
func imageIsParent(ctx context.Context, sc *types.SystemContext, store storage.Store, image *storage.Image) (bool, error) {
children, err := getChildren(ctx, sc, store, image, 1)
if err != nil {
return false, err
}
return len(children) > 0, nil
}
// getParent returns the image ID of the parent. Return nil if a parent is not found.
func getParent(store storage.Store, topLayer string) (*storage.Image, error) {
func getImageConfig(ctx context.Context, sc *types.SystemContext, store storage.Store, imageID string) (*imgspecv1.Image, error) {
ref, err := is.Transport.ParseStoreReference(store, imageID)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse reference to image %q", imageID)
}
image, err := ref.NewImage(ctx, sc)
if err != nil {
return nil, errors.Wrapf(err, "unable to open image %q", imageID)
}
config, err := image.OCIConfig(ctx)
defer image.Close()
if err != nil {
return nil, errors.Wrapf(err, "unable to read configuration from image %q", imageID)
}
return config, nil
}
func historiesDiffer(a, b []imgspecv1.History) bool {
if len(a) != len(b) {
return true
}
i := 0
for i < len(a) {
if a[i].Created == nil && b[i].Created != nil {
break
}
if a[i].Created != nil && b[i].Created == nil {
break
}
if a[i].Created != nil && b[i].Created != nil && !a[i].Created.Equal(*(b[i].Created)) {
break
}
if a[i].CreatedBy != b[i].CreatedBy {
break
}
if a[i].Author != b[i].Author {
break
}
if a[i].Comment != b[i].Comment {
break
}
if a[i].EmptyLayer != b[i].EmptyLayer {
break
}
i++
}
return i != len(a)
}
// getParent returns the image's parent image. Return nil if a parent is not found.
func getParent(ctx context.Context, sc *types.SystemContext, store storage.Store, child *storage.Image) (*storage.Image, error) {
images, err := store.Images()
if err != nil {
return nil, errors.Wrapf(err, "unable to retrieve images from store")
return nil, errors.Wrapf(err, "unable to retrieve image list from store")
}
layer, err := store.Layer(topLayer)
childLayer, err := store.Layer(child.TopLayer)
if err != nil {
return nil, errors.Wrapf(err, "unable to retrieve layers from store")
return nil, errors.Wrapf(err, "unable to retrieve layer list from store")
}
for _, img := range images {
if img.TopLayer == layer.Parent {
return &img, nil
childConfig, err := getImageConfig(ctx, sc, store, child.ID)
if err != nil {
return nil, errors.Wrapf(err, "unable to read configuration from image %q", child.ID)
}
for _, parent := range images {
if parent.ID == child.ID {
continue
}
if parent.TopLayer != childLayer.Parent && parent.TopLayer != childLayer.ID {
continue
}
parentConfig, err := getImageConfig(ctx, sc, store, parent.ID)
if err != nil {
return nil, errors.Wrapf(err, "unable to read configuration from image %q", parent.ID)
}
if len(parentConfig.History)+1 != len(childConfig.History) {
continue
}
if len(parentConfig.RootFS.DiffIDs) > 0 {
if len(childConfig.RootFS.DiffIDs) < len(parentConfig.RootFS.DiffIDs) {
continue
}
childUsesAllParentLayers := true
for i := range parentConfig.RootFS.DiffIDs {
if childConfig.RootFS.DiffIDs[i] != parentConfig.RootFS.DiffIDs[i] {
childUsesAllParentLayers = false
break
}
}
if !childUsesAllParentLayers {
continue
}
}
if historiesDiffer(parentConfig.History, childConfig.History[:len(parentConfig.History)]) {
continue
}
return &parent, nil
}
return nil, nil
}
// getChildren returns a list of the imageIDs that depend on the image
func getChildren(store storage.Store, topLayer string) ([]string, error) {
func getChildren(ctx context.Context, sc *types.SystemContext, store storage.Store, parent *storage.Image, max int) ([]string, error) {
var children []string
images, err := store.Images()
if err != nil {
return nil, errors.Wrapf(err, "unable to retrieve images from store")
}
layers, err := store.Layers()
parentConfig, err := getImageConfig(ctx, sc, store, parent.ID)
if err != nil {
return nil, errors.Wrapf(err, "unable to retrieve layers from store")
return nil, errors.Wrapf(err, "unable to read configuration from image %q", parent.ID)
}
for _, layer := range layers {
if layer.Parent == topLayer {
if imageID := getImageOfTopLayer(images, layer.ID); len(imageID) > 0 {
children = append(children, imageID...)
}
for _, child := range images {
if child.ID == parent.ID {
continue
}
childLayer, err := store.Layer(child.TopLayer)
if err != nil {
return nil, errors.Wrapf(err, "unable to retrieve information about layer %q from store", child.TopLayer)
}
if childLayer.Parent != parent.TopLayer && childLayer.ID != parent.TopLayer {
continue
}
childConfig, err := getImageConfig(ctx, sc, store, child.ID)
if err != nil {
return nil, errors.Wrapf(err, "unable to read configuration from image %q", child.ID)
}
if len(parentConfig.History)+1 != len(childConfig.History) {
continue
}
if historiesDiffer(parentConfig.History, childConfig.History[:len(parentConfig.History)]) {
continue
}
children = append(children, child.ID)
if max > 0 && len(children) >= max {
break
}
}
return children, nil
}
// getImageOfTopLayer returns the image ID where layer is the top layer of the image
func getImageOfTopLayer(images []storage.Image, layer string) []string {
var matches []string
for _, img := range images {
if img.TopLayer == layer {
matches = append(matches, img.ID)
}
}
return matches
}
func getFormat(format string) (string, error) {
switch format {
case buildah.OCI:

View File

@ -10,7 +10,9 @@ import (
buildahcli "github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/formats"
"github.com/containers/buildah/pkg/parse"
is "github.com/containers/image/storage"
"github.com/containers/image/types"
"github.com/containers/storage"
units "github.com/docker/go-units"
"github.com/pkg/errors"
@ -122,6 +124,11 @@ func imagesCmd(c *cobra.Command, args []string, iopts *imageResults) error {
return err
}
systemContext, err := parse.SystemContextFromOptions(c)
if err != nil {
return errors.Wrapf(err, "error building system context")
}
images, err := store.Images()
if err != nil {
return errors.Wrapf(err, "error reading images")
@ -150,7 +157,7 @@ func imagesCmd(c *cobra.Command, args []string, iopts *imageResults) error {
}
}
return outputImages(ctx, store, images, params, name, opts)
return outputImages(ctx, systemContext, store, images, params, name, opts)
}
func parseFilter(ctx context.Context, store storage.Store, images []storage.Image, filter string) (*filterParams, error) {
@ -237,7 +244,7 @@ func outputHeader(opts imageOptions) string {
type imagesSorted []imageOutputParams
func outputImages(ctx context.Context, store storage.Store, images []storage.Image, filters *filterParams, argName string, opts imageOptions) error {
func outputImages(ctx context.Context, systemContext *types.SystemContext, store storage.Store, images []storage.Image, filters *filterParams, argName string, opts imageOptions) error {
found := false
var imagesParams imagesSorted
jsonImages := []jsonImage{}
@ -252,11 +259,12 @@ func outputImages(ctx context.Context, store storage.Store, images []storage.Ima
}
createdTime = createdTime.Local()
// If all is false and the image doesn't have a name, check to see if the top layer of the image is a parent
// to another image's top layer. If it is, then it is an intermediate image so don't print out if the --all flag
// is not set.
// If "all" is false and this image doesn't have a name, check
// to see if the image is the parent of any other image. If it
// is, then it is an intermediate image, so don't list it if
// the --all flag is not set.
if !opts.all && len(image.Names) == 0 {
isParent, err := imageIsParent(store, image.TopLayer)
isParent, err := imageIsParent(ctx, systemContext, store, &image)
if err != nil {
logrus.Errorf("error checking if image is a parent %q: %v", image.ID, err)
}

View File

@ -106,7 +106,7 @@ func TestOutputImagesQuietNotTruncated(t *testing.T) {
// Tests quiet and non-truncated output
output, err := captureOutputWithError(func() error {
return outputImages(getContext(), store, images[:1], nil, "", opts)
return outputImages(getContext(), &testSystemContext, store, images[:1], nil, "", opts)
})
expectedOutput := fmt.Sprintf("sha256:%s\n", images[0].ID)
if err != nil {
@ -144,7 +144,7 @@ func TestOutputImagesFormatString(t *testing.T) {
// Tests output with format template
output, err := captureOutputWithError(func() error {
return outputImages(getContext(), store, images[:1], nil, "", opts)
return outputImages(getContext(), &testSystemContext, store, images[:1], nil, "", opts)
})
expectedOutput := images[0].ID
if err != nil {
@ -183,7 +183,7 @@ func TestOutputImagesArgNoMatch(t *testing.T) {
// because all images in the repository must have a tag, and here the tag is an
// empty string
_, err = captureOutputWithError(func() error {
return outputImages(getContext(), store, images[:1], nil, "foo:", opts)
return outputImages(getContext(), &testSystemContext, store, images[:1], nil, "foo:", opts)
})
if err == nil || err.Error() != "No such image foo:" {
t.Fatalf("expected error arg no match")

View File

@ -176,7 +176,7 @@ func deleteImages(ctx context.Context, systemContext *types.SystemContext, store
}
}
isParent, err := imageIsParent(store, image.TopLayer)
isParent, err := imageIsParent(ctx, systemContext, store, image)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
@ -197,7 +197,7 @@ func deleteImages(ctx context.Context, systemContext *types.SystemContext, store
lastError = errors.Errorf("unable to delete %q (cannot be forced) - image has dependent child images", image.ID)
continue
}
id, err := removeImage(store, image)
id, err := removeImage(ctx, systemContext, store, image)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
@ -256,8 +256,8 @@ func untagImage(imgArg string, store storage.Store, image *storage.Image) (strin
return removedName, nil
}
func removeImage(store storage.Store, image *storage.Image) (string, error) {
parent, err := getParent(store, image.TopLayer)
func removeImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, image *storage.Image) (string, error) {
parent, err := getParent(ctx, systemContext, store, image)
if err != nil {
return "", err
}
@ -265,17 +265,17 @@ func removeImage(store storage.Store, image *storage.Image) (string, error) {
return "", errors.Wrapf(err, "could not remove image %q", image.ID)
}
for parent != nil {
nextParent, err := getParent(store, parent.TopLayer)
nextParent, err := getParent(ctx, systemContext, store, parent)
if err != nil {
return image.ID, errors.Wrapf(err, "unable to get parent from image %q", image.ID)
}
children, err := getChildren(store, parent.TopLayer)
isParent, err := imageIsParent(ctx, systemContext, store, parent)
if err != nil {
return image.ID, errors.Wrapf(err, "unable to get children from image %q", image.ID)
return image.ID, errors.Wrapf(err, "unable to get check if image %q is a parent", image.ID)
}
// Do not remove if image is a base image and is not untagged, or if
// the image has more children.
if len(parent.Names) > 0 || len(children) > 0 {
if len(parent.Names) > 0 || isParent {
return image.ID, nil
}
id := parent.ID

View File

@ -64,6 +64,9 @@ type CommitOptions struct {
// manifest of the new image will reference the blobs rather than
// on-disk layers.
BlobDirectory string
// EmptyLayer tells the builder to omit the diff for the working
// container.
EmptyLayer bool
// OmitTimestamp forces epoch 0 as created timestamp to allow for
// deterministic, content-addressable builds.
OmitTimestamp bool

View File

@ -56,6 +56,7 @@ type containerImageRef struct {
preferredManifestType string
exporting bool
squash bool
emptyLayer bool
tarPath func(path string) (io.ReadCloser, error)
parent string
blobDirectory string
@ -290,6 +291,11 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
if err != nil {
return nil, errors.Wrapf(err, "unable to locate layer %q", layerID)
}
// If we're up to the final layer, but we don't want to include
// a diff for it, we're done.
if i.emptyLayer && layerID == i.layerID {
continue
}
// If we're not re-exporting the data, and we're reusing layers individually, reuse
// the blobsum and diff IDs.
if !i.exporting && !i.squash && layerID != i.layerID {
@ -433,7 +439,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
CreatedBy: i.createdBy,
Author: oimage.Author,
Comment: i.historyComment,
EmptyLayer: false,
EmptyLayer: i.emptyLayer,
}
oimage.History = append(oimage.History, onews)
dnews := docker.V2S2History{
@ -441,7 +447,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
CreatedBy: i.createdBy,
Author: dimage.Author,
Comment: i.historyComment,
EmptyLayer: false,
EmptyLayer: i.emptyLayer,
}
dimage.History = append(dimage.History, dnews)
appendHistory(i.postEmptyLayers)
@ -700,6 +706,7 @@ func (b *Builder) makeImageRef(options CommitOptions, exporting bool) (types.Ima
preferredManifestType: manifestType,
exporting: exporting,
squash: options.Squash,
emptyLayer: options.EmptyLayer,
tarPath: b.tarPath(),
parent: parent,
blobDirectory: options.BlobDirectory,

View File

@ -870,9 +870,9 @@ func (b *Executor) resolveNameToImageRef(output string) (types.ImageReference, e
return imageRef, nil
}
// stepRequiresCommit indicates whether or not the step should be followed by
// committing the in-progress container to create an intermediate image.
func (*StageExecutor) stepRequiresCommit(step *imagebuilder.Step) bool {
// stepRequiresLayer indicates whether or not the step should be followed by
// committing a layer container when creating an intermediate image.
func (*StageExecutor) stepRequiresLayer(step *imagebuilder.Step) bool {
switch strings.ToUpper(step.Command) {
case "ADD", "COPY", "RUN":
return true
@ -951,7 +951,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// squash the contents of the base image. Whichever is
// the case, we need to commit() to create a new image.
logCommit(s.output, -1)
if imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(nil), s.output); err != nil {
if imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(nil), false, s.output); err != nil {
return "", nil, errors.Wrapf(err, "error committing base container")
}
} else {
@ -1032,7 +1032,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// if it's used as the basis for a later stage.
if lastStage || imageIsUsedLater {
logCommit(s.output, i)
imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), s.output)
imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), false, s.output)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
}
@ -1061,11 +1061,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// If we're using the cache, and we've managed to stick with
// cached images so far, look for one that matches what we
// expect to produce for this instruction.
// Only check at steps where we commit, so that we don't
// abandon the cache at this step just because we can't find an
// image with a history entry in it that we wouldn't have
// committed.
if checkForLayers && (s.stepRequiresCommit(step) || lastInstruction) && !(s.executor.squash && lastInstruction && lastStage) {
if checkForLayers && !(s.executor.squash && lastInstruction && lastStage) {
cacheID, err = s.layerExists(ctx, node, children[:i])
if err != nil {
return "", nil, errors.Wrap(err, "error checking if cached image exists from a previous build")
@ -1085,8 +1081,8 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// the last step in this stage, add the name to the
// image.
imgID = cacheID
if commitName != "" && (s.stepRequiresCommit(step) || lastInstruction) {
logCommit(s.output, i)
if commitName != "" {
logCommit(commitName, i)
if imgID, ref, err = s.copyExistingImage(ctx, cacheID, commitName); err != nil {
return "", nil, err
}
@ -1098,6 +1094,19 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
// filesystem to match the image contents for the sake
// of a later stage that wants to copy content from it.
rebase = moreInstructions || rootfsIsUsedLater
// If the instruction would affect our configuration,
// process the configuration change so that, if we fall
// off the cache path, the filesystem changes from the
// last cache image will be all that we need, since we
// still don't want to restart using the image's
// configuration blob.
if !s.stepRequiresLayer(step) {
err := ib.Run(step, s, noRunsRemaining)
if err != nil {
logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step))
return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message)
}
}
} else {
// If we didn't find a cached image that we could just reuse,
// process the instruction directly.
@ -1106,32 +1115,20 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, b
logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step))
return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message)
}
if s.stepRequiresCommit(step) || lastInstruction {
// Either this is the last instruction, or
// there are more instructions and we need to
// create a layer from this one before
// continuing.
logCommit(s.output, i)
imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), commitName)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
}
logImageID(imgID)
// We only need to build a new container rootfs
// using this image if we plan on making
// further changes to it. Subsequent stages
// that just want to use the rootfs as a source
// for COPY or ADD will be content with what we
// already have.
rebase = moreInstructions
} else {
// There are still more instructions to process
// for this stage, and we don't need to commit
// here. Make a note of the instruction in the
// history for the next commit.
now := time.Now()
s.builder.AddPrependedEmptyLayer(&now, s.executor.getCreatedBy(node), "", "")
// Create a new image, maybe with a new layer.
logCommit(s.output, i)
imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), !s.stepRequiresLayer(step), commitName)
if err != nil {
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
}
logImageID(imgID)
// We only need to build a new container rootfs
// using this image if we plan on making
// further changes to it. Subsequent stages
// that just want to use the rootfs as a source
// for COPY or ADD will be content with what we
// already have.
rebase = moreInstructions
}
if rebase {
@ -1217,7 +1214,7 @@ func (s *StageExecutor) layerExists(ctx context.Context, currNode *parser.Node,
// it means that this image is potentially a cached intermediate image from a previous
// build. Next we double check that the history of this image is equivalent to the previous
// lines in the Dockerfile up till the point we are at in the build.
if layer.Parent == s.executor.topLayers[len(s.executor.topLayers)-1] {
if layer.Parent == s.executor.topLayers[len(s.executor.topLayers)-1] || layer.ID == s.executor.topLayers[len(s.executor.topLayers)-1] {
history, err := s.executor.getImageHistory(ctx, image.ID)
if err != nil {
return "", errors.Wrapf(err, "error getting history of %q", image.ID)
@ -1398,7 +1395,7 @@ func urlContentModified(url string, historyTime *time.Time) (bool, error) {
// commit writes the container's contents to an image, using a passed-in tag as
// the name if there is one, generating a unique ID-based one otherwise.
func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, createdBy, output string) (string, reference.Canonical, error) {
func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, createdBy string, emptyLayer bool, output string) (string, reference.Canonical, error) {
var imageRef types.ImageReference
if output != "" {
imageRef2, err := s.executor.resolveNameToImageRef(output)
@ -1488,6 +1485,7 @@ func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, cr
PreferredManifestType: s.executor.outputFormat,
SystemContext: s.executor.systemContext,
Squash: s.executor.squash,
EmptyLayer: emptyLayer,
BlobDirectory: s.executor.blobDirectory,
}
imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options)

View File

@ -50,36 +50,40 @@ load helpers
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test1 ${TESTDIR}/use-layers
run_buildah --debug=false images -a
expect_line_count 6
expect_line_count 8
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test2 ${TESTDIR}/use-layers
run_buildah --debug=false images -a
expect_line_count 8
run_buildah --debug=false inspect --format "{{index .Docker.ContainerConfig.Env 1}}" test2
expect_line_count 10
run_buildah --debug=false inspect --format "{{index .Docker.ContainerConfig.Env 1}}" test1
expect_output "foo=bar"
run_buildah --debug=false inspect --format "{{index .Docker.ContainerConfig.Env 1}}" test2
expect_output "foo=bar"
run_buildah --debug=false inspect --format "{{.Docker.ContainerConfig.ExposedPorts}}" test1
expect_output "map[8080/tcp:{}]"
run_buildah --debug=false inspect --format "{{.Docker.ContainerConfig.ExposedPorts}}" test2
expect_output "map[8080/tcp:{}]"
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test3 -f Dockerfile.2 ${TESTDIR}/use-layers
run_buildah --debug=false images -a
expect_line_count 10
expect_line_count 12
mkdir -p ${TESTDIR}/use-layers/mount/subdir
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test4 -f Dockerfile.3 ${TESTDIR}/use-layers
run_buildah --debug=false images -a
expect_line_count 12
expect_line_count 14
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test5 -f Dockerfile.3 ${TESTDIR}/use-layers
run_buildah --debug=false images -a
expect_line_count 13
expect_line_count 15
touch ${TESTDIR}/use-layers/mount/subdir/file.txt
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test6 -f Dockerfile.3 ${TESTDIR}/use-layers
run_buildah --debug=false images -a
expect_line_count 15
expect_line_count 17
buildah bud --signature-policy ${TESTSDIR}/policy.json --no-cache -t test7 -f Dockerfile.2 ${TESTDIR}/use-layers
run_buildah --debug=false images -a
expect_line_count 16
expect_line_count 18
buildah rmi -a -f
}
@ -186,21 +190,25 @@ load helpers
}
@test "bud with --layers and --build-args" {
# base plus 3, plus the header line
buildah bud --signature-policy ${TESTSDIR}/policy.json --build-arg=user=0 --layers -t test -f Dockerfile.build-args ${TESTSDIR}/bud/use-layers
run_buildah --debug=false images -a
expect_line_count 4
expect_line_count 5
# two more, starting at the "echo $user" instruction
buildah bud --signature-policy ${TESTSDIR}/policy.json --build-arg=user=1 --layers -t test1 -f Dockerfile.build-args ${TESTSDIR}/bud/use-layers
run_buildah --debug=false images -a
expect_line_count 6
buildah bud --signature-policy ${TESTSDIR}/policy.json --build-arg=user=1 --layers -t test2 -f Dockerfile.build-args ${TESTSDIR}/bud/use-layers
run_buildah --debug=false images -a
expect_line_count 7
# one more, because we added a new name to the same image
buildah bud --signature-policy ${TESTSDIR}/policy.json --build-arg=user=1 --layers -t test2 -f Dockerfile.build-args ${TESTSDIR}/bud/use-layers
run_buildah --debug=false images -a
expect_line_count 8
# two more, starting at the "echo $user" instruction
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test3 -f Dockerfile.build-args ${TESTSDIR}/bud/use-layers
run_buildah --debug=false images -a
expect_line_count 9
expect_line_count 10
buildah rmi -a -f
}
@ -212,7 +220,7 @@ load helpers
buildah bud --signature-policy ${TESTSDIR}/policy.json --rm=false --layers -t test2 ${TESTSDIR}/bud/use-layers
run_buildah --debug=false containers
expect_line_count 5
expect_line_count 7
buildah rm -a
buildah rmi -a -f
@ -1086,7 +1094,7 @@ load helpers
@test "bud with copy-from in Dockerfile no prior FROM" {
target=php-image
run_buildah --debug=false bud --signature-policy ${TESTSDIR}/policy.json -t ${target} -f ${TESTSDIR}/bud/copy-from ${TESTSDIR}/bud/copy-from
run_buildah bud --signature-policy ${TESTSDIR}/policy.json -t ${target} -f ${TESTSDIR}/bud/copy-from ${TESTSDIR}/bud/copy-from
ctr=$(buildah --debug=false from --signature-policy ${TESTSDIR}/policy.json ${target})
mnt=$(buildah --debug=false mount ${ctr})

View File

@ -30,7 +30,7 @@ load helpers
expect_line_count 3
run_buildah --debug=false images -a
expect_line_count 6
expect_line_count 8
# create a no name image which should show up when doing buildah images without the --all flag
buildah bud --signature-policy ${TESTSDIR}/policy.json ${TESTSDIR}/bud/use-layers

View File

@ -135,13 +135,13 @@ load helpers
buildah rmi -a -f
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test1 ${TESTSDIR}/bud/use-layers
run_buildah --debug=false images -a -q
expect_line_count 5
expect_line_count 7
buildah bud --signature-policy ${TESTSDIR}/policy.json --layers -t test2 -f Dockerfile.2 ${TESTSDIR}/bud/use-layers
run_buildah --debug=false images -a -q
expect_line_count 7
expect_line_count 9
run_buildah --debug=false rmi test2
run_buildah --debug=false images -a -q
expect_line_count 5
expect_line_count 7
run_buildah --debug=false rmi test1
run_buildah --debug=false images -a -q
expect_line_count 1