From b69aed3bcf18b1c32e217d143937bab2c17f5041 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Thu, 14 Oct 2021 01:44:57 +0200 Subject: [PATCH] feat: add rollback command Closes https://git.coopcloud.tech/coop-cloud/organising/issues/127. --- cli/app/rollback.go | 127 ++++++++++++++++++++++++++++++++++---------- cli/app/upgrade.go | 30 +++++++---- 2 files changed, 119 insertions(+), 38 deletions(-) diff --git a/cli/app/rollback.go b/cli/app/rollback.go index abdc77124..7b6cc340a 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -3,14 +3,15 @@ package app import ( "fmt" - "context" - + "coopcloud.tech/abra/pkg/catalogue" + stack "coopcloud.tech/abra/pkg/client/stack" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/recipe" + "coopcloud.tech/tagcmp" "coopcloud.tech/abra/cli/internal" - appPkg "coopcloud.tech/abra/pkg/app" - "coopcloud.tech/abra/pkg/catalogue" "coopcloud.tech/abra/pkg/client" + "github.com/AlecAivazis/survey/v2" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) @@ -18,8 +19,14 @@ import ( var appRollbackCommand = &cli.Command{ Name: "rollback", Usage: "Roll an app back to a previous version", - Aliases: []string{"r"}, - ArgsUsage: "[]", + Aliases: []string{"r", "downgrade"}, + ArgsUsage: "", + Flags: []cli.Flag{ + internal.ForceFlag, + }, + Description: ` +This command rolls an app back to a previous version if one exists. +`, BashComplete: func(c *cli.Context) { appNames, err := config.GetAppNames() if err != nil { @@ -34,48 +41,110 @@ var appRollbackCommand = &cli.Command{ }, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + stackName := app.StackName() - ctx := context.Background() cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) } - recipeMeta, err := catalogue.GetRecipeMeta(app.Type) + logrus.Debugf("checking whether '%s' is already deployed", stackName) + + isDeployed, deployedVersion, err := stack.IsDeployed(c.Context, cl, stackName) if err != nil { logrus.Fatal(err) } - if len(recipeMeta.Versions) == 0 { - logrus.Fatalf("no catalogue versions available for '%s'", app.Type) - } - deployedVersions, isDeployed, err := appPkg.DeployedVersions(ctx, cl, app) - if err != nil { - logrus.Fatal(err) + if deployedVersion == "" { + logrus.Fatalf("failed to determine version of deployed '%s'", app.Name) } + if !isDeployed { logrus.Fatalf("'%s' is not deployed?", app.Name) } - if _, exists := deployedVersions["app"]; !exists { - logrus.Fatalf("no versioned 'app' service for '%s', cannot determine version", app.Name) + + versions, err := catalogue.GetRecipeCatalogueVersions(app.Type) + if err != nil { + logrus.Fatal(err) } - version := c.Args().Get(1) - if version == "" { - // TODO: - // using deployedVersions["app"], get index+1 version from catalogue - // otherwise bail out saying there is nothing to rollback to - } else { - // TODO - // ensure this version is listed in the catalogue - // ensure this version is "older" (lower down in the list) + var availableDowngrades []string + 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) + } + if parsedVersion != parsedDeployedVersion && parsedVersion.IsLessThan(parsedDeployedVersion) { + availableDowngrades = append(availableDowngrades, version) + } } - // TODO - // display table of existing state and expected state and prompt - // run the deployment with this target version! + if len(availableDowngrades) == 0 { + logrus.Fatal("no available downgrades, you're on latest") + } - logrus.Fatal("command not implemented yet, coming soon TM") + // FIXME: jeezus golang why do you not have a list reverse function + for i, j := 0, len(availableDowngrades)-1; i < j; i, j = i+1, j-1 { + availableDowngrades[i], availableDowngrades[j] = availableDowngrades[j], availableDowngrades[i] + } + + var chosenDowngrade string + if !internal.Force { + prompt := &survey.Select{ + Message: fmt.Sprintf("Please select a downgrade (current version: '%s'):", deployedVersion), + Options: availableDowngrades, + } + if err := survey.AskOne(prompt, &chosenDowngrade); err != nil { + return err + } + } + + if internal.Force { + chosenDowngrade = availableDowngrades[0] + logrus.Debugf("choosing '%s' as version to downgrade to", chosenDowngrade) + } + + if err := recipe.EnsureVersion(app.Type, chosenDowngrade); err != nil { + logrus.Fatal(err) + } + + abraShPath := fmt.Sprintf("%s/%s/%s", config.APPS_DIR, app.Type, "abra.sh") + abraShEnv, err := config.ReadAbraShEnvVars(abraShPath) + if err != nil { + logrus.Fatal(err) + } + for k, v := range abraShEnv { + app.Env[k] = v + } + + composeFiles, err := config.GetAppComposeFiles(app.Type, app.Env) + if err != nil { + logrus.Fatal(err) + } + deployOpts := stack.Deploy{ + Composefiles: composeFiles, + Namespace: stackName, + Prune: false, + ResolveImage: stack.ResolveImageAlways, + } + compose, err := config.GetAppComposeConfig(app.Name, deployOpts, app.Env) + if err != nil { + logrus.Fatal(err) + } + + if !internal.Force { + if err := DeployOverview(app, chosenDowngrade); err != nil { + logrus.Fatal(err) + } + } + + if err := stack.RunDeploy(cl, deployOpts, compose); err != nil { + logrus.Fatal(err) + } return nil }, diff --git a/cli/app/upgrade.go b/cli/app/upgrade.go index 40a6907a2..6edbee1f4 100644 --- a/cli/app/upgrade.go +++ b/cli/app/upgrade.go @@ -20,6 +20,9 @@ var appUpgradeCommand = &cli.Command{ Aliases: []string{"u"}, Usage: "Upgrade an app", ArgsUsage: "", + Flags: []cli.Flag{ + internal.ForceFlag, + }, Description: ` This command supports upgrading an app. You can use it to choose and roll out a new upgrade to an existing app. This command specifically supports changing the @@ -44,11 +47,11 @@ could be destructive, please ensure you have a copy of your app data beforehand } if deployedVersion == "" { - logrus.Fatal("failed to determine version of deployed '%s'", app.Name) + logrus.Fatalf("failed to determine version of deployed '%s'", app.Name) } if !isDeployed { - logrus.Fatal("'%s' is not deployed?", app.Name) + logrus.Fatalf("'%s' is not deployed?", app.Name) } versions, err := catalogue.GetRecipeCatalogueVersions(app.Type) @@ -76,12 +79,19 @@ could be destructive, please ensure you have a copy of your app data beforehand } var chosenUpgrade string - prompt := &survey.Select{ - Message: "Please select an upgrade:", - Options: availableUpgrades, + if !internal.Force { + prompt := &survey.Select{ + Message: fmt.Sprintf("Please select an upgrade (current version: '%s'):", deployedVersion), + Options: availableUpgrades, + } + if err := survey.AskOne(prompt, &chosenUpgrade); err != nil { + return err + } } - if err := survey.AskOne(prompt, &chosenUpgrade); err != nil { - return err + + if internal.Force { + chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] + logrus.Debugf("choosing '%s' as version to upgrade to", chosenUpgrade) } if err := recipe.EnsureVersion(app.Type, chosenUpgrade); err != nil { @@ -112,8 +122,10 @@ could be destructive, please ensure you have a copy of your app data beforehand logrus.Fatal(err) } - if err := DeployOverview(app, chosenUpgrade); err != nil { - logrus.Fatal(err) + if !internal.Force { + if err := DeployOverview(app, chosenUpgrade); err != nil { + logrus.Fatal(err) + } } if err := stack.RunDeploy(cl, deployOpts, compose); err != nil {