summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/build.go42
-rw-r--r--cmd/podman/cliconfig/create.go9
-rw-r--r--cmd/podman/images.go16
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/podman-build.1.md5
-rw-r--r--libpod/container_internal.go2
-rw-r--r--libpod/oci_conmon_linux.go2
-rw-r--r--libpod/runtime_cstorage.go10
-rw-r--r--libpod/runtime_img.go2
-rw-r--r--test/e2e/images_test.go7
10 files changed, 77 insertions, 19 deletions
diff --git a/cmd/podman/build.go b/cmd/podman/build.go
index 4ea4d3825..f4efea544 100644
--- a/cmd/podman/build.go
+++ b/cmd/podman/build.go
@@ -17,6 +17,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -27,6 +28,7 @@ var (
fromAndBudValues buildahcli.FromAndBudResults
userNSValues buildahcli.UserNSResults
namespaceValues buildahcli.NameSpaceResults
+ podBuildValues cliconfig.PodmanBuildResults
_buildCommand = &cobra.Command{
Use: "build [flags] CONTEXT",
@@ -40,6 +42,7 @@ var (
buildCommand.FromAndBudResults = &fromAndBudValues
buildCommand.LayerResults = &layerValues
buildCommand.NameSpaceResults = &namespaceValues
+ buildCommand.PodmanBuildResults = &podBuildValues
buildCommand.Remote = remoteclient
return buildCmd(&buildCommand)
},
@@ -73,15 +76,30 @@ func init() {
logrus.Error("unable to set force-rm flag to true")
}
flag.DefValue = "true"
+ podmanBuildFlags := GetPodmanBuildFlags(&podBuildValues)
+ flag = podmanBuildFlags.Lookup("squash-all")
+ if err := flag.Value.Set("false"); err != nil {
+ logrus.Error("unable to set squash-all flag to false")
+ }
+ flag.DefValue = "true"
fromAndBugFlags := buildahcli.GetFromAndBudFlags(&fromAndBudValues, &userNSValues, &namespaceValues)
flags.AddFlagSet(&budFlags)
- flags.AddFlagSet(&layerFlags)
flags.AddFlagSet(&fromAndBugFlags)
+ flags.AddFlagSet(&layerFlags)
+ flags.AddFlagSet(&podmanBuildFlags)
markFlagHidden(flags, "signature-policy")
}
+// GetPodmanBuildFlags flags used only by `podman build` and not by
+// `buildah bud`.
+func GetPodmanBuildFlags(flags *cliconfig.PodmanBuildResults) pflag.FlagSet {
+ fs := pflag.FlagSet{}
+ fs.BoolVar(&flags.SquashAll, "squash-all", false, "Squash all layers into a single layer.")
+ return fs
+}
+
func getContainerfiles(files []string) []string {
var containerfiles []string
for _, f := range files {
@@ -119,6 +137,12 @@ func getNsValues(c *cliconfig.BuildValues) ([]buildah.NamespaceOption, error) {
}
func buildCmd(c *cliconfig.BuildValues) error {
+ if (c.Flags().Changed("squash") && c.Flags().Changed("layers")) ||
+ (c.Flags().Changed("squash-all") && c.Flags().Changed("layers")) ||
+ (c.Flags().Changed("squash-all") && c.Flags().Changed("squash")) {
+ return fmt.Errorf("cannot specify squash, squash-all and layers options together")
+ }
+
// The following was taken directly from containers/buildah/cmd/bud.go
// TODO Find a away to vendor more of this in rather than copy from bud
output := ""
@@ -293,6 +317,22 @@ func buildCmd(c *cliconfig.BuildValues) error {
Volumes: c.Volumes,
}
+ // `buildah bud --layers=false` acts like `docker build --squash` does.
+ // That is all of the new layers created during the build process are
+ // condensed into one, any layers present prior to this build are retained
+ // without condensing. `buildah bud --squash` squashes both new and old
+ // layers down into one. Translate Podman commands into Buildah.
+ // Squash invoked, retain old layers, squash new layers into one.
+ if c.Flags().Changed("squash") && c.Squash {
+ c.Squash = false
+ layers = false
+ }
+ // Squash-all invoked, squash both new and old layers into one.
+ if c.Flags().Changed("squash-all") {
+ c.Squash = true
+ layers = false
+ }
+
options := imagebuildah.BuildOptions{
CommonBuildOpts: &buildOpts,
AdditionalTags: tags,
diff --git a/cmd/podman/cliconfig/create.go b/cmd/podman/cliconfig/create.go
index 5fb2eed10..c27dfbbee 100644
--- a/cmd/podman/cliconfig/create.go
+++ b/cmd/podman/cliconfig/create.go
@@ -12,13 +12,20 @@ type RunValues struct {
PodmanCommand
}
+// PodmanBuildResults represents the results for Podman Build flags
+// that are unique to Podman.
+type PodmanBuildResults struct {
+ SquashAll bool
+}
+
type BuildValues struct {
PodmanCommand
*buildahcli.BudResults
*buildahcli.UserNSResults
*buildahcli.FromAndBudResults
- *buildahcli.NameSpaceResults
*buildahcli.LayerResults
+ *buildahcli.NameSpaceResults
+ *PodmanBuildResults
}
type CpValues struct {
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index fe7c89b5c..e363fa3bb 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -216,17 +216,18 @@ func (i imagesOptions) setOutputFormat() string {
}
// imagesToGeneric creates an empty array of interfaces for output
-func imagesToGeneric(templParams []imagesTemplateParams, JSONParams []imagesJSONParams) (genericParams []interface{}) {
+func imagesToGeneric(templParams []imagesTemplateParams, JSONParams []imagesJSONParams) []interface{} {
+ genericParams := []interface{}{}
if len(templParams) > 0 {
for _, v := range templParams {
genericParams = append(genericParams, interface{}(v))
}
- return
+ return genericParams
}
for _, v := range JSONParams {
genericParams = append(genericParams, interface{}(v))
}
- return
+ return genericParams
}
func sortImagesOutput(sortBy string, imagesOutput imagesSorted) imagesSorted {
@@ -309,7 +310,8 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma
}
// getImagesJSONOutput returns the images information in its raw form
-func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) (imagesOutput []imagesJSONParams) {
+func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) []imagesJSONParams {
+ imagesOutput := []imagesJSONParams{}
for _, img := range images {
size, err := img.Size(ctx)
if err != nil {
@@ -325,7 +327,7 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage)
}
imagesOutput = append(imagesOutput, params)
}
- return
+ return imagesOutput
}
// generateImagesOutput generates the images based on the format provided
@@ -336,10 +338,6 @@ func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage,
switch opts.format {
case formats.JSONString:
- // If 0 images are present, print nothing for JSON
- if len(images) == 0 {
- return nil
- }
imagesOutput := getImagesJSONOutput(ctx, images)
out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)}
default:
diff --git a/completions/bash/podman b/completions/bash/podman
index 4bc387871..2a55183bd 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1246,6 +1246,7 @@ _podman_build() {
-q
--rm
--squash
+ --squash-all
--tls-verify
"
diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md
index c16d964a9..567d0ead3 100644
--- a/docs/podman-build.1.md
+++ b/docs/podman-build.1.md
@@ -405,6 +405,11 @@ If you omit the unit, the system uses bytes. If you omit the size entirely, the
**--squash**
+Squash all of the image's new layers into a single new layer; any preexisting layers
+are not squashed.
+
+**--squash-all**
+
Squash all of the new image's layers (including those inherited from a base image) into a single new layer.
**--tag**, **-t**=*imageName*
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index e7f541c52..a7ac23f73 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -533,7 +533,7 @@ func (c *Container) teardownStorage() error {
// error - we wanted it gone, it is already gone.
// Potentially another tool using containers/storage already
// removed it?
- if err == storage.ErrNotAContainer || err == storage.ErrContainerUnknown {
+ if errors.Cause(err) == storage.ErrNotAContainer || errors.Cause(err) == storage.ErrContainerUnknown {
logrus.Warnf("Storage for container %s already removed", c.ID())
return nil
}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index f29758a69..3606a9634 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -1359,7 +1359,7 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) {
}
}
}
- return -1, errors.Wrapf(ss.err, "error reading container (probably exited) json message")
+ return -1, errors.Wrapf(ss.err, "container create failed (no logs from conmon)")
}
logrus.Debugf("Received: %d", ss.si.Data)
if ss.si.Data < 0 {
diff --git a/libpod/runtime_cstorage.go b/libpod/runtime_cstorage.go
index 47a91c881..2d523a7d2 100644
--- a/libpod/runtime_cstorage.go
+++ b/libpod/runtime_cstorage.go
@@ -68,7 +68,7 @@ func (r *Runtime) RemoveStorageContainer(idOrName string, force bool) error {
func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
targetID, err := r.store.Lookup(idOrName)
if err != nil {
- if err == storage.ErrLayerUnknown {
+ if errors.Cause(err) == storage.ErrLayerUnknown {
return errors.Wrapf(define.ErrNoSuchCtr, "no container with ID or name %q found", idOrName)
}
return errors.Wrapf(err, "error looking up container %q", idOrName)
@@ -78,7 +78,7 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
// So we can still error here.
ctr, err := r.store.Container(targetID)
if err != nil {
- if err == storage.ErrContainerUnknown {
+ if errors.Cause(err) == storage.ErrContainerUnknown {
return errors.Wrapf(define.ErrNoSuchCtr, "%q does not refer to a container", idOrName)
}
return errors.Wrapf(err, "error retrieving container %q", idOrName)
@@ -96,7 +96,7 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
if !force {
timesMounted, err := r.store.Mounted(ctr.ID)
if err != nil {
- if err == storage.ErrContainerUnknown {
+ if errors.Cause(err) == storage.ErrContainerUnknown {
// Container was removed from under us.
// It's gone, so don't bother erroring.
logrus.Warnf("Storage for container %s already removed", ctr.ID)
@@ -109,7 +109,7 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
}
} else {
if _, err := r.store.Unmount(ctr.ID, true); err != nil {
- if err == storage.ErrContainerUnknown {
+ if errors.Cause(err) == storage.ErrContainerUnknown {
// Container again gone, no error
logrus.Warnf("Storage for container %s already removed", ctr.ID)
return nil
@@ -119,7 +119,7 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
}
if err := r.store.DeleteContainer(ctr.ID); err != nil {
- if err == storage.ErrContainerUnknown {
+ if errors.Cause(err) == storage.ErrContainerUnknown {
// Container again gone, no error
logrus.Warnf("Storage for container %s already removed", ctr.ID)
return nil
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 8cc501629..35c0cdfb9 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -69,7 +69,7 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
// the image. we figure out which repotag the user is trying to refer
// to and untag it.
repoName, err := img.MatchRepoTag(img.InputName)
- if hasChildren && err == image.ErrRepoTagNotFound {
+ if hasChildren && errors.Cause(err) == image.ErrRepoTagNotFound {
return "", errors.Errorf("unable to delete %q (cannot be forced) - image has dependent child images", img.ID())
}
if err != nil {
diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go
index 8203e4273..e125c62b4 100644
--- a/test/e2e/images_test.go
+++ b/test/e2e/images_test.go
@@ -101,6 +101,13 @@ var _ = Describe("Podman images", func() {
Expect(session.LineInOuputStartsWith("docker.io/library/busybox")).To(BeTrue())
})
+ It("podman empty images list in JSON format", func() {
+ session := podmanTest.Podman([]string{"images", "--format=json", "not-existing-image"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.IsJSONOutputValid()).To(BeTrue())
+ })
+
It("podman images in JSON format", func() {
session := podmanTest.Podman([]string{"images", "--format=json"})
session.WaitWithDefaultTimeout()