diff --git a/cmd/buildah/rm.go b/cmd/buildah/rm.go index 2856fbcb3..d9eb898ef 100644 --- a/cmd/buildah/rm.go +++ b/cmd/buildah/rm.go @@ -13,7 +13,7 @@ var ( rmFlags = []cli.Flag{ cli.BoolFlag{ Name: "all, a", - Usage: "Remove all containers", + Usage: "remove all containers", }, } rmCommand = cli.Command{ diff --git a/cmd/buildah/rmi.go b/cmd/buildah/rmi.go index 735bf497c..b1b3aa421 100644 --- a/cmd/buildah/rmi.go +++ b/cmd/buildah/rmi.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "os" is "github.com/containers/image/storage" "github.com/containers/image/transports" @@ -16,6 +17,10 @@ import ( var ( rmiDescription = "removes one or more locally stored images." rmiFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "remove all images", + }, cli.BoolFlag{ Name: "force, f", Usage: "force removal of the image", @@ -33,11 +38,16 @@ var ( func rmiCmd(c *cli.Context) error { force := c.Bool("force") + removeAll := c.Bool("all") args := c.Args() - if len(args) == 0 { + if len(args) == 0 && !removeAll { return errors.Errorf("image name or ID must be specified") } + if len(args) > 0 && removeAll { + return errors.Errorf("when using the --all switch, you may not pass any images names or IDs") + } + if err := validateFlags(c, rmiFlags); err != nil { return err } @@ -47,26 +57,58 @@ func rmiCmd(c *cli.Context) error { return err } - for _, id := range args { - image, err := getImage(id, store) + imagesToDelete := args[:] + var lastError error + + if removeAll { + images, err := store.Images() if err != nil { - return errors.Wrapf(err, "could not get image %q", id) + return errors.Wrapf(err, "error reading images") + } + for _, image := range images { + imagesToDelete = append(imagesToDelete, image.ID) + } + } + + for _, id := range imagesToDelete { + image, err := getImage(id, store) + if err != nil || image == nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + if err == nil { + err = errors.New("Image does not Exist") + } + lastError = errors.Wrapf(err, "could not get image %q", id) + continue } if image != nil { ctrIDs, err := runningContainers(image, store) if err != nil { - return errors.Wrapf(err, "error getting running containers for image %q", id) + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "error getting running containers for image %q", id) + continue } if len(ctrIDs) > 0 && len(image.Names) <= 1 { if force { err = removeContainers(ctrIDs, store) if err != nil { - return errors.Wrapf(err, "error removing containers %v for image %q", ctrIDs, id) + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "error removing containers %v for image %q", ctrIDs, id) + continue } } else { for _, ctrID := range ctrIDs { - return fmt.Errorf("Could not remove image %q (must force) - container %q is using its reference image", id, ctrID) + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(errors.New("Container Exists"), "Could not remove image %q (must force) - container %q is using its reference image", id, ctrID) } + continue } } // If the user supplied an ID, we cannot delete the image if it is referred to by multiple tags @@ -79,7 +121,11 @@ func rmiCmd(c *cli.Context) error { } else { name, err2 := untagImage(id, image, store) if err2 != nil { - return errors.Wrapf(err, "error removing tag %q from image %q", id, image.ID) + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err2, "error removing tag %q from image %q", id, image.ID) + continue } fmt.Printf("untagged: %s\n", name) } @@ -89,12 +135,19 @@ func rmiCmd(c *cli.Context) error { } id, err := removeImage(image, store) if err != nil { - return errors.Wrapf(err, "error removing image %q", image.ID) + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "error removing image %q", image.ID) + continue } fmt.Printf("%s\n", id) } } + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } return nil } diff --git a/contrib/completions/bash/buildah b/contrib/completions/bash/buildah index 9edb7ede8..6c09a423c 100644 --- a/contrib/completions/bash/buildah +++ b/contrib/completions/bash/buildah @@ -210,6 +210,8 @@ return 1 _buildah_rmi() { local boolean_options=" + --all + -a --force -f --help diff --git a/docs/buildah-rmi.md b/docs/buildah-rmi.md index 67f9f7e38..ebb9c3048 100644 --- a/docs/buildah-rmi.md +++ b/docs/buildah-rmi.md @@ -11,14 +11,22 @@ Removes one or more locally stored images. ## OPTIONS +**--all, -a** + +All local images will be removed from the system that do not have containers using the image as a reference image. + **--force, -f** -Executing this command will stop all containers that are using the image and remove them from the system +This option will cause Buildah to remove all containers that are using the image before removing the image from the system. ## EXAMPLE buildah rmi imageID +buildah rmi --all + +buildah rmi --all --force + buildah rmi --force imageID buildah rmi imageID1 imageID2 imageID3 diff --git a/tests/rmi.bats b/tests/rmi.bats new file mode 100644 index 000000000..c3bf5c2f1 --- /dev/null +++ b/tests/rmi.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats + +load helpers + +@test "remove one image" { + cid=$(buildah from --pull --signature-policy ${TESTSDIR}/policy.json alpine) + buildah rm "$cid" + buildah rmi alpine + run buildah --debug=false images -q + [ "$output" == "" ] +} + +@test "remove multiple images" { + cid1=$(buildah from --pull=false --signature-policy ${TESTSDIR}/policy.json scratch) + cid2=$(buildah from --signature-policy ${TESTSDIR}/policy.json alpine) + cid3=$(buildah from --signature-policy ${TESTSDIR}/policy.json busybox) + buildah rmi alpine busybox + run buildah --debug=false images -q + [ "$output" != "" ] + + buildah rm $cid1 $cid2 $cid3 + buildah rmi alpine busybox + run buildah --debug=false images -q + [ "$output" == "" ] + +} + +@test "remove all images" { + cid1=$(buildah from --signature-policy ${TESTSDIR}/policy.json scratch) + cid2=$(buildah from --signature-policy ${TESTSDIR}/policy.json alpine) + cid3=$(buildah from --signature-policy ${TESTSDIR}/policy.json busybox) + buildah rmi -a -f + run buildah --debug=false images -q + [ "$output" == "" ] + + cid1=$(buildah from --signature-policy ${TESTSDIR}/policy.json scratch) + cid2=$(buildah from --signature-policy ${TESTSDIR}/policy.json alpine) + cid3=$(buildah from --signature-policy ${TESTSDIR}/policy.json busybox) + buildah rmi --all + run buildah --debug=false images -q + [ "$output" != "" ] + + buildah rmi --all --force + run buildah --debug=false images -q + [ "$output" == "" ] +}