feat: git hash arg for deploy #434

Merged
decentral1se merged 6 commits from git-hash-arg into main 2024-07-09 09:42:57 +00:00
33 changed files with 310 additions and 207 deletions

View File

@ -5,11 +5,10 @@ import (
)
var AppCommand = cli.Command{
Name: "app",
Aliases: []string{"a"},
Usage: "Manage apps",
ArgsUsage: "<domain>",
Description: "Functionality for managing the life cycle of your apps",
Name: "app",
Aliases: []string{"a"},
Usage: "Manage apps",
ArgsUsage: "<domain>",
Subcommands: []cli.Command{
appBackupCommand,
appCheckCommand,

View File

@ -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
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>]",
Flags: []cli.Flag{
internal.DebugFlag,

View File

@ -43,7 +43,7 @@ var appConfigCommand = cli.Command{
ed, ok := os.LookupEnv("EDITOR")
if !ok {
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"},
}
if err := survey.AskOne(edPrompt, &ed); err != nil {

View File

@ -35,17 +35,18 @@ var appDeployCommand = cli.Command{
internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
Description: `
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.
Description: `Deploy an app.
You may pass "--force" to re-deploy the same version again. This can be useful
if the container runtime has gotten into a weird state.
This command supports chaos operations. Use "--chaos" to deploy your recipe
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,
including unstaged changes and can be useful for live hacking and testing new
recipes.
`,
EXAMPLE:
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,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
@ -76,6 +77,9 @@ recipes.
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
// is because we need to deal with GetComposeFiles under the hood and these
// files change from version to version which therefore affects which
@ -84,9 +88,17 @@ recipes.
if specificVersion != "" {
version = specificVersion
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)
}
if isChaosCommit {
log.Debugf("assuming '%s' is a chaos commit", version)
internal.Chaos = true
}
}
secStats, err := secret.PollSecretsStatus(cl, app)
@ -119,10 +131,10 @@ recipes.
}
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)
if err != nil {
log.Warn(err)
log.Fatal(err)
}
for _, recipeVersion := range recipeVersions {
for version := range recipeVersion {
@ -134,7 +146,7 @@ recipes.
if len(versions) > 0 && !internal.Chaos {
version = versions[len(versions)-1]
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)
}
} else {
@ -150,10 +162,20 @@ recipes.
chaosVersion := "false"
if internal.Chaos {
log.Warnf("chaos mode engaged")
var err error
chaosVersion, err = app.Recipe.ChaosVersion()
if err != nil {
log.Fatal(err)
if isChaosCommit {
chaosVersion = specificVersion
versionLabelLocal, err := app.Recipe.GetVersionLabelLocal()
if err != nil {
log.Fatal(err)
}
version = versionLabelLocal
} else {
var err error
chaosVersion, err = app.Recipe.ChaosVersion()
if err != nil {
log.Fatal(err)
}
}
}

View File

@ -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
actual live deployment status. Depending on how many servers you manage, this
can take some time.
`,
can take some time.`,
Flags: []cli.Flag{
internal.DebugFlag,
internal.MachineReadableFlag,

View File

@ -19,8 +19,8 @@ import (
)
var appNewDescription = `
Take a recipe and uses it to create a new app. This new app configuration is
stored in your ~/.abra directory under the appropriate server.
Creates a new app from a default recipe. This new app configuration is stored
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
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
pass store (see passwordstore.org for more). The pass command must be available
on your $PATH.
`
on your $PATH.`
var appNewCommand = cli.Command{
Name: "new",
@ -92,7 +91,7 @@ var appNewCommand = cli.Command{
version = tag
}
if err := recipe.EnsureVersion(version); err != nil {
if _, err := recipe.EnsureVersion(version); err != nil {
log.Fatal(err)
}
} else {
@ -101,7 +100,7 @@ var appNewCommand = cli.Command{
}
}
} else {
if err := recipe.EnsureVersion(c.Args().Get(1)); err != nil {
if _, err := recipe.EnsureVersion(c.Args().Get(1)); err != nil {
log.Fatal(err)
}
}
@ -174,22 +173,25 @@ var appNewCommand = cli.Command{
table := formatter.CreateTable(tableCol)
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("")
table.Render()
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.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))
if len(secrets) > 0 {
fmt.Println("")
fmt.Println("Here are your generated secrets:")
fmt.Println("Generated secrets:")
fmt.Println("")
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

View File

@ -55,7 +55,7 @@ var appPsCommand = cli.Command{
if statusMeta, ok := statuses[app.StackName()]; ok {
isChaos, exists := statusMeta["chaos"]
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)
}
} else {

View File

@ -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.
To delete everything without prompt, use the "--force/-f" or the "--no-input/n"
flag.
`,
flag.`,
Flags: []cli.Flag{
internal.ForceFlag,
internal.DebugFlag,

View File

@ -33,10 +33,9 @@ Run "abra app ps <domain>" to see a list of service names.
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,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)

View File

@ -34,14 +34,18 @@ var appRollbackCommand = cli.Command{
},
Before: internal.SubCommandBefore,
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
useful if the container runtime has gotten into a weird state.
Unlike "deploy", chaos operations are not supported here. Only recipe versions
are supported values for "[<version>]".
This action could be destructive, please ensure you have a copy of your app
data beforehand.
`,
A rollback can be destructive, please ensure you have a copy of your app data
beforehand.
EXAMPLE:
abra app rollback foo.example.com
abra app rollback foo.example.com 1.2.3+3.2.1`,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
@ -82,10 +86,10 @@ data beforehand.
}
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)
if err != nil {
log.Warn(err)
log.Fatal(err)
}
for _, recipeVersion := range recipeVersions {
@ -105,15 +109,19 @@ data beforehand.
if specificVersion != "" {
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
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)
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)
}
@ -121,7 +129,7 @@ data beforehand.
}
if deployMeta.Version != "unknown" && specificVersion == "" {
if deployMeta.IsChaos == "true" {
if deployMeta.IsChaos {
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)
} else {
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)
}
@ -170,7 +178,7 @@ data beforehand.
}
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)
}
@ -206,8 +214,8 @@ data beforehand.
appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
appPkg.SetUpdateLabel(compose, stackName, app.Env)
chaosVersion := deployMeta.IsChaos
if deployMeta.IsChaos == "true" {
chaosVersion := "false"
if deployMeta.IsChaos {
chaosVersion = deployMeta.ChaosVersion
}

View File

@ -78,8 +78,7 @@ This does not destroy any of the application data.
However, you should remain vigilant, as your swarm installation will consider
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 {
app := internal.ValidateApp(c)
stackName := app.StackName()
@ -100,8 +99,8 @@ Passing "-p/--prune" does not remove those volumes.
log.Fatalf("%s is not deployed?", app.Name)
}
chaosVersion := deployMeta.IsChaos
if deployMeta.IsChaos == "true" {
chaosVersion := "false"
if deployMeta.IsChaos {
chaosVersion = deployMeta.ChaosVersion
}

View File

@ -34,15 +34,18 @@ var appUpgradeCommand = cli.Command{
},
Before: internal.SubCommandBefore,
Description: `
Upgrade an app. You can use it to choose and roll out a new upgrade to a
deployed app.
Upgrade an 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.
Unlike "deploy", chaos operations are not supported here. Only recipe versions
are supported values for "[<version>]".
This action could be destructive, please ensure you have a copy of your app
data beforehand.
`,
An upgrade can be destructive, please ensure you have a copy of your app data
beforehand.
EXAMPLE:
abra app upgrade foo.example.com
abra app upgrade foo.example.com 1.2.3+3.2.1`,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
@ -83,10 +86,10 @@ data beforehand.
}
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)
if err != nil {
log.Warn(err)
log.Fatal(err)
}
for _, recipeVersion := range recipeVersions {
for version := range recipeVersion {
@ -105,15 +108,21 @@ data beforehand.
if specificVersion != "" {
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
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)
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)
}
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
}
availableUpgrades = append(availableUpgrades, specificVersion)
}
@ -123,7 +132,7 @@ data beforehand.
}
if deployMeta.Version != "unknown" && specificVersion == "" {
if deployMeta.IsChaos == "true" {
if deployMeta.IsChaos {
log.Warn("attempting to upgrade a chaos deployment")
}
@ -150,7 +159,7 @@ data beforehand.
log.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
} else {
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)
}
@ -197,7 +206,7 @@ data beforehand.
}
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)
}
@ -250,8 +259,8 @@ data beforehand.
return nil
}
chaosVersion := deployMeta.IsChaos
if deployMeta.IsChaos == "true" {
chaosVersion := "false"
if deployMeta.IsChaos {
chaosVersion = deployMeta.ChaosVersion
}

View File

@ -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
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>",
Aliases: []string{"rm"},
Flags: []cli.Flag{

View File

@ -33,14 +33,7 @@ var catalogueGenerateCommand = cli.Command{
},
Before: internal.SubCommandBefore,
Description: `
Generate a new copy of the recipe catalogue which can be found on:
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.
Generate a new copy of the recipe catalogue.
It is possible to generate new metadata for a single recipe by passing
<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
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>]",
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
@ -213,11 +205,10 @@ keys configured on your account.
// CatalogueCommand defines the `abra catalogue` command and sub-commands.
var CatalogueCommand = cli.Command{
Name: "catalogue",
Usage: "Manage the recipe catalogue",
Aliases: []string{"c"},
ArgsUsage: "<recipe>",
Description: "This command helps recipe packagers interact with the recipe catalogue",
Name: "catalogue",
Usage: "Manage the recipe catalogue",
Aliases: []string{"c"},
ArgsUsage: "<recipe>",
Subcommands: []cli.Command{
catalogueGenerateCommand,
},

View File

@ -25,16 +25,15 @@ import (
var AutoCompleteCommand = cli.Command{
Name: "autocomplete",
Aliases: []string{"ac"},
Usage: "Configure shell autocompletion (recommended)",
Usage: "Configure shell autocompletion",
Description: `
Set up auto-completion in your shell by downloading the relevant files and
laying out what additional information must be loaded. Supported shells are as
follows: bash, fish, fizsh & zsh.
Set up shell auto-completion.
Example:
Supported shells are: bash, fish, fizsh & zsh.
abra autocomplete bash
`,
EXAMPLE:
abra autocomplete bash`,
ArgsUsage: "<shell>",
Flags: []cli.Flag{
internal.DebugFlag,
@ -81,7 +80,7 @@ Example:
switch shellType {
case "bash":
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 cp %s /etc/bash_completion.d/abra
echo "source /etc/bash_completion.d/abra" >> ~/.bashrc
@ -89,19 +88,19 @@ echo "source /etc/bash_completion.d/abra" >> ~/.bashrc
`, autocompletionFile))
case "zsh":
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 cp %s /etc/zsh/completion.d/abra
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))
case "fish":
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 cp %s /etc/fish/completions/abra
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))
}
@ -113,14 +112,18 @@ echo "source /etc/fish/completions/abra" >> ~/.config/fish/config.fish
var UpgradeCommand = cli.Command{
Name: "upgrade",
Aliases: []string{"u"},
Usage: "Upgrade Abra itself",
Usage: "Upgrade abra",
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
that it may contain catastrophic bugs. Thank you very much for the testing
efforts!
`,
Use "-r/--rc" to install the latest release candidate. Please bear in mind that
it may contain absolutely catastrophic deal-breaker bugs. Thank you very much
for the testing efforts 💗
EXAMPLE:
abra upgrade
abra upgrade --rc`,
Flags: []cli.Flag{internal.RCFlag},
Action: func(c *cli.Context) error {
mainURL := "https://install.abra.coopcloud.tech"
@ -144,7 +147,7 @@ efforts!
func newAbraApp(version, commit string) *cli.App {
app := &cli.App{
Name: "abra",
Usage: `The Co-op Cloud command-line utility belt 🎩🐇
Usage: `the Co-op Cloud command-line utility belt 🎩🐇
____ ____ _ _
/ ___|___ ___ _ __ / ___| | ___ _ _ __| |
| | / _ \ _____ / _ \| '_ \ | | | |/ _ \| | | |/ _' |

View File

@ -5,14 +5,13 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
gitPkg "coopcloud.tech/abra/pkg/git"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/abra/pkg/recipe"
"github.com/urfave/cli"
)
var recipeDiffCommand = cli.Command{
Name: "diff",
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"},
ArgsUsage: "<recipe>",
Flags: []cli.Flag{
@ -22,12 +21,7 @@ var recipeDiffCommand = cli.Command{
Before: internal.SubCommandBefore,
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
recipeName := c.Args().First()
r := recipe.Get(recipeName)
if recipeName != "" {
internal.ValidateRecipe(c)
}
r := internal.ValidateRecipe(c)
if err := gitPkg.DiffUnstaged(r.Dir); err != nil {
log.Fatal(err)

View File

@ -48,12 +48,7 @@ Create a new recipe.
Abra uses the built-in example repository which is available here:
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).
`,
https://git.coopcloud.tech/coop-cloud/example`,
Action: func(c *cli.Context) error {
recipeName := c.Args().First()
r := recipe.Get(recipeName)
@ -100,22 +95,8 @@ recipe and domain in the sample environment config).
log.Fatal(err)
}
fmt.Print(fmt.Sprintf(`
Your new %s recipe has been created in %s.
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))
log.Infof("new recipe '%s' created: %s", recipeName, path.Join(r.Dir))
log.Info("happy hacking 🎉")
return nil
},

View File

@ -18,9 +18,7 @@ for you.
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
manner. Abra supports convenient automation for recipe maintainenace, see the
"abra recipe upgrade", "abra recipe sync" and "abra recipe release" commands.
`,
manner.`,
Subcommands: []cli.Command{
recipeFetchCommand,
recipeLintCommand,

View File

@ -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
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{
internal.DebugFlag,
internal.NoInputFlag,

View File

@ -12,7 +12,7 @@ import (
var recipeResetCommand = cli.Command{
Name: "reset",
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"},
ArgsUsage: "<recipe>",
Flags: []cli.Flag{

View File

@ -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
auto-generate it for you. The <recipe> configuration will be updated on the
local file system.
`,
local file system.`,
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
recipe := internal.ValidateRecipe(c)

View File

@ -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
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>",
Flags: []cli.Flag{
internal.DebugFlag,

View File

@ -24,11 +24,10 @@ func sortServiceByName(versions [][]string) func(i, j int) bool {
}
var recipeVersionCommand = cli.Command{
Name: "versions",
Aliases: []string{"v"},
Usage: "List recipe versions",
ArgsUsage: "<recipe>",
Description: "Versions are read from the recipe catalogue.",
Name: "versions",
Aliases: []string{"v"},
Usage: "List recipe versions",
ArgsUsage: "<recipe>",
Flags: []cli.Flag{
internal.DebugFlag,
internal.OfflineFlag,

View File

@ -94,7 +94,7 @@ func createServerDir(name string) (bool, error) {
var serverAddCommand = cli.Command{
Name: "add",
Aliases: []string{"a"},
Usage: "Add a server to your configuration",
Usage: "Add a new server to your configuration",
Description: `
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
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{
internal.DebugFlag,
internal.NoInputFlag,

View File

@ -31,13 +31,12 @@ var volumesFilterFlag = &cli.BoolFlag{
var serverPruneCommand = cli.Command{
Name: "prune",
Aliases: []string{"p"},
Usage: "Prune a managed server; Runs a docker system prune",
Usage: "Prune resources on a server",
Description: `
Prunes unused containers, networks, and dangling images.
If passing "-v/--volumes" then volumes not connected to a deployed app will
also be removed. This can result in unwanted data loss if not used carefully.
`,
Use "-v/--volumes" to remove volumes that are not associated with a deployed
app. This can result in unwanted data loss if not used carefully.`,
ArgsUsage: "[<server>]",
Flags: []cli.Flag{
allFilterFlag,

View File

@ -17,12 +17,12 @@ var serverRemoveCommand = cli.Command{
Aliases: []string{"rm"},
ArgsUsage: "<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
client connection context. This server will then be lost in time, like tears in
rain.
`,
rain.`,
Flags: []cli.Flag{
internal.DebugFlag,
internal.NoInputFlag,

View File

@ -54,10 +54,12 @@ var Notify = cli.Command{
},
Before: internal.SubCommandBefore,
Description: `
It reads the deployed app versions and looks for new versions in the recipe
catalogue. If a new patch/minor version is available, a notification is
printed. To include major versions use the --major flag.
`,
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 printed.
Use "--major" to include new major versions.`,
Action: func(c *cli.Context) error {
cl, err := client.New("default")
if err != nil {
@ -103,14 +105,17 @@ var UpgradeApp = cli.Command{
},
Before: internal.SubCommandBefore,
Description: `
Upgrade an app by specifying its stack name and recipe. By passing "--all"
instead, every deployed app is upgraded. For each apps with enabled auto
updates the deployed version is compared with the current recipe catalogue
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
break things. Only apps that are not deployed with "--chaos" are upgraded, to
update chaos deployments use the "--chaos" flag. Use it with care.
`,
Upgrade an app by specifying stack name and recipe.
Use "--all" to upgrade every deployed app.
For each app with auto updates enabled, the deployed version is compared with
the current recipe catalogue version. If a new patch/minor version is
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 {
cl, err := client.New("default")
if err != nil {
@ -327,7 +332,7 @@ func processRecipeRepoVersion(r recipe.Recipe, version string) error {
return err
}
if err := r.EnsureVersion(version); err != nil {
if _, err := r.EnsureVersion(version); err != nil {
return err
}

View File

@ -57,21 +57,23 @@ func (r Recipe) EnsureExists() error {
}
// 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)
if err := gitPkg.EnsureGitRepo(recipeDir); err != nil {
return err
return isChaosCommit, err
}
repo, err := git.PlainOpen(recipeDir)
if err != nil {
return err
return isChaosCommit, err
}
tags, err := repo.Tags()
if err != nil {
return nil
return isChaosCommit, err
}
var parsedTags []string
@ -83,7 +85,7 @@ func (r Recipe) EnsureVersion(version string) error {
}
return nil
}); err != nil {
return err
return isChaosCommit, err
}
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)
}
var opts *git.CheckoutOptions
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()
if err != nil {
return err
return isChaosCommit, nil
}
opts := &git.CheckoutOptions{
Branch: tagRef,
Create: false,
Force: true,
}
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)
return nil
return isChaosCommit, nil
}
// EnsureIsClean makes sure that the recipe repository has no unstaged changes.

View File

@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"os/signal"
"strconv"
"strings"
"time"
@ -101,7 +102,7 @@ func GetDeployedServicesByName(ctx context.Context, cl *dockerClient.Client, sta
type DeployMeta struct {
IsDeployed bool // whether the app is deployed or not
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
}
@ -110,7 +111,7 @@ func IsDeployed(ctx context.Context, cl *dockerClient.Client, stackName string)
deployMeta := DeployMeta{
IsDeployed: false,
Version: "unknown",
IsChaos: "false", // NOTE(d1): match string type used on label
IsChaos: false,
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)
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)

View File

@ -356,3 +356,12 @@ teardown(){
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all --chaos
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'
}

View File

@ -111,6 +111,26 @@ teardown(){
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
@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
@ -145,3 +165,14 @@ teardown(){
refute_output --partial "${tagHash:0:8}"
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"
}

View File

@ -97,6 +97,26 @@ teardown(){
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
@test "upgrade to latest" {
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}"
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"
}

View File

@ -16,6 +16,14 @@ setup(){
}
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
run rm -rf "$ABRA_DIR/recipes/foobar"
assert_success
@ -25,26 +33,32 @@ teardown(){
@test "create new recipe" {
run $ABRA recipe new foobar
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"
}
# bats test_tags=slow
@test "create new app from new recipe" {
run $ABRA recipe new foobar
assert_success
assert_exists "$ABRA_DIR/recipes/foobar"
run $ABRA app new foobar \
--no-input \
--server "$TEST_SERVER" \
--domain "foobar.$TEST_SERVER"
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" {
run $ABRA recipe new foobar --git-name fooUser --git-email foo@example.com
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"
run bash -c 'git -C "$ABRA_DIR/recipes/foobar" log -n 1'