diff --git a/cli/app/undeploy.go b/cli/app/undeploy.go index 3d1c4d95..79836d24 100644 --- a/cli/app/undeploy.go +++ b/cli/app/undeploy.go @@ -2,15 +2,70 @@ package app import ( "context" + "fmt" + "time" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" stack "coopcloud.tech/abra/pkg/upstream/stack" + "github.com/docker/docker/api/types/filters" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) +var prune bool + +var pruneFlag = &cli.BoolFlag{ + Name: "prune, p", + Destination: &prune, + Usage: "Prunes unused containers, networks, and dangling images for an app", +} + +func cleanup(c *cli.Context) error { + for { + if !prune { + return nil + } + app := internal.ValidateApp(c) + stackName := app.StackName() + cl, err := client.New(app.Server) + if err != nil { + logrus.Fatal(err) + } + ctx := context.Background() + + pruneFilters := filters.NewArgs() + stackSearch := fmt.Sprintf("%s*", stackName) + pruneFilters.Add("label", stackSearch) + cr, err := cl.ContainersPrune(ctx, pruneFilters) + if err != nil { + logrus.Errorf(err.Error()) + time.Sleep(time.Second) + continue + } + logrus.Infof("Containers deleted: %s; Space reclaimed: %v", cr.ContainersDeleted, cr.SpaceReclaimed) + + nr, err := cl.NetworksPrune(ctx, pruneFilters) + if err != nil { + logrus.Errorf(err.Error()) + time.Sleep(time.Second) + continue + } + logrus.Infof("Networks deleted %s", nr.NetworksDeleted) + + ir, err := cl.ImagesPrune(ctx, pruneFilters) + if err != nil { + logrus.Errorf(err.Error()) + time.Sleep(time.Second) + continue + } + logrus.Infof("Images deleted: %s; Space reclaimed: %v", ir.ImagesDeleted, ir.SpaceReclaimed) + break + } + return nil +} + var appUndeployCommand = cli.Command{ Name: "undeploy", Aliases: []string{"un"}, @@ -18,18 +73,18 @@ var appUndeployCommand = cli.Command{ Flags: []cli.Flag{ internal.DebugFlag, internal.NoInputFlag, + pruneFlag, }, Before: internal.SubCommandBefore, Usage: "Undeploy an app", Description: ` This does not destroy any of the application data. However, you should remain vigilant, as your swarm installation will consider any previously attached -volumes as eligiblef or pruning once undeployed. +volumes as eligible for pruning once undeployed. `, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) stackName := app.StackName() - cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) @@ -54,7 +109,7 @@ volumes as eligiblef or pruning once undeployed. if err := stack.RunRemove(context.Background(), cl, rmOpts); err != nil { logrus.Fatal(err) } - + cleanup(c) return nil }, BashComplete: autocomplete.AppNameComplete, diff --git a/cli/server/prune.go b/cli/server/prune.go new file mode 100644 index 00000000..2d59947f --- /dev/null +++ b/cli/server/prune.go @@ -0,0 +1,83 @@ +package server + +import ( + "context" + + "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/pkg/client" + "github.com/docker/docker/api/types/filters" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var allFilter bool + +var allFilterFlag = &cli.BoolFlag{ + Name: "all, a", + Usage: "Remove all unused images not just dangling ones", + Destination: &allFilter, +} + +var volunesFilter bool + +var volumesFilterFlag = &cli.BoolFlag{ + Name: "volumes, v", + Usage: "Prune volumes", + Destination: &volunesFilter, +} + +var serverPruneCommand = cli.Command{ + Name: "prune", + Aliases: []string{"p"}, + Usage: "Prune a managed server; Runs a docker system prune", + Description: "Prunes unused containers, networks, and dangling images", + ArgsUsage: "[]", + Flags: []cli.Flag{ + allFilterFlag, + volumesFilterFlag, + internal.DebugFlag, + }, + Before: internal.SubCommandBefore, + Action: func(c *cli.Context) error { + // Leaving filters empty for now + var args filters.Args + serverName := internal.ValidateServer(c) + + cl, err := client.New(serverName) + if err != nil { + logrus.Fatal(err) + } + ctx := context.Background() + cr, err := cl.ContainersPrune(ctx, args) + if err != nil { + logrus.Fatal(err) + } + logrus.Infof("Containers deleted: %s; Space reclaimed: %v", cr.ContainersDeleted, cr.SpaceReclaimed) + + nr, err := cl.NetworksPrune(ctx, args) + if err != nil { + logrus.Fatal(err) + } + logrus.Infof("Networks deleted %s", nr.NetworksDeleted) + + pruneFilters := filters.NewArgs() + if allFilter { + pruneFilters.Add("dangling", "false") + } + ir, err := cl.ImagesPrune(ctx, pruneFilters) + if err != nil { + logrus.Fatal(err) + } + logrus.Infof("Images deleted: %s; Space reclaimed: %v", ir.ImagesDeleted, ir.SpaceReclaimed) + + if volunesFilter { + vr, err := cl.VolumesPrune(ctx, args) + if err != nil { + logrus.Fatal(err) + } + logrus.Infof("Volumes deleted: %s; Space reclaimed: %v", vr.VolumesDeleted, vr.SpaceReclaimed) + } + + return nil + }, +} diff --git a/cli/server/server.go b/cli/server/server.go index b0869244..034e6fd6 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -22,5 +22,6 @@ recipes, see available flags on "abra server add" for more. serverAddCommand, serverListCommand, serverRemoveCommand, + serverPruneCommand, }, }