feat: git hash arg for deploy #434
@ -9,7 +9,6 @@ var AppCommand = cli.Command{
|
|||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
Usage: "Manage apps",
|
Usage: "Manage apps",
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain>",
|
||||||
Description: "Functionality for managing the life cycle of your apps",
|
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
appBackupCommand,
|
appBackupCommand,
|
||||||
appCheckCommand,
|
appCheckCommand,
|
||||||
|
@ -29,10 +29,11 @@ They can be run within the context of a service (e.g. app) or locally on your
|
|||||||
work station by passing "--local". Arguments can be passed into these functions
|
work station by passing "--local". Arguments can be passed into these functions
|
||||||
using the "-- <args>" syntax.
|
using the "-- <args>" syntax.
|
||||||
|
|
||||||
Example:
|
**WARNING**: options must be passed directly after the sub-command "cmd".
|
||||||
|
|
||||||
abra app cmd example.com app create_user -- me@example.com
|
EXAMPLE:
|
||||||
`,
|
|
||||||
|
abra app cmd --local example.com app create_user -- me@example.com`,
|
||||||
ArgsUsage: "<domain> [<service>] <command> [-- <args>]",
|
ArgsUsage: "<domain> [<service>] <command> [-- <args>]",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
|
@ -43,7 +43,7 @@ var appConfigCommand = cli.Command{
|
|||||||
ed, ok := os.LookupEnv("EDITOR")
|
ed, ok := os.LookupEnv("EDITOR")
|
||||||
if !ok {
|
if !ok {
|
||||||
edPrompt := &survey.Select{
|
edPrompt := &survey.Select{
|
||||||
Message: "Which editor do you wish to use?",
|
Message: "which editor do you wish to use?",
|
||||||
Options: []string{"vi", "vim", "nvim", "nano", "pico", "emacs"},
|
Options: []string{"vi", "vim", "nvim", "nano", "pico", "emacs"},
|
||||||
}
|
}
|
||||||
if err := survey.AskOne(edPrompt, &ed); err != nil {
|
if err := survey.AskOne(edPrompt, &ed); err != nil {
|
||||||
|
@ -35,17 +35,18 @@ var appDeployCommand = cli.Command{
|
|||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Description: `
|
Description: `Deploy an app.
|
||||||
Deploy an app. It does not support incrementing the version of a deployed app,
|
|
||||||
for this you need to look at the "abra app upgrade <domain>" command.
|
|
||||||
|
|
||||||
You may pass "--force" to re-deploy the same version again. This can be useful
|
This command supports chaos operations. Use "--chaos" to deploy your recipe
|
||||||
if the container runtime has gotten into a weird state.
|
checkout as-is. Recipe commit hashes are also supported values for
|
||||||
|
"[<version>]". Please note, "upgrade"/"rollback" do not support chaos
|
||||||
|
operations.
|
||||||
|
|
||||||
Chaos mode ("--chaos") will deploy your local checkout of a recipe as-is,
|
EXAMPLE:
|
||||||
including unstaged changes and can be useful for live hacking and testing new
|
|
||||||
recipes.
|
abra app deploy foo.example.com
|
||||||
`,
|
abra app deploy foo.example.com 1.2.3+3.2.1
|
||||||
|
abra app deploy foo.example.com 1e83340e`,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
@ -76,6 +77,9 @@ recipes.
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE(d1): handles "<version> as git hash" use case
|
||||||
|
var isChaosCommit bool
|
||||||
|
|
||||||
// NOTE(d1): check out specific version before dealing with secrets. This
|
// NOTE(d1): check out specific version before dealing with secrets. This
|
||||||
// is because we need to deal with GetComposeFiles under the hood and these
|
// is because we need to deal with GetComposeFiles under the hood and these
|
||||||
// files change from version to version which therefore affects which
|
// files change from version to version which therefore affects which
|
||||||
@ -84,9 +88,17 @@ recipes.
|
|||||||
if specificVersion != "" {
|
if specificVersion != "" {
|
||||||
version = specificVersion
|
version = specificVersion
|
||||||
log.Debugf("choosing %s as version to deploy", version)
|
log.Debugf("choosing %s as version to deploy", version)
|
||||||
if err := app.Recipe.EnsureVersion(version); err != nil {
|
|
||||||
|
var err error
|
||||||
|
isChaosCommit, err = app.Recipe.EnsureVersion(version)
|
||||||
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isChaosCommit {
|
||||||
|
log.Debugf("assuming '%s' is a chaos commit", version)
|
||||||
|
internal.Chaos = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
secStats, err := secret.PollSecretsStatus(cl, app)
|
secStats, err := secret.PollSecretsStatus(cl, app)
|
||||||
@ -119,10 +131,10 @@ recipes.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(versions) == 0 && !internal.Chaos {
|
if len(versions) == 0 && !internal.Chaos {
|
||||||
log.Warn("no published versions in catalogue, trying local recipe repository")
|
log.Debug("no published versions in catalogue, trying local recipe repository")
|
||||||
recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline)
|
recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
for _, recipeVersion := range recipeVersions {
|
for _, recipeVersion := range recipeVersions {
|
||||||
for version := range recipeVersion {
|
for version := range recipeVersion {
|
||||||
@ -134,7 +146,7 @@ recipes.
|
|||||||
if len(versions) > 0 && !internal.Chaos {
|
if len(versions) > 0 && !internal.Chaos {
|
||||||
version = versions[len(versions)-1]
|
version = versions[len(versions)-1]
|
||||||
log.Debugf("choosing %s as version to deploy", version)
|
log.Debugf("choosing %s as version to deploy", version)
|
||||||
if err := app.Recipe.EnsureVersion(version); err != nil {
|
if _, err := app.Recipe.EnsureVersion(version); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -150,12 +162,22 @@ recipes.
|
|||||||
chaosVersion := "false"
|
chaosVersion := "false"
|
||||||
if internal.Chaos {
|
if internal.Chaos {
|
||||||
log.Warnf("chaos mode engaged")
|
log.Warnf("chaos mode engaged")
|
||||||
|
|
||||||
|
if isChaosCommit {
|
||||||
|
chaosVersion = specificVersion
|
||||||
|
versionLabelLocal, err := app.Recipe.GetVersionLabelLocal()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
version = versionLabelLocal
|
||||||
|
} else {
|
||||||
var err error
|
var err error
|
||||||
chaosVersion, err = app.Recipe.ChaosVersion()
|
chaosVersion, err = app.Recipe.ChaosVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
|
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,8 +77,7 @@ generate a report of all your apps.
|
|||||||
|
|
||||||
By passing the "--status/-S" flag, you can query all your servers for the
|
By passing the "--status/-S" flag, you can query all your servers for the
|
||||||
actual live deployment status. Depending on how many servers you manage, this
|
actual live deployment status. Depending on how many servers you manage, this
|
||||||
can take some time.
|
can take some time.`,
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.MachineReadableFlag,
|
internal.MachineReadableFlag,
|
||||||
|
@ -19,8 +19,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var appNewDescription = `
|
var appNewDescription = `
|
||||||
Take a recipe and uses it to create a new app. This new app configuration is
|
Creates a new app from a default recipe. This new app configuration is stored
|
||||||
stored in your ~/.abra directory under the appropriate server.
|
in your $ABRA_DIR directory under the appropriate server.
|
||||||
|
|
||||||
This command does not deploy your app for you. You will need to run "abra app
|
This command does not deploy your app for you. You will need to run "abra app
|
||||||
deploy <domain>" to do so.
|
deploy <domain>" to do so.
|
||||||
@ -35,8 +35,7 @@ store them somewhere safe.
|
|||||||
|
|
||||||
You can use the "--pass/-P" to store these generated passwords locally in a
|
You can use the "--pass/-P" to store these generated passwords locally in a
|
||||||
pass store (see passwordstore.org for more). The pass command must be available
|
pass store (see passwordstore.org for more). The pass command must be available
|
||||||
on your $PATH.
|
on your $PATH.`
|
||||||
`
|
|
||||||
|
|
||||||
var appNewCommand = cli.Command{
|
var appNewCommand = cli.Command{
|
||||||
Name: "new",
|
Name: "new",
|
||||||
@ -92,7 +91,7 @@ var appNewCommand = cli.Command{
|
|||||||
version = tag
|
version = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := recipe.EnsureVersion(version); err != nil {
|
if _, err := recipe.EnsureVersion(version); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -101,7 +100,7 @@ var appNewCommand = cli.Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := recipe.EnsureVersion(c.Args().Get(1)); err != nil {
|
if _, err := recipe.EnsureVersion(c.Args().Get(1)); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,22 +173,25 @@ var appNewCommand = cli.Command{
|
|||||||
table := formatter.CreateTable(tableCol)
|
table := formatter.CreateTable(tableCol)
|
||||||
table.Append([]string{internal.NewAppServer, recipe.Name, internal.Domain})
|
table.Append([]string{internal.NewAppServer, recipe.Name, internal.Domain})
|
||||||
|
|
||||||
fmt.Println(fmt.Sprintf("A new %s app has been created! Here is an overview:", recipe.Name))
|
log.Infof("new app '%s' created 🌞", recipe.Name)
|
||||||
|
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
table.Render()
|
table.Render()
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
fmt.Println("You can configure this app by running the following:")
|
|
||||||
|
fmt.Println("Configure this app:")
|
||||||
fmt.Println(fmt.Sprintf("\n abra app config %s", internal.Domain))
|
fmt.Println(fmt.Sprintf("\n abra app config %s", internal.Domain))
|
||||||
|
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
fmt.Println("You can deploy this app by running the following:")
|
fmt.Println("Deploy this app:")
|
||||||
fmt.Println(fmt.Sprintf("\n abra app deploy %s", internal.Domain))
|
fmt.Println(fmt.Sprintf("\n abra app deploy %s", internal.Domain))
|
||||||
|
|
||||||
if len(secrets) > 0 {
|
if len(secrets) > 0 {
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
fmt.Println("Here are your generated secrets:")
|
fmt.Println("Generated secrets:")
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
secretTable.Render()
|
secretTable.Render()
|
||||||
log.Warn("generated secrets are not shown again, please take note of them NOW")
|
log.Warn("Generated secrets are not shown again, please take note of them NOW")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -55,7 +55,7 @@ var appPsCommand = cli.Command{
|
|||||||
if statusMeta, ok := statuses[app.StackName()]; ok {
|
if statusMeta, ok := statuses[app.StackName()]; ok {
|
||||||
isChaos, exists := statusMeta["chaos"]
|
isChaos, exists := statusMeta["chaos"]
|
||||||
if exists && isChaos == "false" {
|
if exists && isChaos == "false" {
|
||||||
if err := app.Recipe.EnsureVersion(deployMeta.Version); err != nil {
|
if _, err := app.Recipe.EnsureVersion(deployMeta.Version); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -36,8 +36,7 @@ Please note, if you delete the local app env file without removing volumes and
|
|||||||
secrets first, Abra will *not* be able to help you remove them afterwards.
|
secrets first, Abra will *not* be able to help you remove them afterwards.
|
||||||
|
|
||||||
To delete everything without prompt, use the "--force/-f" or the "--no-input/n"
|
To delete everything without prompt, use the "--force/-f" or the "--no-input/n"
|
||||||
flag.
|
flag.`,
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.ForceFlag,
|
internal.ForceFlag,
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
|
@ -33,10 +33,9 @@ Run "abra app ps <domain>" to see a list of service names.
|
|||||||
|
|
||||||
Pass "--all-services/-a" to restart all services.
|
Pass "--all-services/-a" to restart all services.
|
||||||
|
|
||||||
Example:
|
EXAMPLE:
|
||||||
|
|
||||||
abra app restart example.com app
|
abra app restart example.com app`,
|
||||||
`,
|
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
|
@ -34,14 +34,18 @@ var appRollbackCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Description: `
|
Description: `
|
||||||
This command rolls an app back to a previous version if one exists.
|
This command rolls an app back to a previous version.
|
||||||
|
|
||||||
You may pass "--force/-f" to downgrade to the same version again. This can be
|
Unlike "deploy", chaos operations are not supported here. Only recipe versions
|
||||||
useful if the container runtime has gotten into a weird state.
|
are supported values for "[<version>]".
|
||||||
|
|
||||||
This action could be destructive, please ensure you have a copy of your app
|
A rollback can be destructive, please ensure you have a copy of your app data
|
||||||
data beforehand.
|
beforehand.
|
||||||
`,
|
|
||||||
|
EXAMPLE:
|
||||||
|
|
||||||
|
abra app rollback foo.example.com
|
||||||
|
abra app rollback foo.example.com 1.2.3+3.2.1`,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
@ -82,10 +86,10 @@ data beforehand.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
log.Warn("no published versions in catalogue, trying local recipe repository")
|
log.Debug("no published versions in catalogue, trying local recipe repository")
|
||||||
recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline)
|
recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, recipeVersion := range recipeVersions {
|
for _, recipeVersion := range recipeVersions {
|
||||||
@ -105,15 +109,19 @@ data beforehand.
|
|||||||
if specificVersion != "" {
|
if specificVersion != "" {
|
||||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||||
|
log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||||
log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +129,7 @@ data beforehand.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if deployMeta.Version != "unknown" && specificVersion == "" {
|
if deployMeta.Version != "unknown" && specificVersion == "" {
|
||||||
if deployMeta.IsChaos == "true" {
|
if deployMeta.IsChaos {
|
||||||
log.Warn("attempting to rollback a chaos deployment")
|
log.Warn("attempting to rollback a chaos deployment")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +162,7 @@ data beforehand.
|
|||||||
log.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade)
|
log.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade)
|
||||||
} else {
|
} else {
|
||||||
msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version)
|
msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version)
|
||||||
if deployMeta.IsChaos == "true" {
|
if deployMeta.IsChaos {
|
||||||
msg = fmt.Sprintf("please select a downgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
msg = fmt.Sprintf("please select a downgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +178,7 @@ data beforehand.
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("choosing %s as version to rollback", chosenDowngrade)
|
log.Debugf("choosing %s as version to rollback", chosenDowngrade)
|
||||||
if err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil {
|
if _, err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,8 +214,8 @@ data beforehand.
|
|||||||
appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
||||||
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
||||||
|
|
||||||
chaosVersion := deployMeta.IsChaos
|
chaosVersion := "false"
|
||||||
if deployMeta.IsChaos == "true" {
|
if deployMeta.IsChaos {
|
||||||
chaosVersion = deployMeta.ChaosVersion
|
chaosVersion = deployMeta.ChaosVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +78,7 @@ This does not destroy any of the application data.
|
|||||||
However, you should remain vigilant, as your swarm installation will consider
|
However, you should remain vigilant, as your swarm installation will consider
|
||||||
any previously attached volumes as eligible for pruning once undeployed.
|
any previously attached volumes as eligible for pruning once undeployed.
|
||||||
|
|
||||||
Passing "-p/--prune" does not remove those volumes.
|
Passing "-p/--prune" does not remove those volumes.`,
|
||||||
`,
|
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
stackName := app.StackName()
|
stackName := app.StackName()
|
||||||
@ -100,8 +99,8 @@ Passing "-p/--prune" does not remove those volumes.
|
|||||||
log.Fatalf("%s is not deployed?", app.Name)
|
log.Fatalf("%s is not deployed?", app.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
chaosVersion := deployMeta.IsChaos
|
chaosVersion := "false"
|
||||||
if deployMeta.IsChaos == "true" {
|
if deployMeta.IsChaos {
|
||||||
chaosVersion = deployMeta.ChaosVersion
|
chaosVersion = deployMeta.ChaosVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,15 +34,18 @@ var appUpgradeCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Description: `
|
Description: `
|
||||||
Upgrade an app. You can use it to choose and roll out a new upgrade to a
|
Upgrade an app.
|
||||||
deployed app.
|
|
||||||
|
|
||||||
You may pass "--force/-f" to upgrade to the same version again. This can be
|
Unlike "deploy", chaos operations are not supported here. Only recipe versions
|
||||||
useful if the container runtime has gotten into a weird state.
|
are supported values for "[<version>]".
|
||||||
|
|
||||||
This action could be destructive, please ensure you have a copy of your app
|
An upgrade can be destructive, please ensure you have a copy of your app data
|
||||||
data beforehand.
|
beforehand.
|
||||||
`,
|
|
||||||
|
EXAMPLE:
|
||||||
|
|
||||||
|
abra app upgrade foo.example.com
|
||||||
|
abra app upgrade foo.example.com 1.2.3+3.2.1`,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
app := internal.ValidateApp(c)
|
app := internal.ValidateApp(c)
|
||||||
@ -83,10 +86,10 @@ data beforehand.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
log.Warn("no published versions in catalogue, trying local recipe repository")
|
log.Debug("no published versions in catalogue, trying local recipe repository")
|
||||||
recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline)
|
recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
for _, recipeVersion := range recipeVersions {
|
for _, recipeVersion := range recipeVersions {
|
||||||
for version := range recipeVersion {
|
for version := range recipeVersion {
|
||||||
@ -105,15 +108,21 @@ data beforehand.
|
|||||||
if specificVersion != "" {
|
if specificVersion != "" {
|
||||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)
|
||||||
}
|
}
|
||||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||||
}
|
}
|
||||||
if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
|
||||||
|
if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||||
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||||
|
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||||
|
}
|
||||||
|
|
||||||
availableUpgrades = append(availableUpgrades, specificVersion)
|
availableUpgrades = append(availableUpgrades, specificVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +132,7 @@ data beforehand.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if deployMeta.Version != "unknown" && specificVersion == "" {
|
if deployMeta.Version != "unknown" && specificVersion == "" {
|
||||||
if deployMeta.IsChaos == "true" {
|
if deployMeta.IsChaos {
|
||||||
log.Warn("attempting to upgrade a chaos deployment")
|
log.Warn("attempting to upgrade a chaos deployment")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +159,7 @@ data beforehand.
|
|||||||
log.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
|
log.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
|
||||||
} else {
|
} else {
|
||||||
msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version)
|
msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version)
|
||||||
if deployMeta.IsChaos == "true" {
|
if deployMeta.IsChaos {
|
||||||
msg = fmt.Sprintf("please select an upgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
msg = fmt.Sprintf("please select an upgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +206,7 @@ data beforehand.
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("choosing %s as version to upgrade", chosenUpgrade)
|
log.Debugf("choosing %s as version to upgrade", chosenUpgrade)
|
||||||
if err := app.Recipe.EnsureVersion(chosenUpgrade); err != nil {
|
if _, err := app.Recipe.EnsureVersion(chosenUpgrade); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,8 +259,8 @@ data beforehand.
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
chaosVersion := deployMeta.IsChaos
|
chaosVersion := "false"
|
||||||
if deployMeta.IsChaos == "true" {
|
if deployMeta.IsChaos {
|
||||||
chaosVersion = deployMeta.ChaosVersion
|
chaosVersion = deployMeta.ChaosVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +73,7 @@ The command is interactive and will show a multiple select input which allows
|
|||||||
you to make a seclection. Use the "?" key to see more help on navigating this
|
you to make a seclection. Use the "?" key to see more help on navigating this
|
||||||
interface.
|
interface.
|
||||||
|
|
||||||
Passing "--force/-f" will select all volumes for removal. Be careful.
|
Passing "--force/-f" will select all volumes for removal. Be careful.`,
|
||||||
`,
|
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain>",
|
||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
@ -33,14 +33,7 @@ var catalogueGenerateCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Description: `
|
Description: `
|
||||||
Generate a new copy of the recipe catalogue which can be found on:
|
Generate a new copy of the recipe catalogue.
|
||||||
|
|
||||||
https://recipes.coopcloud.tech (website that humans read)
|
|
||||||
https://recipes.coopcloud.tech/recipes.json (JSON that Abra reads)
|
|
||||||
|
|
||||||
It polls the entire git.coopcloud.tech/coop-cloud/... recipe repository
|
|
||||||
listing, parses README.md and git tags to produce recipe metadata which is
|
|
||||||
loaded into the catalogue JSON file.
|
|
||||||
|
|
||||||
It is possible to generate new metadata for a single recipe by passing
|
It is possible to generate new metadata for a single recipe by passing
|
||||||
<recipe>. The existing local catalogue will be updated, not overwritten.
|
<recipe>. The existing local catalogue will be updated, not overwritten.
|
||||||
@ -51,8 +44,7 @@ If you have a Hub account you can have Abra log you in to avoid this. Pass
|
|||||||
|
|
||||||
Push your new release to git.coopcloud.tech with "-p/--publish". This requires
|
Push your new release to git.coopcloud.tech with "-p/--publish". This requires
|
||||||
that you have permission to git push to these repositories and have your SSH
|
that you have permission to git push to these repositories and have your SSH
|
||||||
keys configured on your account.
|
keys configured on your account.`,
|
||||||
`,
|
|
||||||
ArgsUsage: "[<recipe>]",
|
ArgsUsage: "[<recipe>]",
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
@ -217,7 +209,6 @@ var CatalogueCommand = cli.Command{
|
|||||||
Usage: "Manage the recipe catalogue",
|
Usage: "Manage the recipe catalogue",
|
||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
Description: "This command helps recipe packagers interact with the recipe catalogue",
|
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
catalogueGenerateCommand,
|
catalogueGenerateCommand,
|
||||||
},
|
},
|
||||||
|
41
cli/cli.go
41
cli/cli.go
@ -25,16 +25,15 @@ import (
|
|||||||
var AutoCompleteCommand = cli.Command{
|
var AutoCompleteCommand = cli.Command{
|
||||||
Name: "autocomplete",
|
Name: "autocomplete",
|
||||||
Aliases: []string{"ac"},
|
Aliases: []string{"ac"},
|
||||||
Usage: "Configure shell autocompletion (recommended)",
|
Usage: "Configure shell autocompletion",
|
||||||
Description: `
|
Description: `
|
||||||
Set up auto-completion in your shell by downloading the relevant files and
|
Set up shell auto-completion.
|
||||||
laying out what additional information must be loaded. Supported shells are as
|
|
||||||
follows: bash, fish, fizsh & zsh.
|
|
||||||
|
|
||||||
Example:
|
Supported shells are: bash, fish, fizsh & zsh.
|
||||||
|
|
||||||
abra autocomplete bash
|
EXAMPLE:
|
||||||
`,
|
|
||||||
|
abra autocomplete bash`,
|
||||||
ArgsUsage: "<shell>",
|
ArgsUsage: "<shell>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
@ -81,7 +80,7 @@ Example:
|
|||||||
switch shellType {
|
switch shellType {
|
||||||
case "bash":
|
case "bash":
|
||||||
fmt.Println(fmt.Sprintf(`
|
fmt.Println(fmt.Sprintf(`
|
||||||
# Run the following commands to install auto-completion
|
# run the following commands to install auto-completion
|
||||||
sudo mkdir /etc/bash_completion.d/
|
sudo mkdir /etc/bash_completion.d/
|
||||||
sudo cp %s /etc/bash_completion.d/abra
|
sudo cp %s /etc/bash_completion.d/abra
|
||||||
echo "source /etc/bash_completion.d/abra" >> ~/.bashrc
|
echo "source /etc/bash_completion.d/abra" >> ~/.bashrc
|
||||||
@ -89,19 +88,19 @@ echo "source /etc/bash_completion.d/abra" >> ~/.bashrc
|
|||||||
`, autocompletionFile))
|
`, autocompletionFile))
|
||||||
case "zsh":
|
case "zsh":
|
||||||
fmt.Println(fmt.Sprintf(`
|
fmt.Println(fmt.Sprintf(`
|
||||||
# Run the following commands to install auto-completion
|
# run the following commands to install auto-completion
|
||||||
sudo mkdir /etc/zsh/completion.d/
|
sudo mkdir /etc/zsh/completion.d/
|
||||||
sudo cp %s /etc/zsh/completion.d/abra
|
sudo cp %s /etc/zsh/completion.d/abra
|
||||||
echo "PROG=abra\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/completion.d/abra" >> ~/.zshrc
|
echo "PROG=abra\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/completion.d/abra" >> ~/.zshrc
|
||||||
# To test, run the following: "abra app <hit tab key>" - you should see command completion!
|
# to test, run the following: "abra app <hit tab key>" - you should see command completion!
|
||||||
`, autocompletionFile))
|
`, autocompletionFile))
|
||||||
case "fish":
|
case "fish":
|
||||||
fmt.Println(fmt.Sprintf(`
|
fmt.Println(fmt.Sprintf(`
|
||||||
# Run the following commands to install auto-completion
|
# run the following commands to install auto-completion
|
||||||
sudo mkdir -p /etc/fish/completions
|
sudo mkdir -p /etc/fish/completions
|
||||||
sudo cp %s /etc/fish/completions/abra
|
sudo cp %s /etc/fish/completions/abra
|
||||||
echo "source /etc/fish/completions/abra" >> ~/.config/fish/config.fish
|
echo "source /etc/fish/completions/abra" >> ~/.config/fish/config.fish
|
||||||
# To test, run the following: "abra app <hit tab key>" - you should see command completion!
|
# to test, run the following: "abra app <hit tab key>" - you should see command completion!
|
||||||
`, autocompletionFile))
|
`, autocompletionFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,14 +112,18 @@ echo "source /etc/fish/completions/abra" >> ~/.config/fish/config.fish
|
|||||||
var UpgradeCommand = cli.Command{
|
var UpgradeCommand = cli.Command{
|
||||||
Name: "upgrade",
|
Name: "upgrade",
|
||||||
Aliases: []string{"u"},
|
Aliases: []string{"u"},
|
||||||
Usage: "Upgrade Abra itself",
|
Usage: "Upgrade abra",
|
||||||
Description: `
|
Description: `
|
||||||
Upgrade Abra in-place with the latest stable or release candidate.
|
Upgrade abra in-place with the latest stable or release candidate.
|
||||||
|
|
||||||
Pass "-r/--rc" to install the latest release candidate. Please bear in mind
|
Use "-r/--rc" to install the latest release candidate. Please bear in mind that
|
||||||
that it may contain catastrophic bugs. Thank you very much for the testing
|
it may contain absolutely catastrophic deal-breaker bugs. Thank you very much
|
||||||
efforts!
|
for the testing efforts 💗
|
||||||
`,
|
|
||||||
|
EXAMPLE:
|
||||||
|
|
||||||
|
abra upgrade
|
||||||
|
abra upgrade --rc`,
|
||||||
Flags: []cli.Flag{internal.RCFlag},
|
Flags: []cli.Flag{internal.RCFlag},
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
mainURL := "https://install.abra.coopcloud.tech"
|
mainURL := "https://install.abra.coopcloud.tech"
|
||||||
@ -144,7 +147,7 @@ efforts!
|
|||||||
func newAbraApp(version, commit string) *cli.App {
|
func newAbraApp(version, commit string) *cli.App {
|
||||||
app := &cli.App{
|
app := &cli.App{
|
||||||
Name: "abra",
|
Name: "abra",
|
||||||
Usage: `The Co-op Cloud command-line utility belt 🎩🐇
|
Usage: `the Co-op Cloud command-line utility belt 🎩🐇
|
||||||
____ ____ _ _
|
____ ____ _ _
|
||||||
/ ___|___ ___ _ __ / ___| | ___ _ _ __| |
|
/ ___|___ ___ _ __ / ___| | ___ _ _ __| |
|
||||||
| | / _ \ _____ / _ \| '_ \ | | | |/ _ \| | | |/ _' |
|
| | / _ \ _____ / _ \| '_ \ | | | |/ _ \| | | |/ _' |
|
||||||
|
@ -5,14 +5,13 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||||
"coopcloud.tech/abra/pkg/log"
|
"coopcloud.tech/abra/pkg/log"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var recipeDiffCommand = cli.Command{
|
var recipeDiffCommand = cli.Command{
|
||||||
Name: "diff",
|
Name: "diff",
|
||||||
Usage: "Show unstaged changes in recipe config",
|
Usage: "Show unstaged changes in recipe config",
|
||||||
Description: "Due to limitations in our underlying Git dependency, this command requires /usr/bin/git.",
|
Description: "This command requires /usr/bin/git.",
|
||||||
Aliases: []string{"d"},
|
Aliases: []string{"d"},
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@ -22,12 +21,7 @@ var recipeDiffCommand = cli.Command{
|
|||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
recipeName := c.Args().First()
|
r := internal.ValidateRecipe(c)
|
||||||
r := recipe.Get(recipeName)
|
|
||||||
|
|
||||||
if recipeName != "" {
|
|
||||||
internal.ValidateRecipe(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gitPkg.DiffUnstaged(r.Dir); err != nil {
|
if err := gitPkg.DiffUnstaged(r.Dir); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -48,12 +48,7 @@ Create a new recipe.
|
|||||||
|
|
||||||
Abra uses the built-in example repository which is available here:
|
Abra uses the built-in example repository which is available here:
|
||||||
|
|
||||||
https://git.coopcloud.tech/coop-cloud/example
|
https://git.coopcloud.tech/coop-cloud/example`,
|
||||||
|
|
||||||
Files within the example repository make use of the Golang templating system
|
|
||||||
which Abra uses to inject values into the generated recipe folder (e.g. name of
|
|
||||||
recipe and domain in the sample environment config).
|
|
||||||
`,
|
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
recipeName := c.Args().First()
|
recipeName := c.Args().First()
|
||||||
r := recipe.Get(recipeName)
|
r := recipe.Get(recipeName)
|
||||||
@ -100,22 +95,8 @@ recipe and domain in the sample environment config).
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(fmt.Sprintf(`
|
log.Infof("new recipe '%s' created: %s", recipeName, path.Join(r.Dir))
|
||||||
Your new %s recipe has been created in %s.
|
log.Info("happy hacking 🎉")
|
||||||
|
|
||||||
In order to share your recipe, you can upload it the git repository to:
|
|
||||||
|
|
||||||
https://git.coopcloud.tech/coop-cloud/%s
|
|
||||||
|
|
||||||
If you're not sure how to do that, come chat with us:
|
|
||||||
|
|
||||||
https://docs.coopcloud.tech/intro/contact
|
|
||||||
|
|
||||||
See "abra recipe -h" for additional recipe maintainer commands.
|
|
||||||
|
|
||||||
Happy Hacking!
|
|
||||||
|
|
||||||
`, recipeName, path.Join(r.Dir), recipeName))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -18,9 +18,7 @@ for you.
|
|||||||
|
|
||||||
Anyone who uses a recipe can become a maintainer. Maintainers typically make
|
Anyone who uses a recipe can become a maintainer. Maintainers typically make
|
||||||
sure the recipe is in good working order and the config upgraded in a timely
|
sure the recipe is in good working order and the config upgraded in a timely
|
||||||
manner. Abra supports convenient automation for recipe maintainenace, see the
|
manner.`,
|
||||||
"abra recipe upgrade", "abra recipe sync" and "abra recipe release" commands.
|
|
||||||
`,
|
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
recipeFetchCommand,
|
recipeFetchCommand,
|
||||||
recipeLintCommand,
|
recipeLintCommand,
|
||||||
|
@ -45,8 +45,7 @@ major and therefore require intervention while doing the upgrade work.
|
|||||||
|
|
||||||
Publish your new release to git.coopcloud.tech with "-p/--publish". This
|
Publish your new release to git.coopcloud.tech with "-p/--publish". This
|
||||||
requires that you have permission to git push to these repositories and have
|
requires that you have permission to git push to these repositories and have
|
||||||
your SSH keys configured on your account.
|
your SSH keys configured on your account.`,
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
var recipeResetCommand = cli.Command{
|
var recipeResetCommand = cli.Command{
|
||||||
Name: "reset",
|
Name: "reset",
|
||||||
Usage: "Remove all unstaged changes from recipe config",
|
Usage: "Remove all unstaged changes from recipe config",
|
||||||
Description: "WARNING, this will delete your changes. Be Careful.",
|
Description: "WARNING: this will delete your changes. Be Careful.",
|
||||||
Aliases: []string{"rs"},
|
Aliases: []string{"rs"},
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
@ -37,8 +37,7 @@ named "app") which corresponds to the following format:
|
|||||||
|
|
||||||
Where <version> can be specifed on the command-line or Abra can attempt to
|
Where <version> can be specifed on the command-line or Abra can attempt to
|
||||||
auto-generate it for you. The <recipe> configuration will be updated on the
|
auto-generate it for you. The <recipe> configuration will be updated on the
|
||||||
local file system.
|
local file system.`,
|
||||||
`,
|
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
recipe := internal.ValidateRecipe(c)
|
recipe := internal.ValidateRecipe(c)
|
||||||
|
@ -53,10 +53,11 @@ The command is interactive and will show a select input which allows you to
|
|||||||
make a seclection. Use the "?" key to see more help on navigating this
|
make a seclection. Use the "?" key to see more help on navigating this
|
||||||
interface.
|
interface.
|
||||||
|
|
||||||
You may invoke this command in "wizard" mode and be prompted for input:
|
You may invoke this command in "wizard" mode and be prompted for input.
|
||||||
|
|
||||||
abra recipe upgrade
|
EXAMPLE:
|
||||||
`,
|
|
||||||
|
abra recipe upgrade`,
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
|
@ -28,7 +28,6 @@ var recipeVersionCommand = cli.Command{
|
|||||||
Aliases: []string{"v"},
|
Aliases: []string{"v"},
|
||||||
Usage: "List recipe versions",
|
Usage: "List recipe versions",
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
Description: "Versions are read from the recipe catalogue.",
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
@ -94,7 +94,7 @@ func createServerDir(name string) (bool, error) {
|
|||||||
var serverAddCommand = cli.Command{
|
var serverAddCommand = cli.Command{
|
||||||
Name: "add",
|
Name: "add",
|
||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
Usage: "Add a server to your configuration",
|
Usage: "Add a new server to your configuration",
|
||||||
Description: `
|
Description: `
|
||||||
Add a new server to your configuration so that it can be managed by Abra.
|
Add a new server to your configuration so that it can be managed by Abra.
|
||||||
|
|
||||||
@ -121,8 +121,7 @@ You can also pass "--no-domain-checks/-D" flag to use any arbitrary name
|
|||||||
instead of a real domain. The host will be resolved with the "Hostname" entry
|
instead of a real domain. The host will be resolved with the "Hostname" entry
|
||||||
of your ~/.ssh/config. Checks for a valid online domain will be skipped:
|
of your ~/.ssh/config. Checks for a valid online domain will be skipped:
|
||||||
|
|
||||||
abra server add -D example
|
abra server add -D example`,
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
|
@ -31,13 +31,12 @@ var volumesFilterFlag = &cli.BoolFlag{
|
|||||||
var serverPruneCommand = cli.Command{
|
var serverPruneCommand = cli.Command{
|
||||||
Name: "prune",
|
Name: "prune",
|
||||||
Aliases: []string{"p"},
|
Aliases: []string{"p"},
|
||||||
Usage: "Prune a managed server; Runs a docker system prune",
|
Usage: "Prune resources on a server",
|
||||||
Description: `
|
Description: `
|
||||||
Prunes unused containers, networks, and dangling images.
|
Prunes unused containers, networks, and dangling images.
|
||||||
|
|
||||||
If passing "-v/--volumes" then volumes not connected to a deployed app will
|
Use "-v/--volumes" to remove volumes that are not associated with a deployed
|
||||||
also be removed. This can result in unwanted data loss if not used carefully.
|
app. This can result in unwanted data loss if not used carefully.`,
|
||||||
`,
|
|
||||||
ArgsUsage: "[<server>]",
|
ArgsUsage: "[<server>]",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
allFilterFlag,
|
allFilterFlag,
|
||||||
|
@ -17,12 +17,12 @@ var serverRemoveCommand = cli.Command{
|
|||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
ArgsUsage: "<server>",
|
ArgsUsage: "<server>",
|
||||||
Usage: "Remove a managed server",
|
Usage: "Remove a managed server",
|
||||||
Description: `Remove a managed server.
|
Description: `
|
||||||
|
Remove a managed server.
|
||||||
|
|
||||||
Abra will remove the internal bookkeeping (~/.abra/servers/...) and underlying
|
Abra will remove the internal bookkeeping (~/.abra/servers/...) and underlying
|
||||||
client connection context. This server will then be lost in time, like tears in
|
client connection context. This server will then be lost in time, like tears in
|
||||||
rain.
|
rain.`,
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
|
@ -54,10 +54,12 @@ var Notify = cli.Command{
|
|||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Description: `
|
Description: `
|
||||||
It reads the deployed app versions and looks for new versions in the recipe
|
Read the deployed app versions and look for new versions in the recipe
|
||||||
catalogue. If a new patch/minor version is available, a notification is
|
catalogue.
|
||||||
printed. To include major versions use the --major flag.
|
|
||||||
`,
|
If a new patch/minor version is available, a notification is printed.
|
||||||
|
|
||||||
|
Use "--major" to include new major versions.`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
cl, err := client.New("default")
|
cl, err := client.New("default")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -103,14 +105,17 @@ var UpgradeApp = cli.Command{
|
|||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Description: `
|
Description: `
|
||||||
Upgrade an app by specifying its stack name and recipe. By passing "--all"
|
Upgrade an app by specifying stack name and recipe.
|
||||||
instead, every deployed app is upgraded. For each apps with enabled auto
|
|
||||||
updates the deployed version is compared with the current recipe catalogue
|
Use "--all" to upgrade every deployed app.
|
||||||
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
|
For each app with auto updates enabled, the deployed version is compared with
|
||||||
break things. Only apps that are not deployed with "--chaos" are upgraded, to
|
the current recipe catalogue version. If a new patch/minor version is
|
||||||
update chaos deployments use the "--chaos" flag. Use it with care.
|
available, the app is upgraded.
|
||||||
`,
|
|
||||||
|
To include major versions use the "--major" flag. You probably don't want that
|
||||||
|
as it will 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 {
|
Action: func(c *cli.Context) error {
|
||||||
cl, err := client.New("default")
|
cl, err := client.New("default")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -327,7 +332,7 @@ func processRecipeRepoVersion(r recipe.Recipe, version string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.EnsureVersion(version); err != nil {
|
if _, err := r.EnsureVersion(version); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,21 +57,23 @@ func (r Recipe) EnsureExists() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EnsureVersion checks whether a specific version exists for a recipe.
|
// EnsureVersion checks whether a specific version exists for a recipe.
|
||||||
func (r Recipe) EnsureVersion(version string) error {
|
func (r Recipe) EnsureVersion(version string) (bool, error) {
|
||||||
|
isChaosCommit := false
|
||||||
|
|
||||||
recipeDir := path.Join(config.RECIPES_DIR, r.Name)
|
recipeDir := path.Join(config.RECIPES_DIR, r.Name)
|
||||||
|
|
||||||
if err := gitPkg.EnsureGitRepo(recipeDir); err != nil {
|
if err := gitPkg.EnsureGitRepo(recipeDir); err != nil {
|
||||||
return err
|
return isChaosCommit, err
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := git.PlainOpen(recipeDir)
|
repo, err := git.PlainOpen(recipeDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return isChaosCommit, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, err := repo.Tags()
|
tags, err := repo.Tags()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return isChaosCommit, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsedTags []string
|
var parsedTags []string
|
||||||
@ -83,7 +85,7 @@ func (r Recipe) EnsureVersion(version string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return isChaosCommit, err
|
||||||
}
|
}
|
||||||
|
|
||||||
joinedTags := strings.Join(parsedTags, ", ")
|
joinedTags := strings.Join(parsedTags, ", ")
|
||||||
@ -91,27 +93,33 @@ func (r Recipe) EnsureVersion(version string) error {
|
|||||||
log.Debugf("read %s as tags for recipe %s", joinedTags, r.Name)
|
log.Debugf("read %s as tags for recipe %s", joinedTags, r.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var opts *git.CheckoutOptions
|
||||||
if tagRef.String() == "" {
|
if tagRef.String() == "" {
|
||||||
return fmt.Errorf("the local copy of %s doesn't seem to have version %s available?", r.Name, version)
|
log.Debugf("attempting to checkout '%s' as chaos commit", version)
|
||||||
|
|
||||||
|
hash, err := repo.ResolveRevision(plumbing.Revision(version))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unable to resolve '%s': %s", version, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = &git.CheckoutOptions{Hash: *hash, Create: false, Force: true}
|
||||||
|
isChaosCommit = true
|
||||||
|
} else {
|
||||||
|
opts = &git.CheckoutOptions{Branch: tagRef, Create: false, Force: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
worktree, err := repo.Worktree()
|
worktree, err := repo.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return isChaosCommit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := &git.CheckoutOptions{
|
|
||||||
Branch: tagRef,
|
|
||||||
Create: false,
|
|
||||||
Force: true,
|
|
||||||
}
|
|
||||||
if err := worktree.Checkout(opts); err != nil {
|
if err := worktree.Checkout(opts); err != nil {
|
||||||
return err
|
return isChaosCommit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), recipeDir)
|
log.Debugf("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), recipeDir)
|
||||||
|
|
||||||
return nil
|
return isChaosCommit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureIsClean makes sure that the recipe repository has no unstaged changes.
|
// EnsureIsClean makes sure that the recipe repository has no unstaged changes.
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ func GetDeployedServicesByName(ctx context.Context, cl *dockerClient.Client, sta
|
|||||||
type DeployMeta struct {
|
type DeployMeta struct {
|
||||||
IsDeployed bool // whether the app is deployed or not
|
IsDeployed bool // whether the app is deployed or not
|
||||||
Version string // the deployed version
|
Version string // the deployed version
|
||||||
IsChaos string // whether or not the deployment is --chaos
|
IsChaos bool // whether or not the deployment is --chaos
|
||||||
ChaosVersion string // the --chaos deployment version
|
ChaosVersion string // the --chaos deployment version
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ func IsDeployed(ctx context.Context, cl *dockerClient.Client, stackName string)
|
|||||||
deployMeta := DeployMeta{
|
deployMeta := DeployMeta{
|
||||||
IsDeployed: false,
|
IsDeployed: false,
|
||||||
Version: "unknown",
|
Version: "unknown",
|
||||||
IsChaos: "false", // NOTE(d1): match string type used on label
|
IsChaos: false,
|
||||||
ChaosVersion: "false", // NOTE(d1): match string type used on label
|
ChaosVersion: "false", // NOTE(d1): match string type used on label
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +138,11 @@ func IsDeployed(ctx context.Context, cl *dockerClient.Client, stackName string)
|
|||||||
|
|
||||||
labelKey = fmt.Sprintf("coop-cloud.%s.chaos", stackName)
|
labelKey = fmt.Sprintf("coop-cloud.%s.chaos", stackName)
|
||||||
if isChaos, ok := service.Spec.Labels[labelKey]; ok {
|
if isChaos, ok := service.Spec.Labels[labelKey]; ok {
|
||||||
deployMeta.IsChaos = isChaos
|
boolVal, err := strconv.ParseBool(isChaos)
|
||||||
|
if err != nil {
|
||||||
|
return deployMeta, fmt.Errorf("unable to parse '%s' value as bool: %s", labelKey, err)
|
||||||
|
}
|
||||||
|
deployMeta.IsChaos = boolVal
|
||||||
}
|
}
|
||||||
|
|
||||||
labelKey = fmt.Sprintf("coop-cloud.%s.chaos-version", stackName)
|
labelKey = fmt.Sprintf("coop-cloud.%s.chaos-version", stackName)
|
||||||
|
@ -356,3 +356,12 @@ teardown(){
|
|||||||
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all --chaos
|
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all --chaos
|
||||||
assert_success
|
assert_success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "deploy chaos commit" {
|
||||||
|
tagHash=$(_get_tag_hash "0.1.0+1.20.0")
|
||||||
|
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "$tagHash" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'chaos mode'
|
||||||
|
}
|
||||||
|
@ -111,6 +111,26 @@ teardown(){
|
|||||||
assert_output --partial "0.1.0+1.20.0"
|
assert_output --partial "0.1.0+1.20.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "force rollback to previous version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.1+1.20.2'
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "0.1.0+1.20.0"
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial "not a downgrade"
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||||
|
--no-input --no-converge-checks --force
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "0.1.0+1.20.0"
|
||||||
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@test "rollback to a version 2 tags behind" {
|
@test "rollback to a version 2 tags behind" {
|
||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
||||||
@ -145,3 +165,14 @@ teardown(){
|
|||||||
refute_output --partial "${tagHash:0:8}"
|
refute_output --partial "${tagHash:0:8}"
|
||||||
assert_output --partial "false"
|
assert_output --partial "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "chaos commit rollback not possible" {
|
||||||
|
_deploy_app
|
||||||
|
|
||||||
|
tagHash=$(_get_tag_hash "0.2.0+1.21.0")
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" "$tagHash" --no-input --no-converge-checks
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial "not a known version"
|
||||||
|
}
|
||||||
|
@ -97,6 +97,26 @@ teardown(){
|
|||||||
assert_output --partial '0.2.0+1.21.0'
|
assert_output --partial '0.2.0+1.21.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "force upgrade specific version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.2.0+1.21.0'
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'not an upgrade'
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||||
|
--no-input --no-converge-checks --force
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.2.0+1.21.0'
|
||||||
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@test "upgrade to latest" {
|
@test "upgrade to latest" {
|
||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
@ -189,3 +209,15 @@ teardown(){
|
|||||||
refute_output --partial "${tagHash:0:8}"
|
refute_output --partial "${tagHash:0:8}"
|
||||||
assert_output --partial "false"
|
assert_output --partial "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "chaos commit upgrade not possible" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
tagHash=$(_get_tag_hash "0.2.0+1.21.0")
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "$tagHash" --no-input --no-converge-checks
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial "not a known version"
|
||||||
|
}
|
||||||
|
@ -16,6 +16,14 @@ setup(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
teardown(){
|
teardown(){
|
||||||
|
if [[ -f "$ABRA_DIR/servers/$TEST_SERVER/foobar.$TEST_SERVER.env" ]]; then
|
||||||
|
run $ABRA app undeploy "foobar.$TEST_SERVER" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app rm "foobar.$TEST_SERVER" --no-input
|
||||||
|
assert_success
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -d "$ABRA_DIR/recipes/foobar" ]]; then
|
if [[ -d "$ABRA_DIR/recipes/foobar" ]]; then
|
||||||
run rm -rf "$ABRA_DIR/recipes/foobar"
|
run rm -rf "$ABRA_DIR/recipes/foobar"
|
||||||
assert_success
|
assert_success
|
||||||
@ -25,26 +33,32 @@ teardown(){
|
|||||||
@test "create new recipe" {
|
@test "create new recipe" {
|
||||||
run $ABRA recipe new foobar
|
run $ABRA recipe new foobar
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'Your new foobar recipe has been created'
|
assert_output --partial "new recipe 'foobar' created"
|
||||||
assert_exists "$ABRA_DIR/recipes/foobar"
|
assert_exists "$ABRA_DIR/recipes/foobar"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
@test "create new app from new recipe" {
|
@test "create new app from new recipe" {
|
||||||
run $ABRA recipe new foobar
|
run $ABRA recipe new foobar
|
||||||
assert_success
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/foobar"
|
||||||
|
|
||||||
run $ABRA app new foobar \
|
run $ABRA app new foobar \
|
||||||
--no-input \
|
--no-input \
|
||||||
--server "$TEST_SERVER" \
|
--server "$TEST_SERVER" \
|
||||||
--domain "foobar.$TEST_SERVER"
|
--domain "foobar.$TEST_SERVER"
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'A new foobar app has been created!'
|
assert_output --partial "new app 'foobar' created"
|
||||||
|
|
||||||
|
run $ABRA app deploy "foobar.$TEST_SERVER" --no-input
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'using latest commit'
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "create new recipe with git credentials" {
|
@test "create new recipe with git credentials" {
|
||||||
run $ABRA recipe new foobar --git-name fooUser --git-email foo@example.com
|
run $ABRA recipe new foobar --git-name fooUser --git-email foo@example.com
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'Your new foobar recipe has been created'
|
assert_output --partial "new recipe 'foobar' created"
|
||||||
assert_exists "$ABRA_DIR/recipes/foobar"
|
assert_exists "$ABRA_DIR/recipes/foobar"
|
||||||
|
|
||||||
run bash -c 'git -C "$ABRA_DIR/recipes/foobar" log -n 1'
|
run bash -c 'git -C "$ABRA_DIR/recipes/foobar" log -n 1'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user