add minikube rm image command
Signed-off-by: Tharun <rajendrantharun@live.com>pull/10924/head
parent
d0afd8b9ec
commit
b9b657ca85
|
@ -125,8 +125,28 @@ var loadImageCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
var removeImageCmd = &cobra.Command{
|
||||
Use: "rm IMAGE [IMAGE...]",
|
||||
Short: "Remove one or more images",
|
||||
Long: "Load a image into minikube",
|
||||
Example: "minikube image rm image busybox",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
exit.Message(reason.Usage, "Please provide an image to remove via <minikube image rm IMAGE_NAME>")
|
||||
}
|
||||
profile, err := config.LoadProfile(viper.GetString(config.ProfileName))
|
||||
if err != nil {
|
||||
exit.Error(reason.Usage, "loading profile", err)
|
||||
}
|
||||
if err := machine.RemoveImages(args, []*config.Profile{profile}); err != nil {
|
||||
exit.Error(reason.GuestImageRemove, "Failed to remove image", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
imageCmd.AddCommand(loadImageCmd)
|
||||
imageCmd.AddCommand(removeImageCmd)
|
||||
loadImageCmd.Flags().BoolVar(&imgDaemon, "daemon", false, "Cache image from docker daemon")
|
||||
loadImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image from remote registry")
|
||||
}
|
||||
|
|
|
@ -249,6 +249,11 @@ func (r *Containerd) LoadImage(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveImage removes a image
|
||||
func (r *Containerd) RemoveImage(name string) error {
|
||||
return removeCRIImage(r.Runner, name)
|
||||
}
|
||||
|
||||
// CGroupDriver returns cgroup driver ("cgroupfs" or "systemd")
|
||||
func (r *Containerd) CGroupDriver() (string, error) {
|
||||
info, err := getCRIInfo(r.Runner)
|
||||
|
|
|
@ -187,6 +187,19 @@ func killCRIContainers(cr CommandRunner, ids []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// removeCRIImage remove image using crictl
|
||||
func removeCRIImage(cr CommandRunner, name string) error {
|
||||
klog.Infof("Removing image: %s", name)
|
||||
|
||||
crictl := getCrictlPath(cr)
|
||||
args := append([]string{crictl, "rmi"}, name)
|
||||
c := exec.Command("sudo", args...)
|
||||
if _, err := cr.RunCmd(c); err != nil {
|
||||
return errors.Wrap(err, "crictl")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopCRIContainers stops containers using crictl
|
||||
func stopCRIContainers(cr CommandRunner, ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
|
|
|
@ -177,6 +177,11 @@ func (r *CRIO) LoadImage(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveImage removes a image
|
||||
func (r *CRIO) RemoveImage(name string) error {
|
||||
return removeCRIImage(r.Runner, name)
|
||||
}
|
||||
|
||||
// CGroupDriver returns cgroup driver ("cgroupfs" or "systemd")
|
||||
func (r *CRIO) CGroupDriver() (string, error) {
|
||||
c := exec.Command("crio", "config")
|
||||
|
|
|
@ -99,6 +99,9 @@ type Manager interface {
|
|||
// ImageExists takes image name and image sha checks if an it exists
|
||||
ImageExists(string, string) bool
|
||||
|
||||
// RemoveImage remove image based on name
|
||||
RemoveImage(string) error
|
||||
|
||||
// ListContainers returns a list of managed by this container runtime
|
||||
ListContainers(ListOptions) ([]string, error)
|
||||
// KillContainers removes containers based on ID
|
||||
|
|
|
@ -157,6 +157,7 @@ type FakeRunner struct {
|
|||
cmds []string
|
||||
services map[string]serviceState
|
||||
containers map[string]string
|
||||
images map[string]string
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
|
@ -167,6 +168,7 @@ func NewFakeRunner(t *testing.T) *FakeRunner {
|
|||
cmds: []string{},
|
||||
t: t,
|
||||
containers: map[string]string{},
|
||||
images: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,6 +287,18 @@ func (f *FakeRunner) dockerInspect(args []string) (string, error) {
|
|||
return "", nil
|
||||
}
|
||||
|
||||
func (f *FakeRunner) dockerRmi(args []string) (string, error) {
|
||||
// Skip "-f" argument
|
||||
for _, id := range args[1:] {
|
||||
f.t.Logf("fake docker: Removing id %q", id)
|
||||
if f.images[id] == "" {
|
||||
return "", fmt.Errorf("no such image")
|
||||
}
|
||||
delete(f.images, id)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// docker is a fake implementation of docker
|
||||
func (f *FakeRunner) docker(args []string, _ bool) (string, error) {
|
||||
switch cmd := args[0]; cmd {
|
||||
|
@ -308,6 +322,9 @@ func (f *FakeRunner) docker(args []string, _ bool) (string, error) {
|
|||
return f.dockerInspect(args[1:])
|
||||
}
|
||||
|
||||
case "rmi":
|
||||
return f.dockerRmi(args)
|
||||
|
||||
case "inspect":
|
||||
return f.dockerInspect(args)
|
||||
|
||||
|
@ -417,7 +434,14 @@ func (f *FakeRunner) crictl(args []string, _ bool) (string, error) {
|
|||
delete(f.containers, id)
|
||||
|
||||
}
|
||||
|
||||
case "rmi":
|
||||
for _, id := range args[1:] {
|
||||
f.t.Logf("fake crictl: Removing id %q", id)
|
||||
if f.images[id] == "" {
|
||||
return "", fmt.Errorf("no such image")
|
||||
}
|
||||
delete(f.images, id)
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
@ -660,6 +684,9 @@ func TestContainerFunctions(t *testing.T) {
|
|||
"fgh1": prefix + "coredns",
|
||||
"xyz2": prefix + "storage",
|
||||
}
|
||||
runner.images = map[string]string{
|
||||
"image1": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
}
|
||||
cr, err := New(Config{Type: tc.runtime, Runner: runner})
|
||||
if err != nil {
|
||||
t.Fatalf("New(%s): %v", tc.runtime, err)
|
||||
|
@ -709,6 +736,15 @@ func TestContainerFunctions(t *testing.T) {
|
|||
if len(got) > 0 {
|
||||
t.Errorf("ListContainers(apiserver) = %v, want 0 items", got)
|
||||
}
|
||||
|
||||
// Remove a image
|
||||
if err := cr.RemoveImage("image1"); err != nil {
|
||||
t.Fatalf("RemoveImage: %v", err)
|
||||
}
|
||||
// imageExists := cr.ImageExists("image1", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||
if len(runner.images) > 0 {
|
||||
t.Errorf("RemoveImage = %v, want 0 items", len(runner.images))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,6 +172,16 @@ func (r *Docker) LoadImage(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveImage removes a image
|
||||
func (r *Docker) RemoveImage(name string) error {
|
||||
klog.Infof("Removing image: %s", name)
|
||||
c := exec.Command("docker", "rmi", name)
|
||||
if _, err := r.Runner.RunCmd(c); err != nil {
|
||||
return errors.Wrap(err, "remove image docker.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CGroupDriver returns cgroup driver ("cgroupfs" or "systemd")
|
||||
func (r *Docker) CGroupDriver() (string, error) {
|
||||
// Note: the server daemon has to be running, for this call to return successfully
|
||||
|
|
|
@ -292,3 +292,90 @@ func transferAndLoadImage(cr command.Runner, k8s config.KubernetesConfig, src st
|
|||
klog.Infof("Transferred and loaded %s from cache", src)
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeImages removes images from the container run time
|
||||
func removeImages(cc *config.ClusterConfig, runner command.Runner, images []string) error {
|
||||
cr, err := cruntime.New(cruntime.Config{Type: cc.KubernetesConfig.ContainerRuntime, Runner: runner})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "runtime")
|
||||
}
|
||||
|
||||
klog.Infof("RemovingImages start: %s", images)
|
||||
start := time.Now()
|
||||
|
||||
defer func() {
|
||||
klog.Infof("RemovingImages completed in %s", time.Since(start))
|
||||
}()
|
||||
|
||||
var g errgroup.Group
|
||||
|
||||
for _, image := range images {
|
||||
image := image
|
||||
g.Go(func() error {
|
||||
return cr.RemoveImage(image)
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return errors.Wrap(err, "removing images")
|
||||
}
|
||||
klog.Infoln("Successfully removed images")
|
||||
return nil
|
||||
}
|
||||
|
||||
func RemoveImages(images []string, profiles []*config.Profile) error {
|
||||
api, err := NewAPIClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "api")
|
||||
}
|
||||
defer api.Close()
|
||||
|
||||
succeeded := []string{}
|
||||
failed := []string{}
|
||||
|
||||
for _, p := range profiles { // loading images to all running profiles
|
||||
pName := p.Name // capture the loop variable
|
||||
|
||||
c, err := config.Load(pName)
|
||||
if err != nil {
|
||||
// Non-fatal because it may race with profile deletion
|
||||
klog.Errorf("Failed to load profile %q: %v", pName, err)
|
||||
failed = append(failed, pName)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, n := range c.Nodes {
|
||||
m := config.MachineName(*c, n)
|
||||
|
||||
status, err := Status(api, m)
|
||||
if err != nil {
|
||||
klog.Warningf("error getting status for %s: %v", m, err)
|
||||
failed = append(failed, m)
|
||||
continue
|
||||
}
|
||||
|
||||
if status == state.Running.String() { // the not running hosts will load on next start
|
||||
h, err := api.Load(m)
|
||||
if err != nil {
|
||||
klog.Warningf("Failed to load machine %q: %v", m, err)
|
||||
failed = append(failed, m)
|
||||
continue
|
||||
}
|
||||
cr, err := CommandRunner(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = removeImages(c, cr, images)
|
||||
if err != nil {
|
||||
failed = append(failed, m)
|
||||
klog.Warningf("Failed to load cached images for profile %s. make sure the profile is running. %v", pName, err)
|
||||
continue
|
||||
}
|
||||
succeeded = append(succeeded, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
klog.Infof("succeeded removing to: %s", strings.Join(succeeded, " "))
|
||||
klog.Infof("failed removing to: %s", strings.Join(failed, " "))
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -246,6 +246,7 @@ var (
|
|||
GuestCpConfig = Kind{ID: "GUEST_CP_CONFIG", ExitCode: ExGuestConfig}
|
||||
GuestDeletion = Kind{ID: "GUEST_DELETION", ExitCode: ExGuestError}
|
||||
GuestImageLoad = Kind{ID: "GUEST_IMAGE_LOAD", ExitCode: ExGuestError}
|
||||
GuestImageRemove = Kind{ID: "GUEST_IMAGE_REMOVE", ExitCode: ExGuestError}
|
||||
GuestLoadHost = Kind{ID: "GUEST_LOAD_HOST", ExitCode: ExGuestError}
|
||||
GuestMount = Kind{ID: "GUEST_MOUNT", ExitCode: ExGuestError}
|
||||
GuestMountConflict = Kind{ID: "GUEST_MOUNT_CONFLICT", ExitCode: ExGuestConflict}
|
||||
|
|
|
@ -118,3 +118,43 @@ minikube image load image.tar
|
|||
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
|
||||
```
|
||||
|
||||
## minikube image rm
|
||||
|
||||
Remove one or more images
|
||||
|
||||
### Synopsis
|
||||
|
||||
Load a image into minikube
|
||||
|
||||
```shell
|
||||
minikube image rm IMAGE [IMAGE...] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
minikube image rm image busybox
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--add_dir_header If true, adds the file directory to the header of the log messages
|
||||
--alsologtostderr log to standard error as well as files
|
||||
-b, --bootstrapper string The name of the cluster bootstrapper that will set up the Kubernetes cluster. (default "kubeadm")
|
||||
-h, --help
|
||||
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
|
||||
--log_dir string If non-empty, write log files in this directory
|
||||
--log_file string If non-empty, use this log file
|
||||
--log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
|
||||
--logtostderr log to standard error instead of files
|
||||
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level)
|
||||
-p, --profile string The name of the minikube VM being used. This can be set to allow having multiple instances of minikube independently. (default "minikube")
|
||||
--skip_headers If true, avoid header prefixes in the log messages
|
||||
--skip_log_headers If true, avoid headers when opening log files
|
||||
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
|
||||
--user string Specifies the user executing the operation. Useful for auditing operations executed by 3rd party tools. Defaults to the operating system username.
|
||||
-v, --v Level number for the log level verbosity
|
||||
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in New Issue