github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/rmi.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/containers/libpod/cmd/podman/cliconfig"
     8  	"github.com/containers/libpod/pkg/adapter"
     9  	"github.com/containers/storage"
    10  	"github.com/pkg/errors"
    11  	"github.com/spf13/cobra"
    12  )
    13  
    14  var (
    15  	rmiCommand     cliconfig.RmiValues
    16  	rmiDescription = "Removes one or more previously pulled or locally created images."
    17  	_rmiCommand    = cobra.Command{
    18  		Use:   "rmi [flags] IMAGE [IMAGE...]",
    19  		Short: "Removes one or more images from local storage",
    20  		Long:  rmiDescription,
    21  		RunE: func(cmd *cobra.Command, args []string) error {
    22  			rmiCommand.InputArgs = args
    23  			rmiCommand.GlobalFlags = MainGlobalOpts
    24  			rmiCommand.Remote = remoteclient
    25  			return rmiCmd(&rmiCommand)
    26  		},
    27  		Example: `podman rmi imageID
    28    podman rmi --force alpine
    29    podman rmi c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`,
    30  	}
    31  )
    32  
    33  func rmiInit(command *cliconfig.RmiValues) {
    34  	command.SetHelpTemplate(HelpTemplate())
    35  	command.SetUsageTemplate(UsageTemplate())
    36  	flags := command.Flags()
    37  	flags.BoolVarP(&command.All, "all", "a", false, "Remove all images")
    38  	flags.BoolVarP(&command.Force, "force", "f", false, "Force Removal of the image")
    39  }
    40  
    41  func init() {
    42  	rmiCommand.Command = &_rmiCommand
    43  	rmiInit(&rmiCommand)
    44  }
    45  
    46  func rmiCmd(c *cliconfig.RmiValues) error {
    47  	var (
    48  		lastError  error
    49  		failureCnt int
    50  	)
    51  
    52  	ctx := getContext()
    53  	removeAll := c.All
    54  	runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
    55  	if err != nil {
    56  		return errors.Wrapf(err, "could not get runtime")
    57  	}
    58  	defer runtime.DeferredShutdown(false)
    59  
    60  	args := c.InputArgs
    61  	if len(args) == 0 && !removeAll {
    62  		return errors.Errorf("image name or ID must be specified")
    63  	}
    64  	if len(args) > 0 && removeAll {
    65  		return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
    66  	}
    67  
    68  	images := args
    69  
    70  	removeImage := func(img *adapter.ContainerImage) {
    71  		response, err := runtime.RemoveImage(ctx, img, c.Force)
    72  		if err != nil {
    73  			if errors.Cause(err) == storage.ErrImageUsedByContainer {
    74  				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())
    75  			}
    76  			if !adapter.IsImageNotFound(err) {
    77  				exitCode = 2
    78  				failureCnt++
    79  			}
    80  			if lastError != nil {
    81  				fmt.Fprintln(os.Stderr, lastError)
    82  			}
    83  			lastError = err
    84  			return
    85  		}
    86  		// Iterate if any images tags were deleted
    87  		for _, i := range response.Untagged {
    88  			fmt.Printf("Untagged: %s\n", i)
    89  		}
    90  		// Make sure an image was deleted (and not just untagged); else print it
    91  		if len(response.Deleted) > 0 {
    92  			fmt.Printf("Deleted: %s\n", response.Deleted)
    93  		}
    94  	}
    95  
    96  	if removeAll {
    97  		var imagesToDelete []*adapter.ContainerImage
    98  		imagesToDelete, err = runtime.GetRWImages()
    99  		if err != nil {
   100  			return errors.Wrapf(err, "unable to query local images")
   101  		}
   102  		lastNumberofImages := 0
   103  		for len(imagesToDelete) > 0 {
   104  			if lastNumberofImages == len(imagesToDelete) {
   105  				return errors.New("unable to delete all images; re-run the rmi command again.")
   106  			}
   107  			for _, i := range imagesToDelete {
   108  				isParent, err := i.IsParent(ctx)
   109  				if err != nil {
   110  					return err
   111  				}
   112  				if isParent {
   113  					continue
   114  				}
   115  				removeImage(i)
   116  			}
   117  			lastNumberofImages = len(imagesToDelete)
   118  			imagesToDelete, err = runtime.GetRWImages()
   119  			if err != nil {
   120  				return err
   121  			}
   122  			// If no images are left to delete or there is just one image left and it cannot be deleted,
   123  			// lets break out and display the error
   124  			if len(imagesToDelete) == 0 || (lastNumberofImages == 1 && lastError != nil) {
   125  				break
   126  			}
   127  		}
   128  	} else {
   129  		// Create image.image objects for deletion from user input.
   130  		// Note that we have to query the storage one-by-one to
   131  		// always get the latest state for each image.  Otherwise, we
   132  		// run inconsistency issues, for instance, with repoTags.
   133  		// See https://github.com/containers/libpod/issues/930 as
   134  		// an exemplary inconsistency issue.
   135  		for _, i := range images {
   136  			newImage, err := runtime.NewImageFromLocal(i)
   137  			if err != nil {
   138  				if lastError != nil {
   139  					if !adapter.IsImageNotFound(lastError) {
   140  						failureCnt++
   141  					}
   142  					fmt.Fprintln(os.Stderr, lastError)
   143  				}
   144  				lastError = err
   145  				continue
   146  			}
   147  			removeImage(newImage)
   148  		}
   149  	}
   150  
   151  	if adapter.IsImageNotFound(lastError) && failureCnt == 0 {
   152  		exitCode = 1
   153  	}
   154  
   155  	return lastError
   156  }