Add 'kpod images' and 'kpod rmi' commands
'kpod images' lists all images on a system. 'kpod rmi' removes one or more images from a system. The images will not be removed if they are associated with a running container, unless the -f option is used Signed-off-by: Ryan Cole <rcyoalne@gmail.com>
This commit is contained in:
parent
7fb772b7d1
commit
a040f20a76
11 changed files with 1708 additions and 8 deletions
238
cmd/kpod/rmi.go
Normal file
238
cmd/kpod/rmi.go
Normal file
|
@ -0,0 +1,238 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/image/transports"
|
||||
"github.com/containers/image/transports/alltransports"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
rmiDescription = "removes one or more locally stored images."
|
||||
rmiFlags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "force, f",
|
||||
Usage: "force removal of the image",
|
||||
},
|
||||
}
|
||||
rmiCommand = cli.Command{
|
||||
Name: "rmi",
|
||||
Usage: "removes one or more images from local storage",
|
||||
Description: rmiDescription,
|
||||
Action: rmiCmd,
|
||||
ArgsUsage: "IMAGE-NAME-OR-ID [...]",
|
||||
Flags: rmiFlags,
|
||||
}
|
||||
)
|
||||
|
||||
func rmiCmd(c *cli.Context) error {
|
||||
|
||||
force := false
|
||||
if c.IsSet("force") {
|
||||
force = c.Bool("force")
|
||||
}
|
||||
|
||||
args := c.Args()
|
||||
if len(args) == 0 {
|
||||
return errors.Errorf("image name or ID must be specified")
|
||||
}
|
||||
|
||||
store, err := getStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, id := range args {
|
||||
image, err := getImage(id, store)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get image %q", id)
|
||||
}
|
||||
if image != nil {
|
||||
ctrIDs, err := runningContainers(image, store)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error getting running containers for image %q", id)
|
||||
}
|
||||
if len(ctrIDs) > 0 && len(image.Names) <= 1 {
|
||||
if force {
|
||||
removeContainers(ctrIDs, store)
|
||||
} 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 the user supplied an ID, we cannot delete the image if it is referred to by multiple tags
|
||||
if matchesID(image.ID, id) {
|
||||
if len(image.Names) > 1 && !force {
|
||||
return fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", image.ID)
|
||||
}
|
||||
// If it is forced, we have to untag the image so that it can be deleted
|
||||
image.Names = image.Names[:0]
|
||||
} else {
|
||||
name, err2 := untagImage(id, image, store)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("untagged: %s", name)
|
||||
}
|
||||
|
||||
if len(image.Names) > 0 {
|
||||
continue
|
||||
}
|
||||
id, err := removeImage(image, store)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s\n", id)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImage(id string, store storage.Store) (*storage.Image, error) {
|
||||
var ref types.ImageReference
|
||||
ref, err := properImageRef(id)
|
||||
if err != nil {
|
||||
//logrus.Debug(err)
|
||||
}
|
||||
if ref == nil {
|
||||
if ref, err = storageImageRef(store, id); err != nil {
|
||||
//logrus.Debug(err)
|
||||
}
|
||||
}
|
||||
if ref == nil {
|
||||
if ref, err = storageImageID(store, id); err != nil {
|
||||
//logrus.Debug(err)
|
||||
}
|
||||
}
|
||||
if ref != nil {
|
||||
image, err2 := is.Transport.GetStoreImage(store, ref)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
return image, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func untagImage(imgArg string, image *storage.Image, store storage.Store) (string, error) {
|
||||
// Remove name from image.Names and set the new name in the ImageStore
|
||||
imgStore, err := store.ImageStore()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not untag image")
|
||||
}
|
||||
newNames := []string{}
|
||||
removedName := ""
|
||||
for _, name := range image.Names {
|
||||
if matchesReference(name, imgArg) {
|
||||
removedName = name
|
||||
continue
|
||||
}
|
||||
newNames = append(newNames, name)
|
||||
}
|
||||
imgStore.SetNames(image.ID, newNames)
|
||||
err = imgStore.Save()
|
||||
return removedName, err
|
||||
}
|
||||
|
||||
func removeImage(image *storage.Image, store storage.Store) (string, error) {
|
||||
imgStore, err := store.ImageStore()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "could not open image store")
|
||||
}
|
||||
err = imgStore.Delete(image.ID)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "could not remove image")
|
||||
}
|
||||
err = imgStore.Save()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "could not save image store")
|
||||
}
|
||||
return image.ID, nil
|
||||
}
|
||||
|
||||
// Returns a list of running containers associated with the given ImageReference
|
||||
func runningContainers(image *storage.Image, store storage.Store) ([]string, error) {
|
||||
ctrIDs := []string{}
|
||||
ctrStore, err := store.ContainerStore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
containers, err := ctrStore.Containers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ctr := range containers {
|
||||
if ctr.ImageID == image.ID {
|
||||
ctrIDs = append(ctrIDs, ctr.ID)
|
||||
}
|
||||
}
|
||||
return ctrIDs, nil
|
||||
}
|
||||
|
||||
func removeContainers(ctrIDs []string, store storage.Store) error {
|
||||
ctrStore, err := store.ContainerStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ctrID := range ctrIDs {
|
||||
if err = ctrStore.Delete(ctrID); err != nil {
|
||||
return errors.Wrapf(err, "could not remove container %q", ctrID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If it's looks like a proper image reference, parse it and check if it
|
||||
// corresponds to an image that actually exists.
|
||||
func properImageRef(id string) (types.ImageReference, error) {
|
||||
var ref types.ImageReference
|
||||
var err error
|
||||
if ref, err = alltransports.ParseImageName(id); err == nil {
|
||||
if img, err2 := ref.NewImage(nil); err2 == nil {
|
||||
img.Close()
|
||||
return ref, nil
|
||||
}
|
||||
return nil, fmt.Errorf("error confirming presence of image reference %q: %v", transports.ImageName(ref), err)
|
||||
}
|
||||
return nil, fmt.Errorf("error parsing %q as an image reference: %v", id, err)
|
||||
}
|
||||
|
||||
// If it's looks like an image reference that's relative to our storage, parse
|
||||
// it and check if it corresponds to an image that actually exists.
|
||||
func storageImageRef(store storage.Store, id string) (types.ImageReference, error) {
|
||||
var ref types.ImageReference
|
||||
var err error
|
||||
if ref, err = is.Transport.ParseStoreReference(store, id); err == nil {
|
||||
if img, err2 := ref.NewImage(nil); err2 == nil {
|
||||
img.Close()
|
||||
return ref, nil
|
||||
}
|
||||
return nil, fmt.Errorf("error confirming presence of storage image reference %q: %v", transports.ImageName(ref), err)
|
||||
}
|
||||
return nil, fmt.Errorf("error parsing %q as a storage image reference: %v", id, err)
|
||||
}
|
||||
|
||||
// If it might be an ID that's relative to our storage, parse it and check if it
|
||||
// corresponds to an image that actually exists. This _should_ be redundant,
|
||||
// since we already tried deleting the image using the ID directly above, but it
|
||||
// can't hurt either.
|
||||
func storageImageID(store storage.Store, id string) (types.ImageReference, error) {
|
||||
var ref types.ImageReference
|
||||
var err error
|
||||
if ref, err = is.Transport.ParseStoreReference(store, "@"+id); err == nil {
|
||||
if img, err2 := ref.NewImage(nil); err2 == nil {
|
||||
img.Close()
|
||||
return ref, nil
|
||||
}
|
||||
return nil, fmt.Errorf("error confirming presence of storage image reference %q: %v", transports.ImageName(ref), err)
|
||||
}
|
||||
return nil, fmt.Errorf("error parsing %q as a storage image reference: %v", "@"+id, err)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue