Compare commits

..

2 Commits

Author SHA1 Message Date
0d8191bc3e review cleanups
All checks were successful
continuous-integration/drone/pr Build is passing
2023-02-17 03:24:04 -05:00
20cdfe7a72 Adding server prune and undeploy prune
All checks were successful
continuous-integration/drone/pr Build is passing
2023-02-14 21:25:37 -05:00
20 changed files with 88 additions and 187 deletions

View File

@ -5,11 +5,9 @@
- 3wordchant
- cassowary
- codegod100
- decentral1se
- frando
- kawaiipunk
- knoflook
- moritz
- roxxers
- yksflip

View File

@ -53,7 +53,7 @@ recipes.
conf := runtime.New()
if !internal.Chaos {
if err := recipe.EnsureUpToDate(app.Recipe, conf); err != nil {
if err := recipe.EnsureUpToDate(app.Recipe); err != nil {
logrus.Fatal(err)
}
}

View File

@ -8,11 +8,8 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@ -25,56 +22,47 @@ var pruneFlag = &cli.BoolFlag{
Usage: "Prunes unused containers, networks, and dangling images for an app",
}
// pruneApp runs the equivalent of a "docker system prune" but only filtering
// against resources connected with the app deployment. It is not a system wide
// prune. Volumes are not pruned to avoid unwated data loss.
func pruneApp(c *cli.Context, cl *dockerClient.Client, app config.App) error {
stackName := app.StackName()
ctx := context.Background()
func cleanup(c *cli.Context) error {
for {
logrus.Debugf("polling for %s stack, waiting to be undeployed...", stackName)
services, err := stack.GetStackServices(ctx, cl, stackName)
if !prune {
return nil
}
app := internal.ValidateApp(c)
stackName := app.StackName()
cl, err := client.New(app.Server)
if err != nil {
return err
}
if len(services) == 0 {
logrus.Debugf("%s undeployed, moving on with pruning logic", stackName)
time.Sleep(time.Second) // give runtime more time to tear down related state
break
}
time.Sleep(time.Second)
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 {
return err
logrus.Errorf(err.Error())
time.Sleep(time.Second)
continue
}
cntSpaceReclaimed := formatter.ByteCountSI(cr.SpaceReclaimed)
logrus.Infof("containers pruned: %d; space reclaimed: %s", len(cr.ContainersDeleted), cntSpaceReclaimed)
logrus.Infof("Containers deleted: %s; Space reclaimed: %v", cr.ContainersDeleted, cr.SpaceReclaimed)
nr, err := cl.NetworksPrune(ctx, pruneFilters)
if err != nil {
return err
logrus.Errorf(err.Error())
time.Sleep(time.Second)
continue
}
logrus.Infof("networks pruned: %d", len(nr.NetworksDeleted))
logrus.Infof("Networks deleted %s", nr.NetworksDeleted)
ir, err := cl.ImagesPrune(ctx, pruneFilters)
if err != nil {
return err
logrus.Errorf(err.Error())
time.Sleep(time.Second)
continue
}
logrus.Infof("Images deleted: %s; Space reclaimed: %v", ir.ImagesDeleted, ir.SpaceReclaimed)
break
}
imgSpaceReclaimed := formatter.ByteCountSI(ir.SpaceReclaimed)
logrus.Infof("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed)
return nil
}
@ -89,14 +77,10 @@ var appUndeployCommand = cli.Command{
},
Before: internal.SubCommandBefore,
Usage: "Undeploy an app",
BashComplete: autocomplete.AppNameComplete,
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 eligible for pruning once undeployed.
Passing "-p/--prune" does not remove those volumes.
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 eligible for pruning once undeployed.
`,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
@ -125,13 +109,8 @@ Passing "-p/--prune" does not remove those volumes.
if err := stack.RunRemove(context.Background(), cl, rmOpts); err != nil {
logrus.Fatal(err)
}
if prune {
if err := pruneApp(c, cl, app); err != nil {
logrus.Fatal(err)
}
}
cleanup(c)
return nil
},
BashComplete: autocomplete.AppNameComplete,
}

View File

@ -61,7 +61,7 @@ recipes.
}
if !internal.Chaos {
if err := recipe.EnsureUpToDate(app.Recipe, conf); err != nil {
if err := recipe.EnsureUpToDate(app.Recipe); err != nil {
logrus.Fatal(err)
}
}

View File

@ -13,7 +13,6 @@ import (
"coopcloud.tech/abra/pkg/formatter"
gitPkg "coopcloud.tech/abra/pkg/git"
"coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/runtime"
"github.com/go-git/go-git/v5"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@ -55,8 +54,6 @@ keys configured on your account.
ArgsUsage: "[<recipe>]",
Action: func(c *cli.Context) error {
recipeName := c.Args().First()
conf := runtime.New()
if recipeName != "" {
internal.ValidateRecipe(c)
}
@ -82,7 +79,7 @@ keys configured on your account.
if !internal.SkipUpdates {
logrus.Warn(logMsg)
if err := recipe.UpdateRepositories(repos, recipeName, conf); err != nil {
if err := recipe.UpdateRepositories(repos, recipeName); err != nil {
logrus.Fatal(err)
}
}
@ -100,7 +97,7 @@ keys configured on your account.
continue
}
versions, err := recipe.GetRecipeVersions(recipeMeta.Name, conf)
versions, err := recipe.GetRecipeVersions(recipeMeta.Name)
if err != nil {
logrus.Warn(err)
}

View File

@ -33,7 +33,7 @@ func DeployAction(c *cli.Context) error {
}
if !Chaos {
if err := recipe.EnsureUpToDate(app.Recipe, conf); err != nil {
if err := recipe.EnsureUpToDate(app.Recipe); err != nil {
logrus.Fatal(err)
}
}

View File

@ -122,8 +122,7 @@ func ensureServerFlag() error {
func NewAction(c *cli.Context) error {
recipe := ValidateRecipeWithPrompt(c, runtime.WithEnsureRecipeLatest(false))
conf := runtime.New(runtime.WithEnsureRecipeLatest(false))
if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
logrus.Fatal(err)
}

View File

@ -42,9 +42,11 @@ func ValidateRecipe(c *cli.Context, opts ...runtime.Option) recipe.Recipe {
}
}
if conf.EnsureRecipeLatest {
if err := recipe.EnsureLatest(recipeName, conf); err != nil {
logrus.Fatal(err)
}
}
logrus.Debugf("validated %s as recipe argument", recipeName)
@ -108,9 +110,11 @@ func ValidateRecipeWithPrompt(c *cli.Context, opts ...runtime.Option) recipe.Rec
logrus.Fatal(err)
}
if conf.EnsureRecipeLatest {
if err := recipe.EnsureLatest(recipeName, conf); err != nil {
logrus.Fatal(err)
}
}
logrus.Debugf("validated %s as recipe argument", recipeName)

View File

@ -4,7 +4,6 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/runtime"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@ -22,8 +21,6 @@ var recipeFetchCommand = cli.Command{
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
recipeName := c.Args().First()
conf := runtime.New()
if recipeName != "" {
internal.ValidateRecipe(c)
return nil // ValidateRecipe ensures latest checkout
@ -34,7 +31,7 @@ var recipeFetchCommand = cli.Command{
logrus.Fatal(err)
}
if err := recipe.UpdateRepositories(repos, recipeName, conf); err != nil {
if err := recipe.UpdateRepositories(repos, recipeName); err != nil {
logrus.Fatal(err)
}

View File

@ -8,7 +8,6 @@ import (
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/lint"
recipePkg "coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/runtime"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@ -26,9 +25,8 @@ var recipeLintCommand = cli.Command{
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
recipe := internal.ValidateRecipe(c)
conf := runtime.New()
if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
logrus.Fatal(err)
}

View File

@ -14,7 +14,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
recipePkg "coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/tagcmp"
"github.com/AlecAivazis/survey/v2"
"github.com/docker/distribution/reference"
@ -61,9 +60,8 @@ You may invoke this command in "wizard" mode and be prompted for input:
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
recipe := internal.ValidateRecipeWithPrompt(c)
conf := runtime.New()
if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
logrus.Fatal(err)
}

View File

@ -39,7 +39,11 @@ var serverListCommand = cli.Command{
tableColumns := []string{"name", "host", "user", "port"}
table := formatter.CreateTable(tableColumns)
if internal.MachineReadable {
defer table.JSONRender()
} else {
defer table.Render()
}
serverNames, err := config.ReadServerNames()
if err != nil {
logrus.Fatal(err)
@ -80,16 +84,6 @@ var serverListCommand = cli.Command{
}
}
if internal.MachineReadable {
table.JSONRender()
} else {
if problemsFilter && table.NumLines() == 0 {
logrus.Info("all servers wired up correctly 👏")
} else {
table.Render()
}
}
return nil
},
}

View File

@ -4,9 +4,7 @@ import (
"context"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/formatter"
"github.com/docker/docker/api/types/filters"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@ -24,7 +22,7 @@ var volunesFilter bool
var volumesFilterFlag = &cli.BoolFlag{
Name: "volumes, v",
Usage: "Prune volumes. This will remove app data, Be Careful!",
Usage: "Prune volumes",
Destination: &volunesFilter,
}
@ -32,12 +30,7 @@ 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.
If passing "-v/--volumes" then volumes not connected with a deployed app will
also be removed. This can result in unwanted data loss if not used carefully.
`,
Description: "Prunes unused containers, networks, and dangling images",
ArgsUsage: "[<server>]",
Flags: []cli.Flag{
allFilterFlag,
@ -45,54 +38,44 @@ also be removed. This can result in unwanted data loss if not used carefully.
internal.DebugFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.ServerNameComplete,
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)
}
cntSpaceReclaimed := formatter.ByteCountSI(cr.SpaceReclaimed)
logrus.Infof("containers pruned: %d; space reclaimed: %s", len(cr.ContainersDeleted), cntSpaceReclaimed)
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 pruned: %d", len(nr.NetworksDeleted))
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)
}
imgSpaceReclaimed := formatter.ByteCountSI(ir.SpaceReclaimed)
logrus.Infof("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed)
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)
}
volSpaceReclaimed := formatter.ByteCountSI(vr.SpaceReclaimed)
logrus.Infof("volumes pruned: %d; space reclaimed: %s", len(vr.VolumesDeleted), volSpaceReclaimed)
logrus.Infof("Volumes deleted: %s; Space reclaimed: %v", vr.VolumesDeleted, vr.SpaceReclaimed)
}
return nil

