summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md18
-rw-r--r--cmd/podman/rmi.go11
-rw-r--r--cmd/podman/varlink/io.podman.varlink10
-rw-r--r--libpod/image/config.go8
-rw-r--r--libpod/runtime_img.go33
-rw-r--r--pkg/adapter/runtime.go2
-rw-r--r--pkg/adapter/runtime_remote.go11
-rw-r--r--pkg/varlinkapi/images.go18
8 files changed, 89 insertions, 22 deletions
diff --git a/API.md b/API.md
index 695f374d8..469dbaa1b 100755
--- a/API.md
+++ b/API.md
@@ -149,6 +149,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func RemoveImage(name: string, force: bool) string](#RemoveImage)
+[func RemoveImageWithResponse(name: string, force: bool) RemoveImageResponse](#RemoveImageWithResponse)
+
[func RemovePod(name: string, force: bool) string](#RemovePod)
[func Reset() ](#Reset)
@@ -257,6 +259,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[type PsOpts](#PsOpts)
+[type RemoveImageResponse](#RemoveImageResponse)
+
[type Runlabel](#Runlabel)
[type Sockets](#Sockets)
@@ -1052,6 +1056,13 @@ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveImage '{"name": "regi
"image": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e"
}
~~~
+### <a name="RemoveImageWithResponse"></a>func RemoveImageWithResponse
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method RemoveImageWithResponse(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) [RemoveImageResponse](#RemoveImageResponse)</div>
+RemoveImageWithResponse takes the name or ID of an image as well as a boolean that determines if containers using that image
+should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The reponse is
+in the form of a RemoveImageResponse .
### <a name="RemovePod"></a>func RemovePod
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -2026,6 +2037,13 @@ size [?bool](#?bool)
sort [?string](#?string)
sync [?bool](#?bool)
+### <a name="RemoveImageResponse"></a>type RemoveImageResponse
+
+
+
+untagged [[]string](#[]string)
+
+deleted [string](https://godoc.org/builtin#string)
### <a name="Runlabel"></a>type Runlabel
Runlabel describes the required input for container runlabel
diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go
index 3f621116e..f4ca88ea8 100644
--- a/cmd/podman/rmi.go
+++ b/cmd/podman/rmi.go
@@ -68,7 +68,7 @@ func rmiCmd(c *cliconfig.RmiValues) error {
images := args[:]
removeImage := func(img *adapter.ContainerImage) {
- msg, err := runtime.RemoveImage(ctx, img, c.Force)
+ response, err := runtime.RemoveImage(ctx, img, c.Force)
if err != nil {
if errors.Cause(err) == storage.ErrImageUsedByContainer {
fmt.Printf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID())
@@ -83,7 +83,14 @@ func rmiCmd(c *cliconfig.RmiValues) error {
lastError = err
return
}
- fmt.Println(msg)
+ // Iterate if any images tags were deleted
+ for _, i := range response.Untagged {
+ fmt.Printf("Untagged: %s\n", i)
+ }
+ // Make sure an image was deleted (and not just untagged); else print it
+ if len(response.Deleted) > 0 {
+ fmt.Printf("Deleted: %s\n", response.Deleted)
+ }
}
if removeAll {
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 2251050c3..1bacd2de6 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -18,6 +18,11 @@ type StringResponse (
message: string
)
+type RemoveImageResponse (
+ untagged: []string,
+ deleted: string
+)
+
type LogLine (
device: string,
parseLogType : string,
@@ -867,6 +872,11 @@ method TagImage(name: string, tagged: string) -> (image: string)
# ~~~
method RemoveImage(name: string, force: bool) -> (image: string)
+# RemoveImageWithResponse takes the name or ID of an image as well as a boolean that determines if containers using that image
+# should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The reponse is
+# in the form of a RemoveImageResponse .
+method RemoveImageWithResponse(name: string, force: bool) -> (response: RemoveImageResponse)
+
# SearchImages searches available registries for images that contain the
# contents of "query" in their name. If "limit" is given, limits the amount of
# search results per registry.
diff --git a/libpod/image/config.go b/libpod/image/config.go
new file mode 100644
index 000000000..40e7fd496
--- /dev/null
+++ b/libpod/image/config.go
@@ -0,0 +1,8 @@
+package image
+
+// ImageDeleteResponse is the response for removing an image from storage and containers
+// what was untagged vs actually removed
+type ImageDeleteResponse struct {
+ Untagged []string `json:"untagged"`
+ Deleted string `json:"deleted"`
+}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index abd8b581d..9943c4104 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -27,19 +27,19 @@ import (
// RemoveImage deletes an image from local storage
// Images being used by running containers can only be removed if force=true
-func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool) (string, error) {
- var returnMessage string
+func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool) (*image.ImageDeleteResponse, error) {
+ response := image.ImageDeleteResponse{}
r.lock.Lock()
defer r.lock.Unlock()
if !r.valid {
- return "", define.ErrRuntimeStopped
+ return nil, define.ErrRuntimeStopped
}
// Get all containers, filter to only those using the image, and remove those containers
ctrs, err := r.state.AllContainers()
if err != nil {
- return "", err
+ return nil, err
}
imageCtrs := []*Container{}
for _, ctr := range ctrs {
@@ -51,17 +51,17 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
if force {
for _, ctr := range imageCtrs {
if err := r.removeContainer(ctx, ctr, true, false, false); err != nil {
- return "", errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", img.ID(), ctr.ID())
+ return nil, errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", img.ID(), ctr.ID())
}
}
} else {
- return "", fmt.Errorf("could not remove image %s as it is being used by %d containers", img.ID(), len(imageCtrs))
+ return nil, fmt.Errorf("could not remove image %s as it is being used by %d containers", img.ID(), len(imageCtrs))
}
}
hasChildren, err := img.IsParent(ctx)
if err != nil {
- return "", err
+ return nil, err
}
if (len(img.Names()) > 1 && !img.InputIsID()) || hasChildren {
@@ -70,19 +70,20 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
// to and untag it.
repoName, err := img.MatchRepoTag(img.InputName)
if hasChildren && errors.Cause(err) == image.ErrRepoTagNotFound {
- return "", errors.Errorf("unable to delete %q (cannot be forced) - image has dependent child images", img.ID())
+ return nil, errors.Errorf("unable to delete %q (cannot be forced) - image has dependent child images", img.ID())
}
if err != nil {
- return "", err
+ return nil, err
}
if err := img.UntagImage(repoName); err != nil {
- return "", err
+ return nil, err
}
- return fmt.Sprintf("Untagged: %s", repoName), nil
+ response.Untagged = append(response.Untagged, repoName)
+ return &response, nil
} else if len(img.Names()) > 1 && img.InputIsID() && !force {
// If the user requests to delete an image by ID and the image has multiple
// reponames and no force is applied, we error out.
- return "", fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", img.ID())
+ return nil, fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", img.ID())
}
err = img.Remove(ctx, force)
if err != nil && errors.Cause(err) == storage.ErrImageUsedByContainer {
@@ -94,11 +95,9 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
err = errStorage
}
}
- for _, name := range img.Names() {
- returnMessage = returnMessage + fmt.Sprintf("Untagged: %s\n", name)
- }
- returnMessage = returnMessage + fmt.Sprintf("Deleted: %s", img.ID())
- return returnMessage, err
+ response.Untagged = append(response.Untagged, img.Names()...)
+ response.Deleted = img.ID()
+ return &response, err
}
// Remove containers that are in storage rather than Podman.
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index ac843b655..dd4f0f35f 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -155,7 +155,7 @@ func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authf
}
// RemoveImage calls into local storage and removes an image
-func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (string, error) {
+func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (*image.ImageDeleteResponse, error) {
return r.Runtime.RemoveImage(ctx, img.Image, force)
}
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 87b4999ce..fe5cc4fef 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -414,8 +414,15 @@ func (ci *ContainerImage) TagImage(tag string) error {
}
// RemoveImage calls varlink to remove an image
-func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (string, error) {
- return iopodman.RemoveImage().Call(r.Conn, img.InputName, force)
+func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (*image.ImageDeleteResponse, error) {
+ ir := image.ImageDeleteResponse{}
+ response, err := iopodman.RemoveImageWithResponse().Call(r.Conn, img.InputName, force)
+ if err != nil {
+ return nil, err
+ }
+ ir.Deleted = response.Deleted
+ ir.Untagged = append(ir.Untagged, response.Untagged...)
+ return &ir, nil
}
// History returns the history of an image and its layers
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 1d46c5b71..ac92343d0 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -465,6 +465,24 @@ func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bo
return call.ReplyRemoveImage(newImage.ID())
}
+// RemoveImageWithResponse accepts an image name and force bool. It returns details about what
+// was done in removeimageresponse struct.
+func (i *LibpodAPI) RemoveImageWithResponse(call iopodman.VarlinkCall, name string, force bool) error {
+ ir := iopodman.RemoveImageResponse{}
+ ctx := getContext()
+ newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return call.ReplyImageNotFound(name, err.Error())
+ }
+ response, err := i.Runtime.RemoveImage(ctx, newImage, force)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ ir.Untagged = append(ir.Untagged, response.Untagged...)
+ ir.Deleted = response.Deleted
+ return call.ReplyRemoveImageWithResponse(ir)
+}
+
// SearchImages searches all registries configured in /etc/containers/registries.conf for an image
// Requires an image name and a search limit as int
func (i *LibpodAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64, filter iopodman.ImageSearchFilter) error {