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 }