diff options
-rwxr-xr-x | API.md | 18 | ||||
-rw-r--r-- | cmd/podman/rmi.go | 11 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 10 | ||||
-rw-r--r-- | libpod/image/config.go | 8 | ||||
-rw-r--r-- | libpod/runtime_img.go | 33 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 2 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 11 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 18 |
8 files changed, 89 insertions, 22 deletions
@@ -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 { |