Catalogue package had to be merged into the recipe package due to too many circular import errors. Also, use https url for cloning, assume folks don't have ssh setup by default (the whole reason for the refactor).
188 lines
4.8 KiB
Go
188 lines
4.8 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"coopcloud.tech/abra/pkg/autocomplete"
|
|
"coopcloud.tech/abra/pkg/config"
|
|
"coopcloud.tech/abra/pkg/lint"
|
|
"coopcloud.tech/abra/pkg/recipe"
|
|
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/v2"
|
|
)
|
|
|
|
var appRollbackCommand = &cli.Command{
|
|
Name: "rollback",
|
|
Usage: "Roll an app back to a previous version",
|
|
Aliases: []string{"rl"},
|
|
ArgsUsage: "<app>",
|
|
Flags: []cli.Flag{
|
|
internal.ForceFlag,
|
|
internal.ChaosFlag,
|
|
internal.DontWaitConvergeFlag,
|
|
},
|
|
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 - see "abra app backup <app>" for more.
|
|
|
|
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()
|
|
|
|
if err := recipe.EnsureUpToDate(app.Type); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
r, err := recipe.Get(app.Type)
|
|
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(c.Context, 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.Type, catl)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
var availableDowngrades []string
|
|
if deployedVersion == "" {
|
|
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
|
|
}
|
|
}
|
|
|
|
// 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.Chaos {
|
|
if internal.Force {
|
|
chosenDowngrade = availableDowngrades[0]
|
|
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: availableDowngrades,
|
|
}
|
|
if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if !internal.Chaos {
|
|
if err := recipe.EnsureVersion(app.Type, chosenDowngrade); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if internal.Chaos {
|
|
logrus.Warn("chaos mode engaged")
|
|
var err error
|
|
chosenDowngrade, err = recipe.ChaosVersion(app.Type)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_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 := internal.NewVersionOverview(app, deployedVersion, chosenDowngrade); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if err := stack.RunDeploy(cl, deployOpts, compose, app.Type, internal.DontWaitConverge); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|