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