Now that we have correct sorting of versions: coop-cloud/organising#427 We don't need to reverse sort. Only for showing prompts when the latest should be the first. Otherwise, logic can follow the sorted order, the last item in the list is the latest upgrade. Related: coop-cloud/organising#444 Also fix `upgrade` to actually show the latest version
198 lines
5.2 KiB
Go
198 lines
5.2 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"coopcloud.tech/abra/pkg/autocomplete"
|
|
"coopcloud.tech/abra/pkg/config"
|
|
"coopcloud.tech/abra/pkg/lint"
|
|
"coopcloud.tech/abra/pkg/recipe"
|
|
"coopcloud.tech/abra/pkg/runtime"
|
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
|
"coopcloud.tech/tagcmp"
|
|
|
|
"coopcloud.tech/abra/cli/internal"
|
|
"coopcloud.tech/abra/pkg/client"
|
|
"github.com/AlecAivazis/survey/v2"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var appRollbackCommand = cli.Command{
|
|
Name: "rollback",
|
|
Aliases: []string{"rl"},
|
|
Usage: "Roll an app back to a previous version",
|
|
ArgsUsage: "<domain>",
|
|
Flags: []cli.Flag{
|
|
internal.DebugFlag,
|
|
internal.NoInputFlag,
|
|
internal.ForceFlag,
|
|
internal.ChaosFlag,
|
|
internal.NoDomainChecksFlag,
|
|
internal.DontWaitConvergeFlag,
|
|
},
|
|
Before: internal.SubCommandBefore,
|
|
Description: `
|
|
This command rolls an app back to a previous version if one exists.
|
|
|
|
You may pass "--force/-f" to downgrade to the same version again. This can be
|
|
useful if the container runtime has gotten into a weird state.
|
|
|
|
This action could be destructive, please ensure you have a copy of your app
|
|
data beforehand.
|
|
|
|
Chas mode ("--chaos") will deploy your local checkout of a recipe as-is,
|
|
including unstaged changes and can be useful for live hacking and testing new
|
|
recipes.
|
|
`,
|
|
BashComplete: autocomplete.AppNameComplete,
|
|
Action: func(c *cli.Context) error {
|
|
app := internal.ValidateApp(c)
|
|
stackName := app.StackName()
|
|
conf := runtime.New()
|
|
|
|
if !internal.Chaos {
|
|
if err := recipe.EnsureUpToDate(app.Recipe, conf); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
r, err := recipe.Get(app.Recipe, conf)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := lint.LintForErrors(r); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
cl, err := client.New(app.Server)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
logrus.Debugf("checking 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?", app.Name)
|
|
}
|
|
|
|
catl, err := recipe.ReadRecipeCatalogue()
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
versions, err := recipe.GetRecipeCatalogueVersions(app.Recipe, catl)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if len(versions) == 0 && !internal.Chaos {
|
|
logrus.Fatalf("no published releases for %s in the recipe catalogue?", app.Recipe)
|
|
}
|
|
|
|
var availableDowngrades []string
|
|
if deployedVersion == "unknown" {
|
|
availableDowngrades = versions
|
|
logrus.Warnf("failed to determine version of deployed %s", app.Name)
|
|
}
|
|
|
|
if deployedVersion != "unknown" && !internal.Chaos {
|
|
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)
|
|
}
|
|
}
|
|
|
|
if len(availableDowngrades) == 0 {
|
|
logrus.Info("no available downgrades, you're on oldest ✌️")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
var chosenDowngrade string
|
|
if len(availableDowngrades) > 0 && !internal.Chaos {
|
|
if internal.Force || internal.NoInput {
|
|
chosenDowngrade = availableDowngrades[len(availableDowngrades)-1]
|
|
logrus.Debugf("choosing %s as version to downgrade to (--force)", chosenDowngrade)
|
|
} else {
|
|
prompt := &survey.Select{
|
|
Message: fmt.Sprintf("Please select a downgrade (current version: %s):", deployedVersion),
|
|
Options: internal.ReverseStringList(availableDowngrades),
|
|
}
|
|
if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if !internal.Chaos {
|
|
if err := recipe.EnsureVersion(app.Recipe, chosenDowngrade); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if internal.Chaos {
|
|
logrus.Warn("chaos mode engaged")
|
|
var err error
|
|
chosenDowngrade, err = recipe.ChaosVersion(app.Recipe)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, app.Recipe, "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.Recipe, 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)
|
|
}
|
|
config.ExposeAllEnv(stackName, compose, app.Env)
|
|
config.SetRecipeLabel(compose, stackName, app.Recipe)
|
|
config.SetChaosLabel(compose, stackName, internal.Chaos)
|
|
config.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
|
config.SetUpdateLabel(compose, stackName, app.Env)
|
|
|
|
if err := internal.NewVersionOverview(app, deployedVersion, chosenDowngrade, ""); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := stack.RunDeploy(cl, deployOpts, compose, stackName, internal.DontWaitConverge); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|