mirror of
https://github.com/containers/podman.git
synced 2025-10-14 17:55:51 +08:00
add struct response for removal of images
when removing an image from storage, we should return a struct that details what was untagged vs deleted. this replaces the simple println's used previously and assists in API development. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
18
API.md
18
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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
8
libpod/image/config.go
Normal file
8
libpod/image/config.go
Normal file
@ -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"`
|
||||
}
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user