support pruning images

pull/1/head
Jesse Duffield 5 years ago
parent 4fe26f4f88
commit 15bd991560

@ -6,6 +6,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/i18n"
@ -84,9 +85,12 @@ func (c *DockerCommand) GetImages() ([]*Image, error) {
name = tags[0]
}
nameParts := strings.Split(name, ":")
ownImages[i] = &Image{
ID: image.ID,
Name: name,
Name: nameParts[0],
Tag: nameParts[1],
Image: image,
Client: c.Client,
OSCommand: c.OSCommand,
@ -96,3 +100,9 @@ func (c *DockerCommand) GetImages() ([]*Image, error) {
return ownImages, nil
}
// PruneImages prunes images
func (c *DockerCommand) PruneImages() error {
_, err := c.Client.ImagesPrune(context.Background(), filters.Args{})
return err
}

@ -1,6 +1,8 @@
package commands
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/fatih/color"
@ -11,6 +13,7 @@ import (
// Image : A docker Image
type Image struct {
Name string
Tag string
ID string
Image types.ImageSummary
Client *client.Client
@ -20,5 +23,14 @@ type Image struct {
// GetDisplayStrings returns the display string of Image
func (i *Image) GetDisplayStrings(isFocused bool) []string {
return []string{utils.ColoredString(i.Name, color.FgWhite)}
return []string{utils.ColoredString(i.Name, color.FgWhite), utils.ColoredString(i.Tag, color.FgWhite)}
}
// Remove removes the image
func (i *Image) Remove(options types.ImageRemoveOptions) error {
if _, err := i.Client.ImageRemove(context.Background(), i.ID, options); err != nil {
return err
}
return nil
}

@ -293,7 +293,7 @@ func (gui *Gui) handleContainersNextContext(g *gocui.Gui, v *gocui.View) error {
return nil
}
type removeOption struct {
type removeContainerOption struct {
description string
command string
configOptions types.ContainerRemoveOptions
@ -301,7 +301,7 @@ type removeOption struct {
}
// GetDisplayStrings is a function.
func (r *removeOption) GetDisplayStrings(isFocused bool) []string {
func (r *removeContainerOption) GetDisplayStrings(isFocused bool) []string {
return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
}
@ -311,7 +311,7 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
return nil
}
options := []*removeOption{
options := []*removeContainerOption{
{
description: gui.Tr.SLocalize("remove"),
command: "docker rm " + container.ID[1:10],
@ -339,7 +339,7 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
var originalErr commands.ComplexError
if xerrors.As(cerr, &originalErr) {
if originalErr.Code == commands.MustStopContainer {
return gui.createConfirmationPanel(gui.g, v, gui.Tr.SLocalize("Confirm"), gui.Tr.SLocalize("mustForceToRemove"), func(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(gui.g, v, gui.Tr.SLocalize("Confirm"), gui.Tr.SLocalize("mustForceToRemoveContainer"), func(g *gocui.Gui, v *gocui.View) error {
configOptions.Force = true
if err := container.Remove(configOptions); err != nil {
return err
@ -348,7 +348,7 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
}, nil)
}
} else {
return gui.createErrorPanel(gui.g, err.Error())
return gui.createErrorPanel(gui.g, cerr.Error())
}
}

@ -291,7 +291,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
"images": defaultHeight,
"options": defaultHeight,
}
vHeights[currentCyclebleView] = height - defaultHeight*4 - 1
vHeights[currentCyclebleView] = height - defaultHeight*2 - 1
}
optionsVersionBoundary := width - max(len(utils.Decolorise(information)), 1)

@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"github.com/docker/docker/api/types"
"github.com/fatih/color"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/commands"
@ -206,67 +208,66 @@ func (gui *Gui) handleImagesNextContext(g *gocui.Gui, v *gocui.View) error {
return nil
}
// type removeOption struct {
// description string
// command string
// configOptions types.ImageRemoveOptions
// runCommand bool
// }
// // GetDisplayStrings is a function.
// func (r *removeOption) GetDisplayStrings(isFocused bool) []string {
// return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
// }
// func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
// Image, err := gui.getSelectedImage(g)
// if err != nil {
// return nil
// }
// options := []*removeOption{
// {
// description: gui.Tr.SLocalize("remove"),
// command: "docker rm " + Image.ID[1:10],
// configOptions: types.ImageRemoveOptions{},
// runCommand: true,
// },
// {
// description: gui.Tr.SLocalize("removeWithVolumes"),
// command: "docker rm --volumes " + Image.ID[1:10],
// configOptions: types.ImageRemoveOptions{RemoveVolumes: true},
// runCommand: true,
// },
// {
// description: gui.Tr.SLocalize("cancel"),
// runCommand: false,
// },
// }
// handleMenuPress := func(index int) error {
// if !options[index].runCommand {
// return nil
// }
// configOptions := options[index].configOptions
// if cerr := Image.Remove(configOptions); cerr != nil {
// var originalErr commands.ComplexError
// if xerrors.As(cerr, &originalErr) {
// if originalErr.Code == commands.MustStopImage {
// return gui.createConfirmationPanel(gui.g, v, gui.Tr.SLocalize("Confirm"), gui.Tr.SLocalize("mustForceToRemove"), func(g *gocui.Gui, v *gocui.View) error {
// configOptions.Force = true
// if err := Image.Remove(configOptions); err != nil {
// return err
// }
// return gui.refreshImages()
// }, nil)
// }
// } else {
// return gui.createErrorPanel(gui.g, err.Error())
// }
// }
// return gui.refreshImages()
// }
// return gui.createMenu("", options, len(options), handleMenuPress)
// }
type removeImageOption struct {
description string
command string
configOptions types.ImageRemoveOptions
runCommand bool
}
// GetDisplayStrings is a function.
func (r *removeImageOption) GetDisplayStrings(isFocused bool) []string {
return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
}
func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
Image, err := gui.getSelectedImage(g)
if err != nil {
return nil
}
options := []*removeImageOption{
{
description: gui.Tr.SLocalize("remove"),
command: "docker image rm " + Image.ID[1:20],
configOptions: types.ImageRemoveOptions{PruneChildren: true},
runCommand: true,
},
{
description: gui.Tr.SLocalize("removeWithoutPrune"),
command: "docker image rm --no-prune " + Image.ID[1:20],
configOptions: types.ImageRemoveOptions{PruneChildren: false},
runCommand: true,
},
{
description: gui.Tr.SLocalize("cancel"),
runCommand: false,
},
}
handleMenuPress := func(index int) error {
if !options[index].runCommand {
return nil
}
configOptions := options[index].configOptions
if cerr := Image.Remove(configOptions); cerr != nil {
return gui.createErrorPanel(gui.g, cerr.Error())
}
return gui.refreshImages()
}
return gui.createMenu("", options, len(options), handleMenuPress)
}
func (gui *Gui) handlePruneImages(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(gui.g, v, gui.Tr.SLocalize("Confirm"), gui.Tr.SLocalize("confirmPruneImages"), func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.SLocalize("PruningStatus"), func() error {
err := gui.DockerCommand.PruneImages()
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
return gui.refreshImages()
})
}, nil)
}

@ -192,6 +192,20 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleImagesNextContext,
Description: gui.Tr.SLocalize("nextContext"),
},
{
ViewName: "images",
Key: 'd',
Modifier: gocui.ModNone,
Handler: gui.handleImagesRemoveMenu,
Description: gui.Tr.SLocalize("removeImage"),
},
{
ViewName: "images",
Key: 'D',
Modifier: gocui.ModNone,
Handler: gui.handlePruneImages,
Description: gui.Tr.SLocalize("pruneImages"),
},
}
// TODO: add more views here

@ -765,7 +765,7 @@ func addEnglish(i18nObject *i18n.Bundle) error {
Other: `remove with volumes`,
},
&i18n.Message{
ID: "mustForceToRemove",
ID: "mustForceToRemoveContainer",
Other: "You cannot remove a running container unless you force it. Do you want to force it?",
},
&i18n.Message{
@ -820,5 +820,29 @@ func addEnglish(i18nObject *i18n.Bundle) error {
ID: "NoImages",
Other: "No images",
},
&i18n.Message{
ID: "removeImage",
Other: "remove image",
},
&i18n.Message{
ID: "removeWithoutPrune",
Other: "remove without deleting untagged parents",
},
&i18n.Message{
ID: "mustForceToRemoveImage",
Other: "Regular image delete not allowed. Do you want to force remove this image?",
},
&i18n.Message{
ID: "pruneImages",
Other: "prune unused images",
},
&i18n.Message{
ID: "confirmPruneImages",
Other: "Are you sure you want to prune all unused images?",
},
&i18n.Message{
ID: "PruningStatus",
Other: "pruning",
},
)
}

Loading…
Cancel
Save