View File

@ -7,7 +7,6 @@ import (
"path/filepath"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
@ -126,7 +125,6 @@ like tears in rain.
internal.HetznerCloudAPITokenFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.ServerNameComplete,
Action: func(c *cli.Context) error {
serverName := internal.ValidateServer(c)

View File

@ -321,7 +321,7 @@ func processRecipeRepoVersion(recipeName, version string, conf *runtime.Config)
return err
}
if err := recipe.EnsureUpToDate(recipeName, conf); err != nil {
if err := recipe.EnsureUpToDate(recipeName); err != nil {
return err
}

View File

@ -9,7 +9,7 @@ import (
"github.com/urfave/cli"
)
// AppNameComplete copletes app names.
// AppNameComplete copletes app names
func AppNameComplete(c *cli.Context) {
appNames, err := config.GetAppNames()
if err != nil {
@ -25,7 +25,7 @@ func AppNameComplete(c *cli.Context) {
}
}
// RecipeNameComplete completes recipe names.
// RecipeNameComplete completes recipe names
func RecipeNameComplete(c *cli.Context) {
catl, err := recipe.ReadRecipeCatalogue()
if err != nil {
@ -41,23 +41,7 @@ func RecipeNameComplete(c *cli.Context) {
}
}
// ServerNameComplete completes server names.
func ServerNameComplete(c *cli.Context) {
files, err := config.LoadAppFiles("")
if err != nil {
logrus.Fatal(err)
}
if c.NArg() > 0 {
return
}
for _, appFile := range files {
fmt.Println(appFile.Server)
}
}
// SubcommandComplete completes sub-commands.
// SubcommandComplete completes subcommands.
func SubcommandComplete(c *cli.Context) {
if c.NArg() > 0 {
return

View File

@ -1,7 +1,6 @@
package formatter
import (
"fmt"
"os"
"strings"
"time"
@ -71,21 +70,3 @@ func StripTagMeta(image string) string {
return image
}
// ByteCountSI presents a human friendly representation of a byte count. See
// https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format.
func ByteCountSI(b uint64) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := uint64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
}

View File

@ -339,10 +339,6 @@ func EnsureVersion(recipeName, version string) error {
// EnsureLatest makes sure the latest commit is checked out for a local recipe repository
func EnsureLatest(recipeName string, conf *runtime.Config) error {
if !conf.EnsureRecipeLatest {
return nil
}
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
isClean, err := gitPkg.IsClean(recipeDir)
@ -587,11 +583,7 @@ func GetStringInBetween(recipeName, str, start, end string) (result string, err
}
// EnsureUpToDate ensures that the local repo is synced to the remote
func EnsureUpToDate(recipeName string, conf *runtime.Config) error {
if !conf.EnsureRecipeLatest {
return nil
}
func EnsureUpToDate(recipeName string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
isClean, err := gitPkg.IsClean(recipeDir)
@ -936,8 +928,10 @@ func ReadReposMetadata() (RepoCatalogue, error) {
}
// GetRecipeVersions retrieves all recipe versions.
func GetRecipeVersions(recipeName string, conf *runtime.Config) (RecipeVersions, error) {
func GetRecipeVersions(recipeName string, opts ...runtime.Option) (RecipeVersions, error) {
versions := RecipeVersions{}
conf := runtime.New(opts...)
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
logrus.Debugf("attempting to open git repository in %s", recipeDir)
@ -1039,7 +1033,7 @@ func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]stri
}
// UpdateRepositories clones and updates all recipe repositories locally.
func UpdateRepositories(repos RepoCatalogue, recipeName string, conf *runtime.Config) error {
func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
var barLength int
if recipeName != "" {
barLength = 1
@ -1073,7 +1067,7 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, conf *runtime.Co
logrus.Fatal(err)
}
if err := EnsureUpToDate(rm.Name, conf); err != nil {
if err := EnsureUpToDate(rm.Name); err != nil {
logrus.Fatal(err)
}

View File

@ -11,10 +11,7 @@ type Config struct {
EnsureRecipeLatest bool // ensure the local recipe has latest changes
}
// Option modified a Config. The convetion for passing Options to functions is
// so far, only used in internal/validate.go. A Config is then constructed and
// passed down further into the code. This may change in the future but this is
// at least the abstraction so far.
// Option modified a Config.
type Option func(c *Config)
// New instantiates a new Config.

View File

@ -2,7 +2,7 @@
ABRA_VERSION="0.6.0-beta"
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
RC_VERSION="0.7.0-rc3-beta"
RC_VERSION="0.7.0-rc2-beta"
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$RC_VERSION"
for arg in "$@"; do