diff options
-rw-r--r-- | cmd/podman/build.go | 42 | ||||
-rw-r--r-- | cmd/podman/cliconfig/create.go | 9 | ||||
-rw-r--r-- | cmd/podman/images.go | 16 | ||||
-rw-r--r-- | completions/bash/podman | 1 | ||||
-rw-r--r-- | docs/podman-build.1.md | 5 | ||||
-rw-r--r-- | libpod/container_internal.go | 2 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 2 | ||||
-rw-r--r-- | libpod/runtime_cstorage.go | 10 | ||||
-rw-r--r-- | libpod/runtime_img.go | 2 | ||||
-rw-r--r-- | test/e2e/images_test.go | 7 |
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() |