forked from toolshed/abra
.gitea
cli
app
app.go
backup.go
check.go
cmd.go
cmd_test.go
config.go
cp.go
deploy.go
errors.go
list.go
logs.go
new.go
ps.go
remove.go
restart.go
restore.go
rollback.go
run.go
secret.go
undeploy.go
upgrade.go
version.go
volume.go
catalogue
internal
recipe
record
server
cli.go
cmd
pkg
scripts
tests
.drone.yml
.e2e.env.sample
.envrc.sample
.gitignore
.goreleaser.yml
AUTHORS.md
LICENSE
Makefile
README.md
go.mod
go.sum
renovate.json
205 lines
5.4 KiB
Go
205 lines
5.4 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"coopcloud.tech/abra/cli/internal"
|
|
"coopcloud.tech/abra/pkg/autocomplete"
|
|
"coopcloud.tech/abra/pkg/client"
|
|
"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"
|
|
"github.com/AlecAivazis/survey/v2"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var appUpgradeCommand = cli.Command{
|
|
Name: "upgrade",
|
|
Aliases: []string{"up"},
|
|
Usage: "Upgrade an app",
|
|
ArgsUsage: "<domain>",
|
|
Flags: []cli.Flag{
|
|
internal.DebugFlag,
|
|
internal.NoInputFlag,
|
|
internal.ForceFlag,
|
|
internal.ChaosFlag,
|
|
internal.NoDomainChecksFlag,
|
|
internal.DontWaitConvergeFlag,
|
|
},
|
|
Before: internal.SubCommandBefore,
|
|
Description: `
|
|
Upgrade an app. You can use it to choose and roll out a new upgrade to an
|
|
existing app.
|
|
|
|
This command specifically supports incrementing the version of running apps, as
|
|
opposed to "abra app deploy <domain>" which will not change the version of a
|
|
deployed app.
|
|
|
|
You may pass "--force/-f" to upgrade 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.
|
|
`,
|
|
Action: func(c *cli.Context) error {
|
|
app := internal.ValidateApp(c)
|
|
stackName := app.StackName()
|
|
|
|
if !internal.Chaos {
|
|
if err := recipe.EnsureUpToDate(app.Recipe); 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)
|
|
}
|
|
|
|
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 availableUpgrades []string
|
|
if deployedVersion == "unknown" {
|
|
availableUpgrades = 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.IsGreaterThan(parsedDeployedVersion) {
|
|
availableUpgrades = append(availableUpgrades, version)
|
|
}
|
|
}
|
|
|
|
if len(availableUpgrades) == 0 && !internal.Force {
|
|
logrus.Infof("no available upgrades, you're on latest (%s) ✌️", deployedVersion)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
availableUpgrades = internal.ReverseStringList(availableUpgrades)
|
|
|
|
var chosenUpgrade string
|
|
if len(availableUpgrades) > 0 && !internal.Chaos {
|
|
if internal.Force || internal.NoInput {
|
|
chosenUpgrade = availableUpgrades[len(availableUpgrades)-1]
|
|
logrus.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
|
|
} else {
|
|
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 release notes written after git tag published, read them before we
|
|
// check out the tag and then they'll appear to be missing. this covers
|
|
// when we obviously will forget to write release notes before publishing
|
|
releaseNotes, err := internal.GetReleaseNotes(app.Recipe, chosenUpgrade)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !internal.Chaos {
|
|
if err := recipe.EnsureVersion(app.Recipe, chosenUpgrade); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if internal.Chaos {
|
|
logrus.Warn("chaos mode engaged")
|
|
var err error
|
|
chosenUpgrade, 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)
|
|
}
|
|
|
|
if err := internal.NewVersionOverview(app, deployedVersion, chosenUpgrade, releaseNotes); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := stack.RunDeploy(cl, deployOpts, compose, app.StackName(), internal.DontWaitConverge); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
BashComplete: autocomplete.AppNameComplete,
|
|
}
|