From d894cb0ee7193ff1e846697ae88cdd0990183b43 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 26 Jan 2023 01:28:42 +0100 Subject: [PATCH 01/21] PoC auto updater --- .gitignore | 1 + Makefile | 3 + cli/updater/updater.go | 277 ++++++++++++++++++++++++++++++++++++ pkg/upstream/stack/stack.go | 35 +++++ 4 files changed, 316 insertions(+) create mode 100644 cli/updater/updater.go diff --git a/.gitignore b/.gitignore index e54a8a4b..7c362554 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .envrc .vscode/ abra +kadabra dist/ tests/integration/.abra/catalogue vendor/ diff --git a/Makefile b/Makefile index 731b3c1f..027ce51a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ ABRA := ./cmd/abra +KADABRA := ./cmd/kadabra COMMIT := $(shell git rev-list -1 HEAD) GOPATH := $(shell go env GOPATH) LDFLAGS := "-X 'main.Commit=$(COMMIT)'" @@ -18,9 +19,11 @@ build-dev: build: @go build -ldflags=$(DIST_LDFLAGS) $(ABRA) + @go build -ldflags=$(DIST_LDFLAGS) $(KADABRA) clean: @rm '$(GOPATH)/bin/abra' + @rm '$(GOPATH)/bin/kadabra' format: @gofmt -s -w . diff --git a/cli/updater/updater.go b/cli/updater/updater.go new file mode 100644 index 00000000..b51e2ff8 --- /dev/null +++ b/cli/updater/updater.go @@ -0,0 +1,277 @@ +package updater + +import ( + "context" + "fmt" + "os" + "strings" + + "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/recipe" + "coopcloud.tech/abra/pkg/upstream/convert" + "coopcloud.tech/abra/pkg/upstream/stack" + "coopcloud.tech/tagcmp" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + dockerclient "github.com/docker/docker/client" + + // "github.com/docker/cli/cli/command/stack/swarm" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +// Upgrade a specific app +var UpgradeApp = cli.Command{ + Name: "appupgrade", + Aliases: []string{"a"}, + Usage: "Upgrade an app", + ArgsUsage: " ", + Flags: []cli.Flag{ + internal.DebugFlag, + internal.ForceFlag, + internal.DontWaitConvergeFlag, + }, + Before: internal.SubCommandBefore, + Description: `Upgrade an app`, + Action: func(c *cli.Context) error { + stackName := c.Args().Get(0) + recipeName := c.Args().Get(1) + cl, err := client.New("default") + if err != nil { + logrus.Fatal(err) + } + upgrade(cl, stackName, recipeName) + return nil + }, +} + +// Upgrade all appS +var UpgradeAll = cli.Command{ + Name: "upgrade", + Aliases: []string{"u"}, + Usage: "Upgrade all apps", + Flags: []cli.Flag{ + internal.DebugFlag, + internal.ForceFlag, + internal.DontWaitConvergeFlag, + }, + Before: internal.SubCommandBefore, + Description: `Upgrade all deployed apps`, + Action: func(c *cli.Context) error { + + cl, err := client.New("default") + if err != nil { + logrus.Fatal(err) + } + // stacks := swarm.GetStacks(cl) + stacks, err := stack.GetStacks(cl) + if err != nil { + logrus.Fatal(err) + } + for _, stackInfo := range stacks { + stackName := stackInfo.Name + recipeName := getRecipe(cl, stackName) + // TODO: read chaos from docker label + if recipeName != "" { + logrus.Debugf("RecipeName: %s", recipeName) + upgrade(cl, stackName, recipeName) + } + } + return nil + }, +} + +// Read recipe from docker label +func getRecipe(cl *dockerclient.Client, stackName string) string { + filter := filters.NewArgs() + filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) + + services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) + if err != nil { + logrus.Fatal(err) + } + for _, service := range services { + labelKey := fmt.Sprintf("coop-cloud.%s.recipe", stackName) + if recipeName, ok := service.Spec.Labels[labelKey]; ok { + return recipeName + } + } + logrus.Debugf("no recipe name found for %s", stackName) + return "" +} + +// Read Env variables from docker services +func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { + envMap := make(map[string]string) + filter := filters.NewArgs() + filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) + + services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) + if err != nil { + logrus.Fatal(err) + } + for _, service := range services { + envList := service.Spec.TaskTemplate.ContainerSpec.Env + for _, envString := range envList { + splitString := strings.SplitN(envString, "=", 2) + if len(splitString) != 2 { + logrus.Debugf("can't separate key from value: %s (this variable is probably unset)", envString) + continue + } + k := splitString[0] + v := splitString[1] + logrus.Debugf("Env Key: %s Value: %s", k, v) + envMap[k] = v + } + } + return envMap +} + +func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { + logrus.Debugf("Upgrade StackName: %s \n Recipe: %s", stackName, recipeName) + app := config.App{ + Name: stackName, + Recipe: recipeName, + Server: "localhost", + Env: getEnv(cl, stackName), + } + // Workaround, is there a better way? + app.Env["STACK_NAME"] = stackName + // TODO: read COMPOSE_FILE from docker label + // TODO: evaluate ENABLE_AUTO_UPDATE env var + + logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName) + isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) + if err != nil { + logrus.Fatal(err) + } + + if !isDeployed { + logrus.Fatalf("%s is not deployed?", stackName) + } + + catl, err := recipe.ReadRecipeCatalogue() + if err != nil { + logrus.Fatal(err) + } + + versions, err := recipe.GetRecipeCatalogueVersions(recipeName, catl) + if err != nil { + logrus.Fatal(err) + } + + if len(versions) == 0 { + logrus.Fatalf("no published releases for %s in the recipe catalogue?", recipeName) + } + + var availableUpgrades []string + if deployedVersion == "unknown" { + availableUpgrades = versions + logrus.Warnf("failed to determine version of deployed %s", stackName) + } + + if deployedVersion != "unknown" { + for _, version := range versions { + parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) + if err != nil { + logrus.Fatal(err) + } + parsedVersion, err := tagcmp.Parse(version) + if err != nil { + logrus.Fatal(err) + } + versionDelta, err := parsedDeployedVersion.UpgradeDelta(parsedVersion) + if err != nil { + logrus.Fatal(err) + } + // Only update Patch/Minor updates + if 0 < versionDelta.UpgradeType() && versionDelta.UpgradeType() < 4 { + availableUpgrades = append(availableUpgrades, version) + } + } + + if len(availableUpgrades) == 0 { + logrus.Fatalf("no available upgrades, you're on latest (%s) ✌️", deployedVersion) + } + } + + availableUpgrades = internal.ReverseStringList(availableUpgrades) + + var chosenUpgrade string + if len(availableUpgrades) > 0 { + chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] + logrus.Debugf("choosing %s as version to upgrade to", chosenUpgrade) + } + + if err := recipe.EnsureExists(recipeName); err != nil { + logrus.Fatal(err) + } + if err := recipe.EnsureVersion(recipeName, chosenUpgrade); err != nil { + logrus.Fatal(err) + } + + abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, recipeName, "abra.sh") + abraShEnv, err := config.ReadAbraShEnvVars(abraShPath) + if err != nil { + logrus.Fatal(err) + } + for k, v := range abraShEnv { + logrus.Debugf("read v:%s k: %s", v, k) + app.Env[k] = v + } + + composeFiles, err := config.GetAppComposeFiles(recipeName, app.Env) + if err != nil { + logrus.Fatal(err) + } + deployOpts := stack.Deploy{ + Composefiles: composeFiles, + Namespace: stackName, + Prune: false, + ResolveImage: stack.ResolveImageAlways, + } + compose, err := config.GetAppComposeConfig(stackName, deployOpts, app.Env) + if err != nil { + logrus.Fatal(err) + } + + if err := stack.RunDeploy(cl, deployOpts, compose, stackName, true); err != nil { + logrus.Fatal(err) + } +} + +func newAbraApp(version, commit string) *cli.App { + app := &cli.App{ + Name: "kadabra", + Usage: `The Co-op Cloud update daemon + ____ ____ _ _ + / ___|___ ___ _ __ / ___| | ___ _ _ __| | + | | / _ \ _____ / _ \| '_ \ | | | |/ _ \| | | |/ _' | + | |__| (_) |_____| (_) | |_) | | |___| | (_) | |_| | (_| | + \____\___/ \___/| .__/ \____|_|\___/ \__,_|\__,_| + |_| +`, + Version: fmt.Sprintf("%s-%s", version, commit[:7]), + Commands: []cli.Command{ + UpgradeApp, + UpgradeAll, + }, + } + + app.Before = func(c *cli.Context) error { + logrus.Debugf("abra version %s, commit %s", version, commit) + return nil + } + return app +} + +// RunApp runs CLI abra app. +func RunApp(version, commit string) { + app := newAbraApp(version, commit) + + if err := app.Run(os.Args); err != nil { + logrus.Fatal(err) + } +} diff --git a/pkg/upstream/stack/stack.go b/pkg/upstream/stack/stack.go index 4e766b34..4355462b 100644 --- a/pkg/upstream/stack/stack.go +++ b/pkg/upstream/stack/stack.go @@ -10,6 +10,7 @@ import ( "coopcloud.tech/abra/pkg/upstream/convert" "github.com/docker/cli/cli/command/service/progress" + "github.com/docker/cli/cli/command/stack/formatter" composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -484,3 +485,37 @@ If a service is failing to even start, try smoke out the error with: `, appName, timeout, appName, appName, appName)) } } + +// FIXME: Copypasta from https://github.com/docker/cli/blob/master/cli/command/stack/swarm/list.go because I could't import "github.com/docker/cli/cli/command/stack/swarm" +// GetStacks lists the swarm stacks. +func GetStacks(cl *dockerclient.Client) ([]*formatter.Stack, error) { + services, err := cl.ServiceList( + context.Background(), + types.ServiceListOptions{Filters: getAllStacksFilter()}) + if err != nil { + return nil, err + } + m := make(map[string]*formatter.Stack) + for _, service := range services { + labels := service.Spec.Labels + name, ok := labels[convert.LabelNamespace] + if !ok { + return nil, errors.Errorf("cannot get label %s for service %s", + convert.LabelNamespace, service.ID) + } + ztack, ok := m[name] + if !ok { + m[name] = &formatter.Stack{ + Name: name, + Services: 1, + } + } else { + ztack.Services++ + } + } + var stacks []*formatter.Stack + for _, stack := range m { + stacks = append(stacks, stack) + } + return stacks, nil +} -- 2.49.0 From 6a55ef26bc8ffc87d2828c173e7435089ca34642 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 26 Jan 2023 14:23:04 +0100 Subject: [PATCH 02/21] updater: read chaos deployment from docker label --- cli/updater/updater.go | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index b51e2ff8..cde87089 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "strconv" "strings" "coopcloud.tech/abra/cli/internal" @@ -31,7 +32,6 @@ var UpgradeApp = cli.Command{ Flags: []cli.Flag{ internal.DebugFlag, internal.ForceFlag, - internal.DontWaitConvergeFlag, }, Before: internal.SubCommandBefore, Description: `Upgrade an app`, @@ -73,10 +73,11 @@ var UpgradeAll = cli.Command{ for _, stackInfo := range stacks { stackName := stackInfo.Name recipeName := getRecipe(cl, stackName) - // TODO: read chaos from docker label - if recipeName != "" { - logrus.Debugf("RecipeName: %s", recipeName) + chaos := getChaos(cl, stackName) + if recipeName != "" && (!chaos || internal.Force) { upgrade(cl, stackName, recipeName) + } else { + logrus.Debugf("Don't update %s due to missing recipe name or chaos deployment", stackName) } } return nil @@ -102,6 +103,29 @@ func getRecipe(cl *dockerclient.Client, stackName string) string { return "" } +// Read chaos deployment from docker label +func getChaos(cl *dockerclient.Client, stackName string) bool { + filter := filters.NewArgs() + filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) + + services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) + if err != nil { + logrus.Fatal(err) + } + for _, service := range services { + labelKey := fmt.Sprintf("coop-cloud.%s.chaos", stackName) + if chaosLabel, ok := service.Spec.Labels[labelKey]; ok { + chaos, err := strconv.ParseBool(chaosLabel) + if err != nil { + logrus.Fatal(err) + } + return chaos + } + } + logrus.Debugf("no chaos label found for %s", stackName) + return false +} + // Read Env variables from docker services func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { envMap := make(map[string]string) @@ -139,7 +163,6 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { } // Workaround, is there a better way? app.Env["STACK_NAME"] = stackName - // TODO: read COMPOSE_FILE from docker label // TODO: evaluate ENABLE_AUTO_UPDATE env var logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName) @@ -193,7 +216,8 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { } if len(availableUpgrades) == 0 { - logrus.Fatalf("no available upgrades, you're on latest (%s) ✌️", deployedVersion) + logrus.Debugf("no available upgrades, you're on latest (%s) ✌️", deployedVersion) + return } } @@ -236,6 +260,10 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { if err != nil { logrus.Fatal(err) } + config.ExposeAllEnv(compose, app.Env) + // after the upgrade the deployment won't be in chaos state any more + config.SetChaosLabel(compose, stackName, false) + config.SetRecipeLabel(compose, stackName, app.Recipe) if err := stack.RunDeploy(cl, deployOpts, compose, stackName, true); err != nil { logrus.Fatal(err) @@ -245,7 +273,7 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { func newAbraApp(version, commit string) *cli.App { app := &cli.App{ Name: "kadabra", - Usage: `The Co-op Cloud update daemon + Usage: `The Co-op Cloud autoupdater ____ ____ _ _ / ___|___ ___ _ __ / ___| | ___ _ _ __| | | | / _ \ _____ / _ \| '_ \ | | | |/ _ \| | | |/ _' | -- 2.49.0 From 8aac9e450d3578b0f7f62802eed29c1d0f1d5ac2 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 26 Jan 2023 21:54:58 +0100 Subject: [PATCH 03/21] pull recipe and lint it --- cli/updater/updater.go | 11 +++++++++++ pkg/recipe/recipe.go | 3 +-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index cde87089..85802c89 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -10,6 +10,7 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/upstream/convert" "coopcloud.tech/abra/pkg/upstream/stack" @@ -232,6 +233,16 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { if err := recipe.EnsureExists(recipeName); err != nil { logrus.Fatal(err) } + if err := recipe.EnsureUpToDate(recipeName); err != nil { + logrus.Fatal(err) + } + r, err := recipe.Get(app.Recipe) + if err != nil { + logrus.Fatal(err) + } + if err := lint.LintForErrors(r); err != nil { + logrus.Fatal(err) + } if err := recipe.EnsureVersion(recipeName, chosenUpgrade); err != nil { logrus.Fatal(err) } diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index acc167d4..e226de50 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -310,8 +310,7 @@ func EnsureVersion(recipeName, version string) error { logrus.Debugf("read %s as tags for recipe %s", strings.Join(parsedTags, ", "), recipeName) if tagRef.String() == "" { - logrus.Warnf("no published release discovered for %s", recipeName) - return nil + return fmt.Errorf("no published release discovered for %s", recipeName) } worktree, err := repo.Worktree() -- 2.49.0 From 64417736b3f00c207b013e7eebc104cc86aadee8 Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 26 Jan 2023 21:59:38 +0100 Subject: [PATCH 04/21] more verbose update info --- cli/updater/updater.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 85802c89..79ee595e 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -78,7 +78,7 @@ var UpgradeAll = cli.Command{ if recipeName != "" && (!chaos || internal.Force) { upgrade(cl, stackName, recipeName) } else { - logrus.Debugf("Don't update %s due to missing recipe name or chaos deployment", stackName) + logrus.Infof("Don't update %s due to missing recipe name or chaos deployment", stackName) } } return nil @@ -217,7 +217,7 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { } if len(availableUpgrades) == 0 { - logrus.Debugf("no available upgrades, you're on latest (%s) ✌️", deployedVersion) + logrus.Infof("no available upgrades for %s, you're on latest (%s)", stackName, deployedVersion) return } } @@ -227,7 +227,7 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { var chosenUpgrade string if len(availableUpgrades) > 0 { chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] - logrus.Debugf("choosing %s as version to upgrade to", chosenUpgrade) + logrus.Infof("choosing %s as version to upgrade to", chosenUpgrade) } if err := recipe.EnsureExists(recipeName); err != nil { -- 2.49.0 From 8b12e4bc7f56e08df1a8689e9ea871ea405d4bcc Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 26 Jan 2023 22:08:59 +0100 Subject: [PATCH 05/21] change .gitignore and add kadabras main.go --- .gitignore | 2 +- cmd/kadabra/main.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 cmd/kadabra/main.go diff --git a/.gitignore b/.gitignore index 7c362554..3475418a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ .envrc .vscode/ abra -kadabra +/kadabra dist/ tests/integration/.abra/catalogue vendor/ diff --git a/cmd/kadabra/main.go b/cmd/kadabra/main.go new file mode 100644 index 00000000..bdac47f4 --- /dev/null +++ b/cmd/kadabra/main.go @@ -0,0 +1,23 @@ +// Package main provides the command-line entrypoint. +package main + +import ( + "coopcloud.tech/abra/cli/updater" +) + +// Version is the current version of Abra +var Version string + +// Commit is the current git commit of Abra +var Commit string + +func main() { + if Version == "" { + Version = "dev" + } + if Commit == "" { + Commit = " " + } + + updater.RunApp(Version, Commit) +} -- 2.49.0 From 79526c0f2391d6e1ff8c742e8051e2555dae7490 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 31 Jan 2023 13:10:15 +0100 Subject: [PATCH 06/21] Refactor upgrade function: extract logical parts --- cli/updater/updater.go | 95 ++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 79ee595e..7b9902dd 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -15,6 +15,7 @@ import ( "coopcloud.tech/abra/pkg/upstream/convert" "coopcloud.tech/abra/pkg/upstream/stack" "coopcloud.tech/tagcmp" + composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" dockerclient "github.com/docker/docker/client" @@ -66,6 +67,7 @@ var UpgradeAll = cli.Command{ if err != nil { logrus.Fatal(err) } + // can't import this lib: // stacks := swarm.GetStacks(cl) stacks, err := stack.GetStacks(cl) if err != nil { @@ -154,18 +156,7 @@ func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { return envMap } -func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { - logrus.Debugf("Upgrade StackName: %s \n Recipe: %s", stackName, recipeName) - app := config.App{ - Name: stackName, - Recipe: recipeName, - Server: "localhost", - Env: getEnv(cl, stackName), - } - // Workaround, is there a better way? - app.Env["STACK_NAME"] = stackName - // TODO: evaluate ENABLE_AUTO_UPDATE env var - +func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string) []string { logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName) isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) if err != nil { @@ -193,10 +184,11 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { var availableUpgrades []string if deployedVersion == "unknown" { availableUpgrades = versions - logrus.Warnf("failed to determine version of deployed %s", stackName) + logrus.Fatalf("failed to determine deployed version of %s", stackName) } if deployedVersion != "unknown" { + logrus.Infof("%s is deployed on %s with version %s", recipeName, stackName, deployedVersion) for _, version := range versions { parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) if err != nil { @@ -216,37 +208,33 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { } } - if len(availableUpgrades) == 0 { - logrus.Infof("no available upgrades for %s, you're on latest (%s)", stackName, deployedVersion) - return - } + } else { + logrus.Warnf("Could not determine the deployed version for %s", stackName) } + return availableUpgrades - availableUpgrades = internal.ReverseStringList(availableUpgrades) - - var chosenUpgrade string - if len(availableUpgrades) > 0 { - chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] - logrus.Infof("choosing %s as version to upgrade to", chosenUpgrade) - } +} +// clone, pull, checkout version and lint the recipe repository +func processRecipeRepoVersion(recipeName string, version string) { if err := recipe.EnsureExists(recipeName); err != nil { logrus.Fatal(err) } if err := recipe.EnsureUpToDate(recipeName); err != nil { logrus.Fatal(err) } - r, err := recipe.Get(app.Recipe) - if err != nil { + if err := recipe.EnsureVersion(recipeName, version); err != nil { logrus.Fatal(err) } - if err := lint.LintForErrors(r); err != nil { - logrus.Fatal(err) - } - if err := recipe.EnsureVersion(recipeName, chosenUpgrade); err != nil { + if r, err := recipe.Get(recipeName); err != nil { + logrus.Fatal(err) + } else if err := lint.LintForErrors(r); err != nil { logrus.Fatal(err) } +} +// merge abra.sh env's into app env's +func mergeAbraShEnv(recipeName string, env config.AppEnv) { abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, recipeName, "abra.sh") abraShEnv, err := config.ReadAbraShEnvVars(abraShPath) if err != nil { @@ -254,10 +242,15 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { } for k, v := range abraShEnv { logrus.Debugf("read v:%s k: %s", v, k) - app.Env[k] = v + env[k] = v } +} - composeFiles, err := config.GetAppComposeFiles(recipeName, app.Env) +func createDeployConfig(recipeName string, stackName string, env config.AppEnv) (*composetypes.Config, stack.Deploy) { + // Workaround, is there a better way? + env["STACK_NAME"] = stackName + + composeFiles, err := config.GetAppComposeFiles(recipeName, env) if err != nil { logrus.Fatal(err) } @@ -267,14 +260,44 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { Prune: false, ResolveImage: stack.ResolveImageAlways, } - compose, err := config.GetAppComposeConfig(stackName, deployOpts, app.Env) + compose, err := config.GetAppComposeConfig(stackName, deployOpts, env) if err != nil { logrus.Fatal(err) } - config.ExposeAllEnv(compose, app.Env) - // after the upgrade the deployment won't be in chaos state any more + config.ExposeAllEnv(compose, env) + // after the upgrade the deployment won't be in chaos state anymore config.SetChaosLabel(compose, stackName, false) - config.SetRecipeLabel(compose, stackName, app.Recipe) + config.SetRecipeLabel(compose, stackName, recipeName) + return compose, deployOpts +} + +func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { + logrus.Debugf("Upgrade StackName: %s \n Recipe: %s", stackName, recipeName) + app := config.App{ + Name: stackName, + Recipe: recipeName, + Server: "localhost", + Env: getEnv(cl, stackName), + } + // TODO: evaluate ENABLE_AUTO_UPDATE env var + + availableUpgrades := getAvailableUpgrades(cl, stackName, recipeName) + if len(availableUpgrades) == 0 { + logrus.Infof("no available upgrades for %s", stackName) + return + } + availableUpgrades = internal.ReverseStringList(availableUpgrades) + var chosenUpgrade string + if len(availableUpgrades) > 0 { + chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] + logrus.Infof("choosing %s as version to upgrade to", chosenUpgrade) + } + + processRecipeRepoVersion(recipeName, chosenUpgrade) + + mergeAbraShEnv(recipeName, app.Env) + + compose, deployOpts := createDeployConfig(recipeName, stackName, app.Env) if err := stack.RunDeploy(cl, deployOpts, compose, stackName, true); err != nil { logrus.Fatal(err) -- 2.49.0 From 6e2f05bdabb5a998b17a45afed0994cdd3fcf871 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 31 Jan 2023 14:08:33 +0100 Subject: [PATCH 07/21] evaluate if autoupdates are enabled --- cli/updater/updater.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 7b9902dd..dbeb8e0d 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -77,10 +77,11 @@ var UpgradeAll = cli.Command{ stackName := stackInfo.Name recipeName := getRecipe(cl, stackName) chaos := getChaos(cl, stackName) - if recipeName != "" && (!chaos || internal.Force) { + updatesEnabled := getUpdateActivation(cl, stackName) + if recipeName != "" && updatesEnabled && (!chaos || internal.Force) { upgrade(cl, stackName, recipeName) } else { - logrus.Infof("Don't update %s due to missing recipe name or chaos deployment", stackName) + logrus.Infof("Don't update %s due to missing recipe name, disabled updates or chaos deployment", stackName) } } return nil @@ -129,6 +130,29 @@ func getChaos(cl *dockerclient.Client, stackName string) bool { return false } +// Read chaos deployment from docker label +func getUpdateActivation(cl *dockerclient.Client, stackName string) bool { + filter := filters.NewArgs() + filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) + + services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) + if err != nil { + logrus.Fatal(err) + } + for _, service := range services { + labelKey := fmt.Sprintf("coop-cloud.%s.autoupdate", stackName) + if chaosLabel, ok := service.Spec.Labels[labelKey]; ok { + chaos, err := strconv.ParseBool(chaosLabel) + if err != nil { + logrus.Fatal(err) + } + return chaos + } + } + logrus.Debugf("no autoupdate label found for %s", stackName) + return false +} + // Read Env variables from docker services func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { envMap := make(map[string]string) @@ -279,7 +303,6 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { Server: "localhost", Env: getEnv(cl, stackName), } - // TODO: evaluate ENABLE_AUTO_UPDATE env var availableUpgrades := getAvailableUpgrades(cl, stackName, recipeName) if len(availableUpgrades) == 0 { -- 2.49.0 From 1516d343d8f0bf1af1382c9187f5180bff6834d9 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 31 Jan 2023 14:19:32 +0100 Subject: [PATCH 08/21] refactor: replace some functions with general getLabel function --- cli/updater/updater.go | 69 +++++++++++------------------------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index dbeb8e0d..58f1a287 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -75,9 +75,9 @@ var UpgradeAll = cli.Command{ } for _, stackInfo := range stacks { stackName := stackInfo.Name - recipeName := getRecipe(cl, stackName) - chaos := getChaos(cl, stackName) - updatesEnabled := getUpdateActivation(cl, stackName) + recipeName := getLabel(cl, stackName, "recipe") + chaos := getBoolLabel(cl, stackName, "chaos") + updatesEnabled := getBoolLabel(cl, stackName, "autoupdate") if recipeName != "" && updatesEnabled && (!chaos || internal.Force) { upgrade(cl, stackName, recipeName) } else { @@ -88,8 +88,8 @@ var UpgradeAll = cli.Command{ }, } -// Read recipe from docker label -func getRecipe(cl *dockerclient.Client, stackName string) string { +// Read docker label in the format coop-cloud.${STACK_NAME}.${LABEL} +func getLabel(cl *dockerclient.Client, stackName string, label string) string { filter := filters.NewArgs() filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) @@ -98,58 +98,25 @@ func getRecipe(cl *dockerclient.Client, stackName string) string { logrus.Fatal(err) } for _, service := range services { - labelKey := fmt.Sprintf("coop-cloud.%s.recipe", stackName) - if recipeName, ok := service.Spec.Labels[labelKey]; ok { - return recipeName + labelKey := fmt.Sprintf("coop-cloud.%s.%s", stackName, label) + if labelValue, ok := service.Spec.Labels[labelKey]; ok { + return labelValue } } - logrus.Debugf("no recipe name found for %s", stackName) + logrus.Debugf("no %s label found for %s", label, stackName) return "" } -// Read chaos deployment from docker label -func getChaos(cl *dockerclient.Client, stackName string) bool { - filter := filters.NewArgs() - filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) - - services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) - if err != nil { - logrus.Fatal(err) - } - for _, service := range services { - labelKey := fmt.Sprintf("coop-cloud.%s.chaos", stackName) - if chaosLabel, ok := service.Spec.Labels[labelKey]; ok { - chaos, err := strconv.ParseBool(chaosLabel) - if err != nil { - logrus.Fatal(err) - } - return chaos +// Read boolean docker label +func getBoolLabel(cl *dockerclient.Client, stackName string, label string) bool { + lableValue := getLabel(cl, stackName, label) + if lableValue != "" { + value, err := strconv.ParseBool(lableValue) + if err != nil { + logrus.Fatal(err) } + return value } - logrus.Debugf("no chaos label found for %s", stackName) - return false -} - -// Read chaos deployment from docker label -func getUpdateActivation(cl *dockerclient.Client, stackName string) bool { - filter := filters.NewArgs() - filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) - - services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) - if err != nil { - logrus.Fatal(err) - } - for _, service := range services { - labelKey := fmt.Sprintf("coop-cloud.%s.autoupdate", stackName) - if chaosLabel, ok := service.Spec.Labels[labelKey]; ok { - chaos, err := strconv.ParseBool(chaosLabel) - if err != nil { - logrus.Fatal(err) - } - return chaos - } - } - logrus.Debugf("no autoupdate label found for %s", stackName) return false } @@ -288,7 +255,7 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv) if err != nil { logrus.Fatal(err) } - config.ExposeAllEnv(compose, env) + config.ExposeAllEnv(stackName, compose, env) // after the upgrade the deployment won't be in chaos state anymore config.SetChaosLabel(compose, stackName, false) config.SetRecipeLabel(compose, stackName, recipeName) -- 2.49.0 From 98db4095a3ea933c21e304525a0804fd34311c8f Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 1 Feb 2023 15:29:47 +0100 Subject: [PATCH 09/21] add notify command and --major flag --- cli/updater/updater.go | 148 +++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 49 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 58f1a287..2e5c47f6 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -25,6 +25,47 @@ import ( "github.com/urfave/cli" ) +var majorUpdate bool +var majorFlag = &cli.BoolFlag{ + Name: "major, m", + Usage: "Also check for major updates", + Destination: &majorUpdate, +} + +// Check for available upgrades +var Notify = cli.Command{ + Name: "notify", + Aliases: []string{"n"}, + Usage: "Check for available upgrades", + Flags: []cli.Flag{ + internal.DebugFlag, + majorFlag, + }, + Before: internal.SubCommandBefore, + Description: `Check for available upgrades`, + Action: func(c *cli.Context) error { + + cl, err := client.New("default") + if err != nil { + logrus.Fatal(err) + } + // can't import this lib: + // stacks := swarm.GetStacks(cl) + stacks, err := stack.GetStacks(cl) + if err != nil { + logrus.Fatal(err) + } + for _, stackInfo := range stacks { + stackName := stackInfo.Name + recipeName := getLabel(cl, stackName, "recipe") + if recipeName != "" { + getLatestUpgrade(cl, stackName, recipeName) + } + } + return nil + }, +} + // Upgrade a specific app var UpgradeApp = cli.Command{ Name: "appupgrade", @@ -34,6 +75,7 @@ var UpgradeApp = cli.Command{ Flags: []cli.Flag{ internal.DebugFlag, internal.ForceFlag, + majorFlag, }, Before: internal.SubCommandBefore, Description: `Upgrade an app`, @@ -44,12 +86,15 @@ var UpgradeApp = cli.Command{ if err != nil { logrus.Fatal(err) } - upgrade(cl, stackName, recipeName) + upgradeVersion := getLatestUpgrade(cl, stackName, recipeName) + if upgradeVersion != "" { + upgrade(cl, stackName, recipeName, upgradeVersion) + } return nil }, } -// Upgrade all appS +// Upgrade all apps var UpgradeAll = cli.Command{ Name: "upgrade", Aliases: []string{"u"}, @@ -57,7 +102,7 @@ var UpgradeAll = cli.Command{ Flags: []cli.Flag{ internal.DebugFlag, internal.ForceFlag, - internal.DontWaitConvergeFlag, + majorFlag, }, Before: internal.SubCommandBefore, Description: `Upgrade all deployed apps`, @@ -79,9 +124,12 @@ var UpgradeAll = cli.Command{ chaos := getBoolLabel(cl, stackName, "chaos") updatesEnabled := getBoolLabel(cl, stackName, "autoupdate") if recipeName != "" && updatesEnabled && (!chaos || internal.Force) { - upgrade(cl, stackName, recipeName) + upgradeVersion := getLatestUpgrade(cl, stackName, recipeName) + if upgradeVersion != "" { + upgrade(cl, stackName, recipeName, upgradeVersion) + } } else { - logrus.Infof("Don't update %s due to missing recipe name, disabled updates or chaos deployment", stackName) + logrus.Debugf("Don't update %s due to missing recipe name, disabled updates or chaos deployment", stackName) } } return nil @@ -140,24 +188,47 @@ func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { } k := splitString[0] v := splitString[1] - logrus.Debugf("Env Key: %s Value: %s", k, v) + logrus.Debugf("For %s read env %s with value: %s from docker service", stackName, k, v) envMap[k] = v } } return envMap } -func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string) []string { +func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) string { + deployedVersion := getDeployedVersion(cl, stackName, recipeName) + availableUpgrades := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion) + if len(availableUpgrades) == 0 { + logrus.Debugf("no available upgrades for %s", stackName) + return "" + } + availableUpgrades = internal.ReverseStringList(availableUpgrades) + var chosenUpgrade string + if len(availableUpgrades) > 0 { + chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] + logrus.Infof("%s (%s) can be upgraded from version %s to %s", stackName, recipeName, deployedVersion, chosenUpgrade) + } + return chosenUpgrade + +} + +func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) string { logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName) isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) if err != nil { logrus.Fatal(err) } - if !isDeployed { logrus.Fatalf("%s is not deployed?", stackName) } + if deployedVersion == "unknown" { + logrus.Fatalf("failed to determine deployed version of %s", stackName) + } + return deployedVersion +} +func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string, + deployedVersion string) []string { catl, err := recipe.ReadRecipeCatalogue() if err != nil { logrus.Fatal(err) @@ -173,35 +244,25 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName } var availableUpgrades []string - if deployedVersion == "unknown" { - availableUpgrades = versions - logrus.Fatalf("failed to determine deployed version of %s", stackName) - } - if deployedVersion != "unknown" { - logrus.Infof("%s is deployed on %s with version %s", recipeName, stackName, deployedVersion) - for _, version := range versions { - parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) - if err != nil { - logrus.Fatal(err) - } - parsedVersion, err := tagcmp.Parse(version) - if err != nil { - logrus.Fatal(err) - } - versionDelta, err := parsedDeployedVersion.UpgradeDelta(parsedVersion) - if err != nil { - logrus.Fatal(err) - } - // Only update Patch/Minor updates - if 0 < versionDelta.UpgradeType() && versionDelta.UpgradeType() < 4 { - availableUpgrades = append(availableUpgrades, version) - } + for _, version := range versions { + parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) + if err != nil { + logrus.Fatal(err) + } + parsedVersion, err := tagcmp.Parse(version) + if err != nil { + logrus.Fatal(err) + } + versionDelta, err := parsedDeployedVersion.UpgradeDelta(parsedVersion) + if err != nil { + logrus.Fatal(err) + } + if 0 < versionDelta.UpgradeType() && (versionDelta.UpgradeType() < 4 || majorUpdate) { + availableUpgrades = append(availableUpgrades, version) } - - } else { - logrus.Warnf("Could not determine the deployed version for %s", stackName) } + return availableUpgrades } @@ -262,8 +323,7 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv) return compose, deployOpts } -func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { - logrus.Debugf("Upgrade StackName: %s \n Recipe: %s", stackName, recipeName) +func upgrade(cl *dockerclient.Client, stackName string, recipeName string, upgradeVersion string) { app := config.App{ Name: stackName, Recipe: recipeName, @@ -271,24 +331,13 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string) { Env: getEnv(cl, stackName), } - availableUpgrades := getAvailableUpgrades(cl, stackName, recipeName) - if len(availableUpgrades) == 0 { - logrus.Infof("no available upgrades for %s", stackName) - return - } - availableUpgrades = internal.ReverseStringList(availableUpgrades) - var chosenUpgrade string - if len(availableUpgrades) > 0 { - chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] - logrus.Infof("choosing %s as version to upgrade to", chosenUpgrade) - } - - processRecipeRepoVersion(recipeName, chosenUpgrade) + processRecipeRepoVersion(recipeName, upgradeVersion) mergeAbraShEnv(recipeName, app.Env) compose, deployOpts := createDeployConfig(recipeName, stackName, app.Env) + logrus.Infof("Upgrade %s (%s) to version %s", stackName, recipeName, upgradeVersion) if err := stack.RunDeploy(cl, deployOpts, compose, stackName, true); err != nil { logrus.Fatal(err) } @@ -307,6 +356,7 @@ func newAbraApp(version, commit string) *cli.App { `, Version: fmt.Sprintf("%s-%s", version, commit[:7]), Commands: []cli.Command{ + Notify, UpgradeApp, UpgradeAll, }, -- 2.49.0 From dd2af251f9ae6897dac9f67650f4914b6190e0b4 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 1 Feb 2023 19:43:25 +0100 Subject: [PATCH 10/21] fix: set updatelabel on upgrade, select latest instead of next release --- cli/updater/updater.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 2e5c47f6..9bfa0b9e 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -202,7 +202,8 @@ func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName stri logrus.Debugf("no available upgrades for %s", stackName) return "" } - availableUpgrades = internal.ReverseStringList(availableUpgrades) + // Uncomment to select the next version instead of the last version + // availableUpgrades = internal.ReverseStringList(availableUpgrades) var chosenUpgrade string if len(availableUpgrades) > 0 { chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] @@ -262,6 +263,7 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName availableUpgrades = append(availableUpgrades, version) } } + logrus.Debugf("Available updates for %s: %s", stackName, availableUpgrades) return availableUpgrades @@ -320,6 +322,7 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv) // after the upgrade the deployment won't be in chaos state anymore config.SetChaosLabel(compose, stackName, false) config.SetRecipeLabel(compose, stackName, recipeName) + config.SetUpdateLabel(compose, stackName, env) return compose, deployOpts } -- 2.49.0 From c21432818cef5c24d759d9e2beb86887686b4094 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:02:21 +0100 Subject: [PATCH 11/21] add kadabra to goreleaser --- .goreleaser.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.goreleaser.yml b/.goreleaser.yml index 808d87fa..9255de18 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -26,6 +26,24 @@ builds: ldflags: - "-X 'main.Commit={{ .Commit }}'" - "-X 'main.Version={{ .Version }}'" + - env: + - CGO_ENABLED=0 + dir: cmd/kadabra + goos: + - linux + - darwin + goarch: + - 386 + - amd64 + - arm + - arm64 + goarm: + - 5 + - 6 + - 7 + ldflags: + - "-X 'main.Commit={{ .Commit }}'" + - "-X 'main.Version={{ .Version }}'" archives: - replacements: 386: i386 -- 2.49.0 From d6fe48d1ce00c8dac35a45e67be14f6416167a8d Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:02:37 +0100 Subject: [PATCH 12/21] fix Makefile alignment --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 027ce51a..8482c27a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ ABRA := ./cmd/abra -KADABRA := ./cmd/kadabra +KADABRA := ./cmd/kadabra COMMIT := $(shell git rev-list -1 HEAD) GOPATH := $(shell go env GOPATH) LDFLAGS := "-X 'main.Commit=$(COMMIT)'" -- 2.49.0 From d1a0145e2f80b4ca3efd6b3a06cd4a166813a703 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:03:00 +0100 Subject: [PATCH 13/21] fix dockerClient renaming --- pkg/upstream/stack/stack.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/upstream/stack/stack.go b/pkg/upstream/stack/stack.go index 4355462b..27d2ee6c 100644 --- a/pkg/upstream/stack/stack.go +++ b/pkg/upstream/stack/stack.go @@ -488,7 +488,7 @@ If a service is failing to even start, try smoke out the error with: // FIXME: Copypasta from https://github.com/docker/cli/blob/master/cli/command/stack/swarm/list.go because I could't import "github.com/docker/cli/cli/command/stack/swarm" // GetStacks lists the swarm stacks. -func GetStacks(cl *dockerclient.Client) ([]*formatter.Stack, error) { +func GetStacks(cl *dockerClient.Client) ([]*formatter.Stack, error) { services, err := cl.ServiceList( context.Background(), types.ServiceListOptions{Filters: getAllStacksFilter()}) -- 2.49.0 From c665d3be0da3206133a2395ccf1a03a60028cc9d Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:04:52 +0100 Subject: [PATCH 14/21] use SERVER constant for setting the server address to localhost --- cli/updater/updater.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 9bfa0b9e..00388a75 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -25,6 +25,8 @@ import ( "github.com/urfave/cli" ) +const SERVER = "localhost" + var majorUpdate bool var majorFlag = &cli.BoolFlag{ Name: "major, m", @@ -330,8 +332,8 @@ func upgrade(cl *dockerclient.Client, stackName string, recipeName string, upgra app := config.App{ Name: stackName, Recipe: recipeName, - Server: "localhost", Env: getEnv(cl, stackName), + Server: SERVER, } processRecipeRepoVersion(recipeName, upgradeVersion) -- 2.49.0 From c20447a6939c079486d369a9d95ed08b35519f4f Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:08:43 +0100 Subject: [PATCH 15/21] Fix comments --- cli/updater/updater.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 00388a75..d49e5c8a 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -138,8 +138,8 @@ var UpgradeAll = cli.Command{ }, } -// Read docker label in the format coop-cloud.${STACK_NAME}.${LABEL} func getLabel(cl *dockerclient.Client, stackName string, label string) string { +// getLabel reads docker label in the format coop-cloud.${STACK_NAME}.${LABEL} filter := filters.NewArgs() filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) @@ -157,9 +157,9 @@ func getLabel(cl *dockerclient.Client, stackName string, label string) string { return "" } -// Read boolean docker label func getBoolLabel(cl *dockerclient.Client, stackName string, label string) bool { lableValue := getLabel(cl, stackName, label) +// getBoolLabel reads a boolean docker label if lableValue != "" { value, err := strconv.ParseBool(lableValue) if err != nil { @@ -170,8 +170,8 @@ func getBoolLabel(cl *dockerclient.Client, stackName string, label string) bool return false } -// Read Env variables from docker services func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { +// getEnv reads Env variables from docker services envMap := make(map[string]string) filter := filters.NewArgs() filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) @@ -200,6 +200,8 @@ func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) string { deployedVersion := getDeployedVersion(cl, stackName, recipeName) availableUpgrades := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion) +// getLatestUpgrade returns the latest available version for an app regarding to the --major flag +// if it is newer than the currently deployed version if len(availableUpgrades) == 0 { logrus.Debugf("no available upgrades for %s", stackName) return "" @@ -216,6 +218,7 @@ func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName stri } func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) string { +// getDeployedVersion returns the currently deployed version of an app logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName) isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) if err != nil { @@ -230,6 +233,8 @@ func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName st return deployedVersion } +// getAvailableUpgrades returns all available versions of an app that are newer than +// the deployed version. It only includes major steps if the --major flag is set. func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string, deployedVersion string) []string { catl, err := recipe.ReadRecipeCatalogue() @@ -271,8 +276,8 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName } -// clone, pull, checkout version and lint the recipe repository func processRecipeRepoVersion(recipeName string, version string) { +// processRecipeRepoVersion clones, pulls, checks out the version and lints the recipe repository if err := recipe.EnsureExists(recipeName); err != nil { logrus.Fatal(err) } @@ -289,8 +294,8 @@ func processRecipeRepoVersion(recipeName string, version string) { } } -// merge abra.sh env's into app env's func mergeAbraShEnv(recipeName string, env config.AppEnv) { +// mergeAbraShEnv merges abra.sh env's into the app env's abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, recipeName, "abra.sh") abraShEnv, err := config.ReadAbraShEnvVars(abraShPath) if err != nil { @@ -303,6 +308,7 @@ func mergeAbraShEnv(recipeName string, env config.AppEnv) { } func createDeployConfig(recipeName string, stackName string, env config.AppEnv) (*composetypes.Config, stack.Deploy) { +// createDeployConfig merges and enriches the compose config for the deployment // Workaround, is there a better way? env["STACK_NAME"] = stackName @@ -329,6 +335,7 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv) } func upgrade(cl *dockerclient.Client, stackName string, recipeName string, upgradeVersion string) { +// upgrade performs all necessary steps to upgrade an app app := config.App{ Name: stackName, Recipe: recipeName, -- 2.49.0 From 25a562f9a18cc93a287bb2b24fc7c91c470d3253 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:10:01 +0100 Subject: [PATCH 16/21] update command descriptions --- cli/updater/updater.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index d49e5c8a..ba73b162 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -44,7 +44,7 @@ var Notify = cli.Command{ majorFlag, }, Before: internal.SubCommandBefore, - Description: `Check for available upgrades`, + Description: `It reads the deployed app versions and looks for new versions in the recipe catalogue. If a new patch/minor version is available, a notification is printed. To include major versions use the --major flag.`, Action: func(c *cli.Context) error { cl, err := client.New("default") @@ -80,7 +80,7 @@ var UpgradeApp = cli.Command{ majorFlag, }, Before: internal.SubCommandBefore, - Description: `Upgrade an app`, + Description: `Upgrade an app by specifying its stack name and recipe. By passing --all instead every deployed app is upgraded. For each apps with enabled auto updates the deployed version is compared with the current recipe catalogue version. If a new patch/minor version is available the app is upgraded. To include major versions use the --major flag. Don't do that, it will probably break things. Only apps that are not deployed with --chaos are upgraded, to update chaos deployments use the --chaos flag. Use it with care.`, Action: func(c *cli.Context) error { stackName := c.Args().Get(0) recipeName := c.Args().Get(1) -- 2.49.0 From 474cbd904b6f1a1ff969ecc7388878cddcc852cd Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:12:11 +0100 Subject: [PATCH 17/21] merge UpgradeApp and UpgradeAll using --all flag --- cli/updater/updater.go | 86 +++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index ba73b162..a6bd725b 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -20,7 +20,6 @@ import ( "github.com/docker/docker/api/types/filters" dockerclient "github.com/docker/docker/client" - // "github.com/docker/cli/cli/command/stack/swarm" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -34,6 +33,13 @@ var majorFlag = &cli.BoolFlag{ Destination: &majorUpdate, } +var updateAll bool +var allFlag = &cli.BoolFlag{ + Name: "all, a", + Usage: "Update all deployed apps", + Destination: &updateAll, +} + // Check for available upgrades var Notify = cli.Command{ Name: "notify", @@ -51,8 +57,6 @@ var Notify = cli.Command{ if err != nil { logrus.Fatal(err) } - // can't import this lib: - // stacks := swarm.GetStacks(cl) stacks, err := stack.GetStacks(cl) if err != nil { logrus.Fatal(err) @@ -68,70 +72,48 @@ var Notify = cli.Command{ }, } -// Upgrade a specific app +// Upgrade apps var UpgradeApp = cli.Command{ - Name: "appupgrade", - Aliases: []string{"a"}, - Usage: "Upgrade an app", + Name: "upgrade", + Aliases: []string{"u"}, + Usage: "Upgrade apps", ArgsUsage: " ", Flags: []cli.Flag{ internal.DebugFlag, - internal.ForceFlag, + internal.ChaosFlag, majorFlag, + allFlag, }, Before: internal.SubCommandBefore, Description: `Upgrade an app by specifying its stack name and recipe. By passing --all instead every deployed app is upgraded. For each apps with enabled auto updates the deployed version is compared with the current recipe catalogue version. If a new patch/minor version is available the app is upgraded. To include major versions use the --major flag. Don't do that, it will probably break things. Only apps that are not deployed with --chaos are upgraded, to update chaos deployments use the --chaos flag. Use it with care.`, Action: func(c *cli.Context) error { - stackName := c.Args().Get(0) - recipeName := c.Args().Get(1) cl, err := client.New("default") if err != nil { logrus.Fatal(err) } - upgradeVersion := getLatestUpgrade(cl, stackName, recipeName) - if upgradeVersion != "" { - upgrade(cl, stackName, recipeName, upgradeVersion) - } - return nil - }, -} -// Upgrade all apps -var UpgradeAll = cli.Command{ - Name: "upgrade", - Aliases: []string{"u"}, - Usage: "Upgrade all apps", - Flags: []cli.Flag{ - internal.DebugFlag, - internal.ForceFlag, - majorFlag, - }, - Before: internal.SubCommandBefore, - Description: `Upgrade all deployed apps`, - Action: func(c *cli.Context) error { - - cl, err := client.New("default") - if err != nil { - logrus.Fatal(err) - } - // can't import this lib: - // stacks := swarm.GetStacks(cl) - stacks, err := stack.GetStacks(cl) - if err != nil { - logrus.Fatal(err) - } - for _, stackInfo := range stacks { - stackName := stackInfo.Name - recipeName := getLabel(cl, stackName, "recipe") - chaos := getBoolLabel(cl, stackName, "chaos") - updatesEnabled := getBoolLabel(cl, stackName, "autoupdate") - if recipeName != "" && updatesEnabled && (!chaos || internal.Force) { - upgradeVersion := getLatestUpgrade(cl, stackName, recipeName) - if upgradeVersion != "" { - upgrade(cl, stackName, recipeName, upgradeVersion) + if !updateAll { + stackName := c.Args().Get(0) + recipeName := c.Args().Get(1) + err = tryUpgrade(cl, stackName, recipeName) + if err != nil { + logrus.Fatal(err) + } + } else { + stacks, err := stack.GetStacks(cl) + if err != nil { + logrus.Fatal(err) + } + for _, stackInfo := range stacks { + stackName := stackInfo.Name + recipeName, err := getLabel(cl, stackName, "recipe") + if err != nil { + logrus.Fatal(err) + } + err = tryUpgrade(cl, stackName, recipeName) + if err != nil { + logrus.Fatal(err) } - } else { - logrus.Debugf("Don't update %s due to missing recipe name, disabled updates or chaos deployment", stackName) } } return nil -- 2.49.0 From da1094029356868e6cf39284eb4800323599f876 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:15:09 +0100 Subject: [PATCH 18/21] pass all errors to the Command function --- cli/updater/updater.go | 141 +++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 56 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index a6bd725b..c9798f6d 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -63,9 +63,15 @@ var Notify = cli.Command{ } for _, stackInfo := range stacks { stackName := stackInfo.Name - recipeName := getLabel(cl, stackName, "recipe") + recipeName, err := getLabel(cl, stackName, "recipe") + if err != nil { + logrus.Fatal(err) + } if recipeName != "" { - getLatestUpgrade(cl, stackName, recipeName) + _, err = getLatestUpgrade(cl, stackName, recipeName) + if err != nil { + logrus.Fatal(err) + } } } return nil @@ -120,47 +126,51 @@ var UpgradeApp = cli.Command{ }, } -func getLabel(cl *dockerclient.Client, stackName string, label string) string { // getLabel reads docker label in the format coop-cloud.${STACK_NAME}.${LABEL} +func getLabel(cl *dockerclient.Client, stackName string, label string) (string, error) { filter := filters.NewArgs() filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) if err != nil { - logrus.Fatal(err) + return "", err } for _, service := range services { labelKey := fmt.Sprintf("coop-cloud.%s.%s", stackName, label) if labelValue, ok := service.Spec.Labels[labelKey]; ok { - return labelValue + return labelValue, nil } } logrus.Debugf("no %s label found for %s", label, stackName) - return "" + return "", nil } -func getBoolLabel(cl *dockerclient.Client, stackName string, label string) bool { - lableValue := getLabel(cl, stackName, label) // getBoolLabel reads a boolean docker label +func getBoolLabel(cl *dockerclient.Client, stackName string, label string) (bool, error) { + lableValue, err := getLabel(cl, stackName, label) + if err != nil { + return false, err + } if lableValue != "" { value, err := strconv.ParseBool(lableValue) if err != nil { - logrus.Fatal(err) + return false, err } - return value + return value, nil } - return false + logrus.Debugf("Boolean label %s could not be found for %s, set default to false.", label, stackName) + return false, nil } -func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { // getEnv reads Env variables from docker services +func getEnv(cl *dockerclient.Client, stackName string) (config.AppEnv, error) { envMap := make(map[string]string) filter := filters.NewArgs() filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter}) if err != nil { - logrus.Fatal(err) + return nil, err } for _, service := range services { envList := service.Spec.TaskTemplate.ContainerSpec.Env @@ -176,17 +186,23 @@ func getEnv(cl *dockerclient.Client, stackName string) config.AppEnv { envMap[k] = v } } - return envMap + return envMap, nil } -func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) string { - deployedVersion := getDeployedVersion(cl, stackName, recipeName) - availableUpgrades := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion) // getLatestUpgrade returns the latest available version for an app regarding to the --major flag // if it is newer than the currently deployed version +func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) (string, error) { + deployedVersion, err := getDeployedVersion(cl, stackName, recipeName) + if err != nil { + return "", err + } + availableUpgrades, err := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion) + if err != nil { + return "", err + } if len(availableUpgrades) == 0 { logrus.Debugf("no available upgrades for %s", stackName) - return "" + return "", nil } // Uncomment to select the next version instead of the last version // availableUpgrades = internal.ReverseStringList(availableUpgrades) @@ -195,42 +211,42 @@ func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName stri chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] logrus.Infof("%s (%s) can be upgraded from version %s to %s", stackName, recipeName, deployedVersion, chosenUpgrade) } - return chosenUpgrade + return chosenUpgrade, nil } -func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) string { // getDeployedVersion returns the currently deployed version of an app +func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) (string, error) { logrus.Debugf("Retrieve deployed version whether %s is already deployed", stackName) isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) if err != nil { - logrus.Fatal(err) + return "", err } if !isDeployed { - logrus.Fatalf("%s is not deployed?", stackName) + return "", fmt.Errorf("%s is not deployed?", stackName) } if deployedVersion == "unknown" { - logrus.Fatalf("failed to determine deployed version of %s", stackName) + return "", fmt.Errorf("failed to determine deployed version of %s", stackName) } - return deployedVersion + return deployedVersion, nil } // getAvailableUpgrades returns all available versions of an app that are newer than // the deployed version. It only includes major steps if the --major flag is set. func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string, - deployedVersion string) []string { + deployedVersion string) ([]string, error) { catl, err := recipe.ReadRecipeCatalogue() if err != nil { - logrus.Fatal(err) + return nil, err } versions, err := recipe.GetRecipeCatalogueVersions(recipeName, catl) if err != nil { - logrus.Fatal(err) + return nil, err } if len(versions) == 0 { - logrus.Fatalf("no published releases for %s in the recipe catalogue?", recipeName) + return nil, fmt.Errorf("no published releases for %s in the recipe catalogue?", recipeName) } var availableUpgrades []string @@ -238,15 +254,15 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName for _, version := range versions { parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) if err != nil { - logrus.Fatal(err) + return nil, err } parsedVersion, err := tagcmp.Parse(version) if err != nil { - logrus.Fatal(err) + return nil, err } versionDelta, err := parsedDeployedVersion.UpgradeDelta(parsedVersion) if err != nil { - logrus.Fatal(err) + return nil, err } if 0 < versionDelta.UpgradeType() && (versionDelta.UpgradeType() < 4 || majorUpdate) { availableUpgrades = append(availableUpgrades, version) @@ -254,87 +270,100 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName } logrus.Debugf("Available updates for %s: %s", stackName, availableUpgrades) - return availableUpgrades + return availableUpgrades, nil } -func processRecipeRepoVersion(recipeName string, version string) { // processRecipeRepoVersion clones, pulls, checks out the version and lints the recipe repository +func processRecipeRepoVersion(recipeName string, version string) error { if err := recipe.EnsureExists(recipeName); err != nil { - logrus.Fatal(err) + return err } if err := recipe.EnsureUpToDate(recipeName); err != nil { - logrus.Fatal(err) + return err } if err := recipe.EnsureVersion(recipeName, version); err != nil { - logrus.Fatal(err) + return err } if r, err := recipe.Get(recipeName); err != nil { - logrus.Fatal(err) + return err } else if err := lint.LintForErrors(r); err != nil { - logrus.Fatal(err) + return err } + return nil } -func mergeAbraShEnv(recipeName string, env config.AppEnv) { // mergeAbraShEnv merges abra.sh env's into the app env's +func mergeAbraShEnv(recipeName string, env config.AppEnv) error { abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, recipeName, "abra.sh") abraShEnv, err := config.ReadAbraShEnvVars(abraShPath) if err != nil { - logrus.Fatal(err) + return err } for k, v := range abraShEnv { logrus.Debugf("read v:%s k: %s", v, k) env[k] = v } + return nil } -func createDeployConfig(recipeName string, stackName string, env config.AppEnv) (*composetypes.Config, stack.Deploy) { // createDeployConfig merges and enriches the compose config for the deployment +func createDeployConfig(recipeName string, stackName string, env config.AppEnv) (*composetypes.Config, stack.Deploy, error) { // Workaround, is there a better way? env["STACK_NAME"] = stackName - composeFiles, err := config.GetAppComposeFiles(recipeName, env) - if err != nil { - logrus.Fatal(err) - } deployOpts := stack.Deploy{ - Composefiles: composeFiles, Namespace: stackName, Prune: false, ResolveImage: stack.ResolveImageAlways, } + composeFiles, err := config.GetAppComposeFiles(recipeName, env) + if err != nil { + return nil, deployOpts, err + } + deployOpts.Composefiles = composeFiles compose, err := config.GetAppComposeConfig(stackName, deployOpts, env) if err != nil { - logrus.Fatal(err) + return nil, deployOpts, err } config.ExposeAllEnv(stackName, compose, env) // after the upgrade the deployment won't be in chaos state anymore config.SetChaosLabel(compose, stackName, false) config.SetRecipeLabel(compose, stackName, recipeName) config.SetUpdateLabel(compose, stackName, env) - return compose, deployOpts + return compose, deployOpts, nil +} } -func upgrade(cl *dockerclient.Client, stackName string, recipeName string, upgradeVersion string) { // upgrade performs all necessary steps to upgrade an app +func upgrade(cl *dockerclient.Client, stackName string, recipeName string, upgradeVersion string) error { + env, err := getEnv(cl, stackName) + if err != nil { + return err + } app := config.App{ Name: stackName, Recipe: recipeName, - Env: getEnv(cl, stackName), Server: SERVER, + Env: env, } - processRecipeRepoVersion(recipeName, upgradeVersion) + if err = processRecipeRepoVersion(recipeName, upgradeVersion); err != nil { + return err + } - mergeAbraShEnv(recipeName, app.Env) + if err = mergeAbraShEnv(recipeName, app.Env); err != nil { + return err + } - compose, deployOpts := createDeployConfig(recipeName, stackName, app.Env) + compose, deployOpts, err := createDeployConfig(recipeName, stackName, app.Env) + if err != nil { + return err + } logrus.Infof("Upgrade %s (%s) to version %s", stackName, recipeName, upgradeVersion) - if err := stack.RunDeploy(cl, deployOpts, compose, stackName, true); err != nil { - logrus.Fatal(err) - } + err = stack.RunDeploy(cl, deployOpts, compose, stackName, true) + return err } func newAbraApp(version, commit string) *cli.App { -- 2.49.0 From db99bfcaa6130f06ae0f390003f311c822753940 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 7 Feb 2023 18:15:41 +0100 Subject: [PATCH 19/21] extract upgrade requirement checkings --- cli/updater/updater.go | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index c9798f6d..1dce0c70 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -333,6 +333,40 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv) config.SetUpdateLabel(compose, stackName, env) return compose, deployOpts, nil } + +// tryUpgrade performs the upgrade if all the requirements are fulfilled +func tryUpgrade(cl *dockerclient.Client, stackName string, recipeName string) error { + if recipeName == "" { + logrus.Debugf("Don't update %s due to missing recipe name", stackName) + return nil + } + chaos, err := getBoolLabel(cl, stackName, "chaos") + if err != nil { + return err + } + if chaos && !internal.Chaos { + logrus.Debugf("Don't update %s due to chaos deployment.", stackName) + return nil + } + updatesEnabled, err := getBoolLabel(cl, stackName, "autoupdate") + if err != nil { + return err + } + if !updatesEnabled { + logrus.Debugf("Don't update %s due to disabling auto updates or missing ENABLE_AUTOUPDATE env.", stackName) + return nil + } + upgradeVersion, err := getLatestUpgrade(cl, stackName, recipeName) + if err != nil { + return err + } + if upgradeVersion == "" { + logrus.Debugf("Don't update %s due to no new version.", stackName) + return nil + } + err = upgrade(cl, stackName, recipeName, upgradeVersion) + return err + } // upgrade performs all necessary steps to upgrade an app @@ -381,7 +415,6 @@ func newAbraApp(version, commit string) *cli.App { Commands: []cli.Command{ Notify, UpgradeApp, - UpgradeAll, }, } -- 2.49.0 From a255c1125deb6014558c16e057ca0150eebe99d5 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 8 Feb 2023 10:40:17 +0100 Subject: [PATCH 20/21] fix goreleaser.yml --- .goreleaser.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 9255de18..a88adcbc 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,5 +1,4 @@ --- -project_name: abra gitea_urls: api: https://git.coopcloud.tech/api/v1 download: https://git.coopcloud.tech/ @@ -11,6 +10,7 @@ builds: - env: - CGO_ENABLED=0 dir: cmd/abra + id: abra goos: - linux - darwin @@ -28,6 +28,7 @@ builds: - "-X 'main.Version={{ .Version }}'" - env: - CGO_ENABLED=0 + id: kadabra dir: cmd/kadabra goos: - linux -- 2.49.0 From 026df90ec1caad27941c9e2acae7fce341850f11 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 8 Feb 2023 19:08:40 +0100 Subject: [PATCH 21/21] resolve some small issues --- cli/updater/updater.go | 26 +++++++++++++------------- pkg/upstream/stack/stack.go | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cli/updater/updater.go b/cli/updater/updater.go index 1dce0c70..bbd31862 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -105,21 +105,22 @@ var UpgradeApp = cli.Command{ if err != nil { logrus.Fatal(err) } - } else { - stacks, err := stack.GetStacks(cl) + return nil + } + + stacks, err := stack.GetStacks(cl) + if err != nil { + logrus.Fatal(err) + } + for _, stackInfo := range stacks { + stackName := stackInfo.Name + recipeName, err := getLabel(cl, stackName, "recipe") if err != nil { logrus.Fatal(err) } - for _, stackInfo := range stacks { - stackName := stackInfo.Name - recipeName, err := getLabel(cl, stackName, "recipe") - if err != nil { - logrus.Fatal(err) - } - err = tryUpgrade(cl, stackName, recipeName) - if err != nil { - logrus.Fatal(err) - } + err = tryUpgrade(cl, stackName, recipeName) + if err != nil { + logrus.Fatal(err) } } return nil @@ -309,7 +310,6 @@ func mergeAbraShEnv(recipeName string, env config.AppEnv) error { // createDeployConfig merges and enriches the compose config for the deployment func createDeployConfig(recipeName string, stackName string, env config.AppEnv) (*composetypes.Config, stack.Deploy, error) { - // Workaround, is there a better way? env["STACK_NAME"] = stackName deployOpts := stack.Deploy{ diff --git a/pkg/upstream/stack/stack.go b/pkg/upstream/stack/stack.go index 27d2ee6c..c2a9ee10 100644 --- a/pkg/upstream/stack/stack.go +++ b/pkg/upstream/stack/stack.go @@ -486,7 +486,7 @@ If a service is failing to even start, try smoke out the error with: } } -// FIXME: Copypasta from https://github.com/docker/cli/blob/master/cli/command/stack/swarm/list.go because I could't import "github.com/docker/cli/cli/command/stack/swarm" +// Copypasta from https://github.com/docker/cli/blob/master/cli/command/stack/swarm/list.go // GetStacks lists the swarm stacks. func GetStacks(cl *dockerClient.Client) ([]*formatter.Stack, error) { services, err := cl.ServiceList( -- 2.49.0