diff --git a/.e2e.env.sample b/.e2e.env.sample
deleted file mode 100644
index f16b6c15..00000000
--- a/.e2e.env.sample
+++ /dev/null
@@ -1,4 +0,0 @@
-GANDI_TOKEN=...
-HCLOUD_TOKEN=...
-REGISTRY_PASSWORD=...
-REGISTRY_USERNAME=...
diff --git a/.envrc.sample b/.envrc.sample
index fd5b5dea..3f8d827c 100644
--- a/.envrc.sample
+++ b/.envrc.sample
@@ -1,6 +1,7 @@
go env -w GOPRIVATE=coopcloud.tech
# export PASSWORD_STORE_DIR=$(pwd)/../../autonomic/passwords/passwords/
-# export HCLOUD_TOKEN=$(pass show logins/hetzner/cicd/api_key)
-# export CAPSUL_TOKEN=...
-# export GITEA_TOKEN=...
+
+# export ABRA_DIR="$HOME/.abra_test"
+# export ABRA_TEST_DOMAIN=test.example.com
+# export ABRA_SKIP_TEARDOWN=1 # for faster feedback when developing tests
diff --git a/.gitignore b/.gitignore
index 85da835c..4b36ca73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,5 +5,5 @@
/kadabra
abra
dist/
-tests/integration/.abra/catalogue
+tests/integration/.bats
vendor/
diff --git a/AUTHORS.md b/AUTHORS.md
index bb499cb0..6698c3cb 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -1,7 +1,7 @@
# authors
> If you're looking at this and you hack on `abra` and you're not listed here,
-> please do add yourself! This is a community project, let's show some :heart:
+> please do add yourself! This is a community project, let's show some 💞
- 3wordchant
- cassowary
diff --git a/Makefile b/Makefile
index 3349a1b9..ba0759f4 100644
--- a/Makefile
+++ b/Makefile
@@ -9,12 +9,18 @@ export GOPRIVATE=coopcloud.tech
all: format check build test
-run:
+run-abra:
@go run -ldflags=$(LDFLAGS) $(ABRA)
-install:
+run-kadabra:
+ @go run -ldflags=$(LDFLAGS) $(KADABRA)
+
+install-abra:
@go install -ldflags=$(LDFLAGS) $(ABRA)
+install-kadaabra:
+ @go install -ldflags=$(LDFLAGS) $(KADABRA)
+
build-abra:
@go build -v -ldflags=$(LDFLAGS) $(ABRA)
@@ -36,15 +42,8 @@ check:
@test -z $$(gofmt -l .) || \
(echo "gofmt: formatting issue - run 'make format' to resolve" && exit 1)
-test:
+unit-test:
@go test ./... -cover -v
loc:
@find . -name "*.go" | xargs wc -l
-
-loc-author:
- @git ls-files -z | \
- xargs -0rn 1 -P "$$(nproc)" -I{} sh -c 'git blame -w -M -C -C --line-porcelain -- {} | grep -I --line-buffered "^author "' | \
- sort -f | \
- uniq -ic | \
- sort -n
diff --git a/README.md b/README.md
index f307c04d..0b50bb2f 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,6 @@ The Co-op Cloud utility belt 🎩🐇
-`abra` is the flagship client & command-line tool for Co-op Cloud. It has been developed specifically for the purpose of making the day-to-day operations of [operators](https://docs.coopcloud.tech/operators/) and [maintainers](https://docs.coopcloud.tech/maintainers/) pleasant & convenient. It is libre software, written in [Go](https://go.dev) and maintained and extended by the community :heart:
+`abra` is the flagship client & command-line tool for Co-op Cloud. It has been developed specifically for the purpose of making the day-to-day operations of [operators](https://docs.coopcloud.tech/operators/) and [maintainers](https://docs.coopcloud.tech/maintainers/) pleasant & convenient. It is libre software, written in [Go](https://go.dev) and maintained and extended by the community 💖
Please see [docs.coopcloud.tech/abra](https://docs.coopcloud.tech/abra) for help on install, upgrade, hacking, troubleshooting & more!
diff --git a/cli/app/backup.go b/cli/app/backup.go
index 9b9832e0..18d5d2df 100644
--- a/cli/app/backup.go
+++ b/cli/app/backup.go
@@ -15,8 +15,7 @@ import (
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
containerPkg "coopcloud.tech/abra/pkg/container"
- "coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
+ recipePkg "coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/upstream/container"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
@@ -72,15 +71,9 @@ This file is a compressed archive which contains all backup paths. To see paths,
This single file can be used to restore your app. See "abra app restore" for more.
`,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
- cl, err := client.New(app.Server)
- if err != nil {
- logrus.Fatal(err)
- }
-
- recipe, err := recipe.Get(app.Recipe, conf)
+ recipe, err := recipePkg.Get(app.Recipe, internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -114,6 +107,11 @@ This single file can be used to restore your app. See "abra app restore" for mor
}
}
+ cl, err := client.New(app.Server)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
serviceName := c.Args().Get(1)
if serviceName != "" {
backupConfig, ok := backupConfigs[serviceName]
diff --git a/cli/app/check.go b/cli/app/check.go
index 72c07a65..572b3d6a 100644
--- a/cli/app/check.go
+++ b/cli/app/check.go
@@ -8,7 +8,7 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/config"
- "coopcloud.tech/abra/pkg/runtime"
+ "coopcloud.tech/abra/pkg/recipe"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -16,16 +16,19 @@ import (
var appCheckCommand = cli.Command{
Name: "check",
Aliases: []string{"chk"},
- Usage: "Check if app is configured correctly",
+ Usage: "Check if an app is configured correctly",
ArgsUsage: "",
Flags: []cli.Flag{
internal.DebugFlag,
- internal.OfflineFlag,
},
- Before: internal.SubCommandBefore,
+ Before: internal.SubCommandBefore,
+ BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
+
+ if err := recipe.EnsureExists(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
envSamplePath := path.Join(config.RECIPES_DIR, app.Recipe, ".env.sample")
if _, err := os.Stat(envSamplePath); err != nil {
@@ -56,5 +59,4 @@ var appCheckCommand = cli.Command{
return nil
},
- BashComplete: autocomplete.AppNameComplete,
}
diff --git a/cli/app/cmd.go b/cli/app/cmd.go
index 928fb3d0..23e98494 100644
--- a/cli/app/cmd.go
+++ b/cli/app/cmd.go
@@ -12,7 +12,7 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
- "coopcloud.tech/abra/pkg/runtime"
+ "coopcloud.tech/abra/pkg/recipe"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -39,16 +39,13 @@ Example:
internal.LocalCmdFlag,
internal.RemoteUserFlag,
internal.TtyFlag,
- internal.OfflineFlag,
},
BashComplete: autocomplete.AppNameComplete,
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
- cl, err := client.New(app.Server)
- if err != nil {
+ if err := recipe.EnsureExists(app.Recipe); err != nil {
logrus.Fatal(err)
}
@@ -67,6 +64,10 @@ Example:
}
if internal.LocalCmd {
+ if !(len(c.Args()) >= 2) {
+ internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments"))
+ }
+
cmdName := c.Args().Get(1)
if err := internal.EnsureCommand(abraSh, app.Recipe, cmdName); err != nil {
logrus.Fatal(err)
@@ -78,6 +79,7 @@ Example:
for k, v := range app.Env {
exportEnv = exportEnv + fmt.Sprintf("%s='%s'; ", k, v)
}
+
var sourceAndExec string
if hasCmdArgs {
logrus.Debugf("parsed following command arguments: %s", parsedCmdArgs)
@@ -98,6 +100,10 @@ Example:
logrus.Fatal(err)
}
} else {
+ if !(len(c.Args()) >= 3) {
+ internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments"))
+ }
+
targetServiceName := c.Args().Get(1)
cmdName := c.Args().Get(2)
@@ -129,6 +135,11 @@ Example:
logrus.Debug("did not detect any command arguments")
}
+ cl, err := client.New(app.Server)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
if err := internal.RunCmdRemote(cl, app, abraSh, targetServiceName, cmdName, parsedCmdArgs); err != nil {
logrus.Fatal(err)
}
diff --git a/cli/app/config.go b/cli/app/config.go
index 6aebb786..9404fae4 100644
--- a/cli/app/config.go
+++ b/cli/app/config.go
@@ -20,9 +20,9 @@ var appConfigCommand = cli.Command{
ArgsUsage: "",
Flags: []cli.Flag{
internal.DebugFlag,
- internal.OfflineFlag,
},
- Before: internal.SubCommandBefore,
+ Before: internal.SubCommandBefore,
+ BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
appName := c.Args().First()
@@ -61,5 +61,4 @@ var appConfigCommand = cli.Command{
return nil
},
- BashComplete: autocomplete.AppNameComplete,
}
diff --git a/cli/app/cp.go b/cli/app/cp.go
index 910fbc05..937c7975 100644
--- a/cli/app/cp.go
+++ b/cli/app/cp.go
@@ -12,7 +12,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/container"
"coopcloud.tech/abra/pkg/formatter"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
@@ -28,10 +27,9 @@ var appCpCommand = cli.Command{
Flags: []cli.Flag{
internal.DebugFlag,
internal.NoInputFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
- Usage: "Copy files to/from a running app service",
+ Usage: "Copy files to/from a deployed app service",
Description: `
Copy files to and from any app service file system.
@@ -41,16 +39,11 @@ If you want to copy a myfile.txt to the root of the app service:
And if you want to copy that file back to your current working directory locally:
- abra app cp app:/myfile.txt .
+ abra app cp app:/myfile.txt .
`,
+ BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
-
- cl, err := client.New(app.Server)
- if err != nil {
- logrus.Fatal(err)
- }
+ app := internal.ValidateApp(c)
src := c.Args().Get(1)
dst := c.Args().Get(2)
@@ -92,19 +85,23 @@ And if you want to copy that file back to your current working directory locally
logrus.Debugf("assuming transfer is going TO the container")
}
- if !isToContainer {
- if _, err := os.Stat(dstPath); os.IsNotExist(err) {
- logrus.Fatalf("%s does not exist locally?", dstPath)
+ if isToContainer {
+ if _, err := os.Stat(srcPath); os.IsNotExist(err) {
+ logrus.Fatalf("%s does not exist locally?", srcPath)
}
}
+ cl, err := client.New(app.Server)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
if err := configureAndCp(c, cl, app, srcPath, dstPath, service, isToContainer); err != nil {
logrus.Fatal(err)
}
return nil
},
- BashComplete: autocomplete.AppNameComplete,
}
func configureAndCp(
@@ -126,10 +123,6 @@ func configureAndCp(
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
if isToContainer {
- if _, err := os.Stat(srcPath); err != nil {
- logrus.Fatalf("%s does not exist?", srcPath)
- }
-
toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
content, err := archive.TarWithOptions(srcPath, toTarOpts)
if err != nil {
diff --git a/cli/app/deploy.go b/cli/app/deploy.go
index 44bbfd0c..81fd780a 100644
--- a/cli/app/deploy.go
+++ b/cli/app/deploy.go
@@ -3,14 +3,9 @@ package app
import (
"context"
"fmt"
- "io/ioutil"
- "os"
- "path"
- "strings"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
@@ -20,8 +15,6 @@ import (
"coopcloud.tech/abra/pkg/lint"
"coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/upstream/stack"
- "github.com/AlecAivazis/survey/v2"
- dockerClient "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -48,28 +41,36 @@ for this you need to look at the "abra app upgrade " command.
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.
-Chas mode ("--chaos") will deploy your local checkout of a recipe as-is,
+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.
`,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
stackName := app.StackName()
- cl, err := client.New(app.Server)
- if err != nil {
+ if err := recipe.EnsureExists(app.Recipe); err != nil {
logrus.Fatal(err)
}
if !internal.Chaos {
- if err := recipe.EnsureUpToDate(app.Recipe, conf); err != nil {
+ if err := recipe.EnsureIsClean(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+
+ if !internal.Offline {
+ if err := recipe.EnsureUpToDate(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+ }
+
+ if err := recipe.EnsureLatest(app.Recipe); err != nil {
logrus.Fatal(err)
}
}
- r, err := recipe.Get(app.Recipe, conf)
+ r, err := recipe.Get(app.Recipe, internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -80,6 +81,11 @@ recipes.
logrus.Debugf("checking whether %s is already deployed", stackName)
+ cl, err := client.New(app.Server)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName)
if err != nil {
logrus.Fatal(err)
@@ -95,8 +101,8 @@ recipes.
isLatestHash := false
version := deployedVersion
- if version == "unknown" && !internal.Chaos {
- catl, err := recipe.ReadRecipeCatalogue(conf)
+ if !internal.Chaos {
+ catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -118,20 +124,11 @@ recipes.
isLatestHash = true
version = formatter.SmallSHA(head.String())
logrus.Warn("no versions detected, using latest commit")
- if err := recipe.EnsureLatest(app.Recipe, conf); err != nil {
- logrus.Fatal(err)
- }
- }
- }
-
- if version == "unknown" && !internal.Chaos {
- logrus.Debugf("choosing %s as version to deploy", version)
- if err := recipe.EnsureVersion(app.Recipe, version); err != nil {
- logrus.Fatal(err)
}
}
if version != "unknown" && !internal.Chaos && !isLatestHash {
+ logrus.Debugf("choosing %s as version to deploy", version)
if err := recipe.EnsureVersion(app.Recipe, version); err != nil {
logrus.Fatal(err)
}
@@ -159,6 +156,7 @@ recipes.
if err != nil {
logrus.Fatal(err)
}
+
deployOpts := stack.Deploy{
Composefiles: composeFiles,
Namespace: stackName,
@@ -169,13 +167,14 @@ recipes.
if err != nil {
logrus.Fatal(err)
}
+
config.ExposeAllEnv(stackName, compose, app.Env)
config.SetRecipeLabel(compose, stackName, app.Recipe)
config.SetChaosLabel(compose, stackName, internal.Chaos)
config.SetChaosVersionLabel(compose, stackName, version)
config.SetUpdateLabel(compose, stackName, app.Env)
- if err := DeployOverview(app, version, "continue with deployment?"); err != nil {
+ if err := internal.DeployOverview(app, version, "continue with deployment?"); err != nil {
logrus.Fatal(err)
}
@@ -205,175 +204,10 @@ recipes.
postDeployCmds, ok := app.Env["POST_DEPLOY_CMDS"]
if ok && !internal.DontWaitConverge {
logrus.Debugf("run the following post-deploy commands: %s", postDeployCmds)
- if err := PostCmds(cl, app, postDeployCmds); err != nil {
+ if err := internal.PostCmds(cl, app, postDeployCmds); err != nil {
logrus.Fatalf("attempting to run post deploy commands, saw: %s", err)
}
}
return nil
},
}
-
-// PostCmds parses a string of commands and executes them inside of the respective services
-// the commands string must have the following format:
-// " | |... "
-func PostCmds(cl *dockerClient.Client, app config.App, commands string) error {
- abraSh := path.Join(config.RECIPES_DIR, app.Recipe, "abra.sh")
- if _, err := os.Stat(abraSh); err != nil {
- if os.IsNotExist(err) {
- return fmt.Errorf(fmt.Sprintf("%s does not exist for %s?", abraSh, app.Name))
- }
- return err
- }
-
- for _, command := range strings.Split(commands, "|") {
- commandParts := strings.Split(command, " ")
- if len(commandParts) < 2 {
- return fmt.Errorf(fmt.Sprintf("not enough arguments: %s", command))
- }
- targetServiceName := commandParts[0]
- cmdName := commandParts[1]
- parsedCmdArgs := ""
- if len(commandParts) > 2 {
- parsedCmdArgs = fmt.Sprintf("%s ", strings.Join(commandParts[2:], " "))
- }
- logrus.Infof("running post-command '%s %s' in container %s", cmdName, parsedCmdArgs, targetServiceName)
-
- if err := internal.EnsureCommand(abraSh, app.Recipe, cmdName); err != nil {
- return err
- }
-
- serviceNames, err := config.GetAppServiceNames(app.Name)
- if err != nil {
- return err
- }
-
- matchingServiceName := false
- for _, serviceName := range serviceNames {
- if serviceName == targetServiceName {
- matchingServiceName = true
- }
- }
-
- if !matchingServiceName {
- return fmt.Errorf(fmt.Sprintf("no service %s for %s?", targetServiceName, app.Name))
- }
-
- logrus.Debugf("running command %s %s within the context of %s_%s", cmdName, parsedCmdArgs, app.StackName(), targetServiceName)
-
- internal.Tty = true
- if err := internal.RunCmdRemote(cl, app, abraSh, targetServiceName, cmdName, parsedCmdArgs); err != nil {
- return err
- }
- }
- return nil
-}
-
-// DeployOverview shows a deployment overview
-func DeployOverview(app config.App, version, message string) error {
- tableCol := []string{"server", "recipe", "config", "domain", "version"}
- table := formatter.CreateTable(tableCol)
-
- deployConfig := "compose.yml"
- if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
- deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
- }
-
- server := app.Server
- if app.Server == "default" {
- server = "local"
- }
-
- table.Append([]string{server, app.Recipe, deployConfig, app.Domain, version})
- table.Render()
-
- if internal.NoInput {
- return nil
- }
-
- response := false
- prompt := &survey.Confirm{
- Message: message,
- }
-
- if err := survey.AskOne(prompt, &response); err != nil {
- return err
- }
-
- if !response {
- logrus.Fatal("exiting as requested")
- }
-
- return nil
-}
-
-// NewVersionOverview shows an upgrade or downgrade overview
-func NewVersionOverview(app config.App, currentVersion, newVersion, releaseNotes string) error {
- tableCol := []string{"server", "recipe", "config", "domain", "current version", "to be deployed"}
- table := formatter.CreateTable(tableCol)
-
- deployConfig := "compose.yml"
- if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
- deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
- }
-
- server := app.Server
- if app.Server == "default" {
- server = "local"
- }
-
- table.Append([]string{server, app.Recipe, deployConfig, app.Domain, currentVersion, newVersion})
- table.Render()
-
- if releaseNotes == "" {
- var err error
- releaseNotes, err = GetReleaseNotes(app.Recipe, newVersion)
- if err != nil {
- return err
- }
- }
-
- if releaseNotes != "" && newVersion != "" {
- fmt.Println()
- fmt.Println(fmt.Sprintf("%s release notes:\n\n%s", newVersion, releaseNotes))
- } else {
- logrus.Warnf("no release notes available for %s", newVersion)
- }
-
- if internal.NoInput {
- return nil
- }
-
- response := false
- prompt := &survey.Confirm{
- Message: "continue with deployment?",
- }
-
- if err := survey.AskOne(prompt, &response); err != nil {
- return err
- }
-
- if !response {
- logrus.Fatal("exiting as requested")
- }
-
- return nil
-}
-
-// GetReleaseNotes prints release notes for a recipe version
-func GetReleaseNotes(recipeName, version string) (string, error) {
- if version == "" {
- return "", nil
- }
-
- fpath := path.Join(config.RECIPES_DIR, recipeName, "release", version)
-
- if _, err := os.Stat(fpath); !os.IsNotExist(err) {
- releaseNotes, err := ioutil.ReadFile(fpath)
- if err != nil {
- return "", err
- }
- return string(releaseNotes), nil
- }
-
- return "", nil
-}
diff --git a/cli/app/errors.go b/cli/app/errors.go
index 4d6d21cb..79425be8 100644
--- a/cli/app/errors.go
+++ b/cli/app/errors.go
@@ -12,7 +12,6 @@ import (
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@@ -56,8 +55,7 @@ the logs.
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
cl, err := client.New(app.Server)
if err != nil {
@@ -74,25 +72,23 @@ the logs.
}
if !internal.Watch {
- if err := checkErrors(c, cl, app, conf); err != nil {
+ if err := checkErrors(c, cl, app); err != nil {
logrus.Fatal(err)
}
return nil
}
for {
- if err := checkErrors(c, cl, app, conf); err != nil {
+ if err := checkErrors(c, cl, app); err != nil {
logrus.Fatal(err)
}
time.Sleep(2 * time.Second)
}
-
- return nil
},
}
-func checkErrors(c *cli.Context, cl *dockerClient.Client, app config.App, conf *runtime.Config) error {
- recipe, err := recipe.Get(app.Recipe, conf)
+func checkErrors(c *cli.Context, cl *dockerClient.Client, app config.App) error {
+ recipe, err := recipe.Get(app.Recipe, internal.Offline)
if err != nil {
return err
}
diff --git a/cli/app/list.go b/cli/app/list.go
index b3e01606..08dff6f2 100644
--- a/cli/app/list.go
+++ b/cli/app/list.go
@@ -11,7 +11,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/tagcmp"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -84,8 +83,6 @@ can take some time.
},
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
-
appFiles, err := config.LoadAppFiles(listAppServer)
if err != nil {
logrus.Fatal(err)
@@ -113,7 +110,7 @@ can take some time.
logrus.Fatal(err)
}
- catl, err = recipe.ReadRecipeCatalogue(conf)
+ catl, err = recipe.ReadRecipeCatalogue(internal.Offline)
if err != nil {
logrus.Fatal(err)
}
diff --git a/cli/app/logs.go b/cli/app/logs.go
index b1cd992c..5c60f324 100644
--- a/cli/app/logs.go
+++ b/cli/app/logs.go
@@ -11,8 +11,8 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/service"
+ "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
@@ -79,23 +79,27 @@ var appLogsCommand = cli.Command{
internal.StdErrOnlyFlag,
internal.SinceLogsFlag,
internal.DebugFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(
- runtime.WithOffline(internal.Offline),
- runtime.WithEnsureRecipeExists(false),
- )
-
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
+ stackName := app.StackName()
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
}
+ isDeployed, _, err := stack.IsDeployed(context.Background(), cl, stackName)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
+ if !isDeployed {
+ logrus.Fatalf("%s is not deployed?", app.Name)
+ }
+
logOpts.Since = internal.SinceLogs
serviceName := c.Args().Get(1)
diff --git a/cli/app/new.go b/cli/app/new.go
index 9034b3bc..cba41dec 100644
--- a/cli/app/new.go
+++ b/cli/app/new.go
@@ -13,7 +13,6 @@ import (
"coopcloud.tech/abra/pkg/jsontable"
"coopcloud.tech/abra/pkg/recipe"
recipePkg "coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/secret"
"github.com/AlecAivazis/survey/v2"
dockerClient "github.com/docker/docker/client"
@@ -55,17 +54,19 @@ var appNewCommand = cli.Command{
internal.SecretsFlag,
internal.OfflineFlag,
},
- Before: internal.SubCommandBefore,
- ArgsUsage: "[]",
+ Before: internal.SubCommandBefore,
+ ArgsUsage: "[]",
+ BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(
- runtime.WithOffline(internal.Offline),
- runtime.WithEnsureRecipeUpToDate(false),
- )
+ recipe := internal.ValidateRecipe(c)
- recipe := internal.ValidateRecipeWithPrompt(c, conf)
+ if !internal.Offline {
+ if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
+ logrus.Fatal(err)
+ }
+ }
- if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
+ if err := recipePkg.EnsureLatest(recipe.Name); err != nil {
logrus.Fatal(err)
}
@@ -144,7 +145,6 @@ var appNewCommand = cli.Command{
return nil
},
- BashComplete: autocomplete.RecipeNameComplete,
}
// AppSecrets represents all app secrest
diff --git a/cli/app/ps.go b/cli/app/ps.go
index 9f0665c2..b593a64f 100644
--- a/cli/app/ps.go
+++ b/cli/app/ps.go
@@ -10,7 +10,6 @@ import (
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/service"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/buger/goterm"
@@ -30,13 +29,11 @@ var appPsCommand = cli.Command{
Flags: []cli.Flag{
internal.WatchFlag,
internal.DebugFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
cl, err := client.New(app.Server)
if err != nil {
diff --git a/cli/app/remove.go b/cli/app/remove.go
index 40b5802a..ea4efedf 100644
--- a/cli/app/remove.go
+++ b/cli/app/remove.go
@@ -8,7 +8,6 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
- "coopcloud.tech/abra/pkg/runtime"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/AlecAivazis/survey/v2"
"github.com/docker/docker/api/types"
@@ -49,8 +48,7 @@ flag.
BashComplete: autocomplete.AppNameComplete,
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
if !internal.Force && !internal.NoInput {
response := false
diff --git a/cli/app/restart.go b/cli/app/restart.go
index 0dede1fa..15b4cfd0 100644
--- a/cli/app/restart.go
+++ b/cli/app/restart.go
@@ -8,7 +8,6 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
- "coopcloud.tech/abra/pkg/runtime"
upstream "coopcloud.tech/abra/pkg/upstream/service"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/sirupsen/logrus"
@@ -28,8 +27,7 @@ var appRestartCommand = cli.Command{
Description: `This command restarts a service within a deployed app.`,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
serviceNameShort := c.Args().Get(1)
if serviceNameShort == "" {
@@ -42,6 +40,15 @@ var appRestartCommand = cli.Command{
logrus.Fatal(err)
}
+ isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName())
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
+ if !isDeployed {
+ logrus.Fatalf("%s is not deployed?", app.Name)
+ }
+
serviceName := fmt.Sprintf("%s_%s", app.StackName(), serviceNameShort)
logrus.Debugf("attempting to scale %s to 0 (restart logic)", serviceName)
diff --git a/cli/app/restore.go b/cli/app/restore.go
index 6303ad28..82e0db01 100644
--- a/cli/app/restore.go
+++ b/cli/app/restore.go
@@ -12,7 +12,6 @@ import (
"coopcloud.tech/abra/pkg/config"
containerPkg "coopcloud.tech/abra/pkg/container"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/upstream/container"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
@@ -49,17 +48,17 @@ restoring the backup.
Unlike "abra app backup", restore must be run on a per-service basis. You can
not restore all services in one go. Backup files produced by Abra are
compressed archives which use absolute paths. This allows Abra to restore
-according to standard tar command logic.
+according to standard tar command logic, i.e. the backup will be restored to
+the path it was originally backed up from.
Example:
abra app restore example.com app ~/.abra/backups/example_com_app_609341138.tar.gz
`,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
- cl, err := client.New(app.Server)
+ recipe, err := recipe.Get(app.Recipe, internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -80,11 +79,6 @@ Example:
}
}
- recipe, err := recipe.Get(app.Recipe, conf)
- if err != nil {
- logrus.Fatal(err)
- }
-
restoreConfigs := make(map[string]restoreConfig)
for _, service := range recipe.Config.Services {
if restoreEnabled, ok := service.Deploy.Labels["backupbot.restore"]; ok {
@@ -114,6 +108,11 @@ Example:
rsConfig = restoreConfig{}
}
+ cl, err := client.New(app.Server)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
if err := runRestore(cl, app, backupPath, serviceName, rsConfig); err != nil {
logrus.Fatal(err)
}
@@ -170,8 +169,8 @@ func runRestore(cl *dockerClient.Client, app config.App, backupPath, serviceName
return err
}
- // we use absolute paths so tar knows what to do. it will restore files
- // according to the paths set in the compresed archive
+ // NOTE(d1): we use absolute paths so tar knows what to do. it will restore
+ // files according to the paths set in the compressed archive
restorePath := "/"
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
diff --git a/cli/app/rollback.go b/cli/app/rollback.go
index c03f4701..afb70b9e 100644
--- a/cli/app/rollback.go
+++ b/cli/app/rollback.go
@@ -8,7 +8,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/lint"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"coopcloud.tech/tagcmp"
@@ -49,17 +48,30 @@ recipes.
`,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
stackName := app.StackName()
+ if err := recipe.EnsureExists(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+
if !internal.Chaos {
- if err := recipe.EnsureUpToDate(app.Recipe, conf); err != nil {
+ if err := recipe.EnsureIsClean(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+
+ if !internal.Offline {
+ if err := recipe.EnsureUpToDate(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+ }
+
+ if err := recipe.EnsureLatest(app.Recipe); err != nil {
logrus.Fatal(err)
}
}
- r, err := recipe.Get(app.Recipe, conf)
+ r, err := recipe.Get(app.Recipe, internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -84,7 +96,7 @@ recipes.
logrus.Fatalf("%s is not deployed?", app.Name)
}
- catl, err := recipe.ReadRecipeCatalogue(conf)
+ catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -129,7 +141,7 @@ recipes.
if len(availableDowngrades) > 0 && !internal.Chaos {
if internal.Force || internal.NoInput {
chosenDowngrade = availableDowngrades[len(availableDowngrades)-1]
- logrus.Debugf("choosing %s as version to downgrade to (--force)", chosenDowngrade)
+ logrus.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade)
} else {
prompt := &survey.Select{
Message: fmt.Sprintf("Please select a downgrade (current version: %s):", deployedVersion),
@@ -185,7 +197,7 @@ recipes.
config.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
config.SetUpdateLabel(compose, stackName, app.Env)
- if err := NewVersionOverview(app, deployedVersion, chosenDowngrade, ""); err != nil {
+ if err := internal.NewVersionOverview(app, deployedVersion, chosenDowngrade, ""); err != nil {
logrus.Fatal(err)
}
diff --git a/cli/app/run.go b/cli/app/run.go
index 901edc1e..4ae68c1b 100644
--- a/cli/app/run.go
+++ b/cli/app/run.go
@@ -9,7 +9,6 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
containerPkg "coopcloud.tech/abra/pkg/container"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/upstream/container"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
@@ -38,15 +37,13 @@ var appRunCommand = cli.Command{
internal.DebugFlag,
noTTYFlag,
userFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
ArgsUsage: " ...",
Usage: "Run a command in a service container",
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
if len(c.Args()) < 2 {
internal.ShowSubcommandHelpAndError(c, errors.New("no provided?"))
diff --git a/cli/app/secret.go b/cli/app/secret.go
index eefbdc90..fa3479ff 100644
--- a/cli/app/secret.go
+++ b/cli/app/secret.go
@@ -12,7 +12,7 @@ import (
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
- "coopcloud.tech/abra/pkg/runtime"
+ recipePkg "coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/secret"
"github.com/docker/docker/api/types"
dockerClient "github.com/docker/docker/client"
@@ -48,12 +48,12 @@ var appSecretGenerateCommand = cli.Command{
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
- cl, err := client.New(app.Server)
- if err != nil {
- logrus.Fatal(err)
+ if !internal.Offline {
+ if err := recipePkg.EnsureUpToDate(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
}
if len(c.Args()) == 1 && !allSecrets {
@@ -87,6 +87,11 @@ var appSecretGenerateCommand = cli.Command{
}
}
+ cl, err := client.New(app.Server)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
secretVals, err := secret.GenerateSecrets(cl, secretsToCreate, app.StackName(), app.Server)
if err != nil {
logrus.Fatal(err)
@@ -142,18 +147,17 @@ Example:
`,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
+
+ if len(c.Args()) != 4 {
+ internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments?"))
+ }
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
}
- if len(c.Args()) != 4 {
- internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments?"))
- }
-
name := c.Args().Get(1)
version := c.Args().Get(2)
data := c.Args().Get(3)
@@ -203,7 +207,6 @@ var appSecretRmCommand = cli.Command{
internal.NoInputFlag,
rmAllSecretsFlag,
internal.PassRemoveFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
ArgsUsage: " []",
@@ -216,8 +219,7 @@ Example:
abra app secret remove myapp db_pass
`,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
secrets := secret.ReadSecretEnvVars(app.Env)
if c.Args().Get(1) != "" && rmAllSecrets {
@@ -295,13 +297,12 @@ var appSecretLsCommand = cli.Command{
Aliases: []string{"ls"},
Flags: []cli.Flag{
internal.DebugFlag,
- internal.OfflineFlag,
},
- Before: internal.SubCommandBefore,
- Usage: "List all secrets",
+ Before: internal.SubCommandBefore,
+ Usage: "List all secrets",
+ BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
secrets := secret.ReadSecretEnvVars(app.Env)
tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"}
@@ -350,7 +351,6 @@ var appSecretLsCommand = cli.Command{
return nil
},
- BashComplete: autocomplete.AppNameComplete,
}
var appSecretCommand = cli.Command{
diff --git a/cli/app/services.go b/cli/app/services.go
index 765c939f..60a7245e 100644
--- a/cli/app/services.go
+++ b/cli/app/services.go
@@ -9,7 +9,6 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/formatter"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/service"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/docker/docker/api/types"
@@ -24,13 +23,11 @@ var appServicesCommand = cli.Command{
ArgsUsage: "",
Flags: []cli.Flag{
internal.DebugFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
cl, err := client.New(app.Server)
if err != nil {
diff --git a/cli/app/undeploy.go b/cli/app/undeploy.go
index 6800868f..3a1c2ffa 100644
--- a/cli/app/undeploy.go
+++ b/cli/app/undeploy.go
@@ -10,7 +10,6 @@ import (
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
- "coopcloud.tech/abra/pkg/runtime"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
@@ -87,7 +86,6 @@ var appUndeployCommand = cli.Command{
internal.DebugFlag,
internal.NoInputFlag,
pruneFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
Usage: "Undeploy an app",
@@ -101,8 +99,7 @@ any previously attached volumes as eligible for pruning once undeployed.
Passing "-p/--prune" does not remove those volumes.
`,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
stackName := app.StackName()
cl, err := client.New(app.Server)
@@ -121,7 +118,7 @@ Passing "-p/--prune" does not remove those volumes.
logrus.Fatalf("%s is not deployed?", app.Name)
}
- if err := DeployOverview(app, deployedVersion, "continue with undeploy?"); err != nil {
+ if err := internal.DeployOverview(app, deployedVersion, "continue with undeploy?"); err != nil {
logrus.Fatal(err)
}
diff --git a/cli/app/upgrade.go b/cli/app/upgrade.go
index 225e2d7f..7bb85fbf 100644
--- a/cli/app/upgrade.go
+++ b/cli/app/upgrade.go
@@ -10,7 +10,7 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/lint"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
+ recipePkg "coopcloud.tech/abra/pkg/recipe"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"coopcloud.tech/tagcmp"
"github.com/AlecAivazis/survey/v2"
@@ -51,33 +51,43 @@ Chas mode ("--chaos") will deploy your local checkout of a recipe as-is,
including unstaged changes and can be useful for live hacking and testing new
recipes.
`,
+ BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
stackName := app.StackName()
+ if !internal.Chaos {
+ if err := recipe.EnsureIsClean(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+
+ if !internal.Offline {
+ if err := recipe.EnsureUpToDate(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+ }
+
+ if err := recipe.EnsureLatest(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+ }
+
+ recipe, err := recipePkg.Get(app.Recipe, internal.Offline)
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
+ if err := lint.LintForErrors(recipe); err != nil {
+ logrus.Fatal(err)
+ }
+
+ logrus.Debugf("checking whether %s is already deployed", stackName)
+
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
}
- if !internal.Chaos {
- if err := recipe.EnsureUpToDate(app.Recipe, conf); err != nil {
- logrus.Fatal(err)
- }
- }
-
- r, err := recipe.Get(app.Recipe, conf)
- if err != nil {
- logrus.Fatal(err)
- }
-
- if err := lint.LintForErrors(r); err != nil {
- logrus.Fatal(err)
- }
-
- logrus.Debugf("checking whether %s is already deployed", stackName)
-
isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName)
if err != nil {
logrus.Fatal(err)
@@ -87,12 +97,12 @@ recipes.
logrus.Fatalf("%s is not deployed?", app.Name)
}
- catl, err := recipe.ReadRecipeCatalogue(conf)
+ catl, err := recipePkg.ReadRecipeCatalogue(internal.Offline)
if err != nil {
logrus.Fatal(err)
}
- versions, err := recipe.GetRecipeCatalogueVersions(app.Recipe, catl)
+ versions, err := recipePkg.GetRecipeCatalogueVersions(app.Recipe, catl)
if err != nil {
logrus.Fatal(err)
}
@@ -152,13 +162,13 @@ recipes.
// if release notes written after git tag published, read them before we
// check out the tag and then they'll appear to be missing. this covers
// when we obviously will forget to write release notes before publishing
- releaseNotes, err := GetReleaseNotes(app.Recipe, chosenUpgrade)
+ releaseNotes, err := internal.GetReleaseNotes(app.Recipe, chosenUpgrade)
if err != nil {
return err
}
if !internal.Chaos {
- if err := recipe.EnsureVersion(app.Recipe, chosenUpgrade); err != nil {
+ if err := recipePkg.EnsureVersion(app.Recipe, chosenUpgrade); err != nil {
logrus.Fatal(err)
}
}
@@ -166,7 +176,7 @@ recipes.
if internal.Chaos {
logrus.Warn("chaos mode engaged")
var err error
- chosenUpgrade, err = recipe.ChaosVersion(app.Recipe)
+ chosenUpgrade, err = recipePkg.ChaosVersion(app.Recipe)
if err != nil {
logrus.Fatal(err)
}
@@ -201,7 +211,7 @@ recipes.
config.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
config.SetUpdateLabel(compose, stackName, app.Env)
- if err := NewVersionOverview(app, deployedVersion, chosenUpgrade, releaseNotes); err != nil {
+ if err := internal.NewVersionOverview(app, deployedVersion, chosenUpgrade, releaseNotes); err != nil {
logrus.Fatal(err)
}
@@ -218,12 +228,11 @@ recipes.
postDeployCmds, ok := app.Env["POST_UPGRADE_CMDS"]
if ok && !internal.DontWaitConverge {
logrus.Debugf("run the following post-deploy commands: %s", postDeployCmds)
- if err := PostCmds(cl, app, postDeployCmds); err != nil {
+ if err := internal.PostCmds(cl, app, postDeployCmds); err != nil {
logrus.Fatalf("attempting to run post deploy commands, saw: %s", err)
}
}
return nil
},
- BashComplete: autocomplete.AppNameComplete,
}
diff --git a/cli/app/version.go b/cli/app/version.go
index 0777d4aa..f5dff54e 100644
--- a/cli/app/version.go
+++ b/cli/app/version.go
@@ -8,7 +8,6 @@ import (
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/upstream/stack"
"github.com/docker/distribution/reference"
"github.com/sirupsen/logrus"
@@ -47,9 +46,9 @@ Show all information about versioning related to a deployed app. This includes
the individual image names, tags and digests. But also the Co-op Cloud recipe
version.
`,
+ BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
stackName := app.StackName()
cl, err := client.New(app.Server)
@@ -64,15 +63,19 @@ version.
logrus.Fatal(err)
}
- if deployedVersion == "unknown" {
- logrus.Fatalf("failed to determine version of deployed %s", app.Name)
- }
-
if !isDeployed {
logrus.Fatalf("%s is not deployed?", app.Name)
}
- recipeMeta, err := recipe.GetRecipeMeta(app.Recipe, conf)
+ if deployedVersion == "unknown" {
+ logrus.Fatalf("failed to determine version of deployed %s", app.Name)
+ }
+
+ if err := recipe.EnsureExists(app.Recipe); err != nil {
+ logrus.Fatal(err)
+ }
+
+ recipeMeta, err := recipe.GetRecipeMeta(app.Recipe, internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -100,5 +103,4 @@ version.
return nil
},
- BashComplete: autocomplete.AppNameComplete,
}
diff --git a/cli/app/volume.go b/cli/app/volume.go
index 2008c1e9..b64ce69c 100644
--- a/cli/app/volume.go
+++ b/cli/app/volume.go
@@ -7,7 +7,7 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/formatter"
- "coopcloud.tech/abra/pkg/runtime"
+ "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/AlecAivazis/survey/v2"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -20,14 +20,12 @@ var appVolumeListCommand = cli.Command{
Flags: []cli.Flag{
internal.DebugFlag,
internal.NoInputFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
Usage: "List volumes associated with an app",
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
cl, err := client.New(app.Server)
if err != nil {
@@ -83,18 +81,26 @@ Passing "--force/-f" will select all volumes for removal. Be careful.
internal.DebugFlag,
internal.NoInputFlag,
internal.ForceFlag,
- internal.OfflineFlag,
},
- Before: internal.SubCommandBefore,
+ Before: internal.SubCommandBefore,
+ BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- app := internal.ValidateApp(c, conf)
+ app := internal.ValidateApp(c)
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
}
+ isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName())
+ if err != nil {
+ logrus.Fatal(err)
+ }
+
+ if isDeployed {
+ logrus.Fatalf("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name)
+ }
+
filters, err := app.Filters(false, true)
if err != nil {
logrus.Fatal(err)
@@ -124,16 +130,19 @@ Passing "--force/-f" will select all volumes for removal. Be careful.
volumesToRemove = volumeNames
}
- err = client.RemoveVolumes(cl, context.Background(), app.Server, volumesToRemove, internal.Force)
- if err != nil {
- logrus.Fatal(err)
- }
+ if len(volumesToRemove) > 0 {
+ err = client.RemoveVolumes(cl, context.Background(), app.Server, volumesToRemove, internal.Force)
+ if err != nil {
+ logrus.Fatal(err)
+ }
- logrus.Info("volumes removed successfully")
+ logrus.Info("volumes removed successfully")
+ } else {
+ logrus.Info("no volumes removed")
+ }
return nil
},
- BashComplete: autocomplete.AppNameComplete,
}
var appVolumeCommand = cli.Command{
diff --git a/cli/catalogue/catalogue.go b/cli/catalogue/catalogue.go
index a2260acf..7c97372f 100644
--- a/cli/catalogue/catalogue.go
+++ b/cli/catalogue/catalogue.go
@@ -13,7 +13,6 @@ import (
"coopcloud.tech/abra/pkg/formatter"
gitPkg "coopcloud.tech/abra/pkg/git"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/go-git/go-git/v5"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -29,6 +28,7 @@ var catalogueGenerateCommand = cli.Command{
internal.PublishFlag,
internal.DryFlag,
internal.SkipUpdatesFlag,
+ internal.ChaosFlag,
internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
@@ -53,20 +53,22 @@ 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.
`,
- ArgsUsage: "[]",
+ ArgsUsage: "[]",
+ BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
recipeName := c.Args().First()
if recipeName != "" {
- internal.ValidateRecipe(c, conf)
+ internal.ValidateRecipe(c)
}
- if err := catalogue.EnsureUpToDate(conf); err != nil {
- logrus.Fatal(err)
+ if !internal.Chaos {
+ if err := catalogue.EnsureIsClean(); err != nil {
+ logrus.Fatal(err)
+ }
}
- repos, err := recipe.ReadReposMetadata(conf)
+ repos, err := recipe.ReadReposMetadata()
if err != nil {
logrus.Fatal(err)
}
@@ -83,7 +85,7 @@ keys configured on your account.
if !internal.SkipUpdates {
logrus.Warn(logMsg)
- if err := recipe.UpdateRepositories(repos, recipeName, conf); err != nil {
+ if err := recipe.UpdateRepositories(repos, recipeName); err != nil {
logrus.Fatal(err)
}
}
@@ -101,7 +103,7 @@ keys configured on your account.
continue
}
- versions, err := recipe.GetRecipeVersions(recipeMeta.Name, conf)
+ versions, err := recipe.GetRecipeVersions(recipeMeta.Name, internal.Offline)
if err != nil {
logrus.Warn(err)
}
@@ -137,7 +139,7 @@ keys configured on your account.
logrus.Fatal(err)
}
} else {
- catlFS, err := recipe.ReadRecipeCatalogue(conf)
+ catlFS, err := recipe.ReadRecipeCatalogue(internal.Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -211,7 +213,6 @@ keys configured on your account.
return nil
},
- BashComplete: autocomplete.RecipeNameComplete,
}
// CatalogueCommand defines the `abra catalogue` command and sub-commands.
diff --git a/cli/internal/deploy.go b/cli/internal/deploy.go
new file mode 100644
index 00000000..410fa97d
--- /dev/null
+++ b/cli/internal/deploy.go
@@ -0,0 +1,180 @@
+package internal
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+
+ "coopcloud.tech/abra/pkg/config"
+ "coopcloud.tech/abra/pkg/formatter"
+ "github.com/AlecAivazis/survey/v2"
+ dockerClient "github.com/docker/docker/client"
+ "github.com/sirupsen/logrus"
+)
+
+// NewVersionOverview shows an upgrade or downgrade overview
+func NewVersionOverview(app config.App, currentVersion, newVersion, releaseNotes string) error {
+ tableCol := []string{"server", "recipe", "config", "domain", "current version", "to be deployed"}
+ table := formatter.CreateTable(tableCol)
+
+ deployConfig := "compose.yml"
+ if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
+ deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
+ }
+
+ server := app.Server
+ if app.Server == "default" {
+ server = "local"
+ }
+
+ table.Append([]string{server, app.Recipe, deployConfig, app.Domain, currentVersion, newVersion})
+ table.Render()
+
+ if releaseNotes == "" {
+ var err error
+ releaseNotes, err = GetReleaseNotes(app.Recipe, newVersion)
+ if err != nil {
+ return err
+ }
+ }
+
+ if releaseNotes != "" && newVersion != "" {
+ fmt.Println()
+ fmt.Println(fmt.Sprintf("%s release notes:\n\n%s", newVersion, releaseNotes))
+ } else {
+ logrus.Warnf("no release notes available for %s", newVersion)
+ }
+
+ if NoInput {
+ return nil
+ }
+
+ response := false
+ prompt := &survey.Confirm{
+ Message: "continue with deployment?",
+ }
+
+ if err := survey.AskOne(prompt, &response); err != nil {
+ return err
+ }
+
+ if !response {
+ logrus.Fatal("exiting as requested")
+ }
+
+ return nil
+}
+
+// GetReleaseNotes prints release notes for a recipe version
+func GetReleaseNotes(recipeName, version string) (string, error) {
+ if version == "" {
+ return "", nil
+ }
+
+ fpath := path.Join(config.RECIPES_DIR, recipeName, "release", version)
+
+ if _, err := os.Stat(fpath); !os.IsNotExist(err) {
+ releaseNotes, err := ioutil.ReadFile(fpath)
+ if err != nil {
+ return "", err
+ }
+ return string(releaseNotes), nil
+ }
+
+ return "", nil
+}
+
+// PostCmds parses a string of commands and executes them inside of the respective services
+// the commands string must have the following format:
+// " | |... "
+func PostCmds(cl *dockerClient.Client, app config.App, commands string) error {
+ abraSh := path.Join(config.RECIPES_DIR, app.Recipe, "abra.sh")
+ if _, err := os.Stat(abraSh); err != nil {
+ if os.IsNotExist(err) {
+ return fmt.Errorf(fmt.Sprintf("%s does not exist for %s?", abraSh, app.Name))
+ }
+ return err
+ }
+
+ for _, command := range strings.Split(commands, "|") {
+ commandParts := strings.Split(command, " ")
+ if len(commandParts) < 2 {
+ return fmt.Errorf(fmt.Sprintf("not enough arguments: %s", command))
+ }
+ targetServiceName := commandParts[0]
+ cmdName := commandParts[1]
+ parsedCmdArgs := ""
+ if len(commandParts) > 2 {
+ parsedCmdArgs = fmt.Sprintf("%s ", strings.Join(commandParts[2:], " "))
+ }
+ logrus.Infof("running post-command '%s %s' in container %s", cmdName, parsedCmdArgs, targetServiceName)
+
+ if err := EnsureCommand(abraSh, app.Recipe, cmdName); err != nil {
+ return err
+ }
+
+ serviceNames, err := config.GetAppServiceNames(app.Name)
+ if err != nil {
+ return err
+ }
+
+ matchingServiceName := false
+ for _, serviceName := range serviceNames {
+ if serviceName == targetServiceName {
+ matchingServiceName = true
+ }
+ }
+
+ if !matchingServiceName {
+ return fmt.Errorf(fmt.Sprintf("no service %s for %s?", targetServiceName, app.Name))
+ }
+
+ logrus.Debugf("running command %s %s within the context of %s_%s", cmdName, parsedCmdArgs, app.StackName(), targetServiceName)
+
+ Tty = true
+ if err := RunCmdRemote(cl, app, abraSh, targetServiceName, cmdName, parsedCmdArgs); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// DeployOverview shows a deployment overview
+func DeployOverview(app config.App, version, message string) error {
+ tableCol := []string{"server", "recipe", "config", "domain", "version"}
+ table := formatter.CreateTable(tableCol)
+
+ deployConfig := "compose.yml"
+ if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
+ deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
+ }
+
+ server := app.Server
+ if app.Server == "default" {
+ server = "local"
+ }
+
+ table.Append([]string{server, app.Recipe, deployConfig, app.Domain, version})
+ table.Render()
+
+ if NoInput {
+ return nil
+ }
+
+ response := false
+ prompt := &survey.Confirm{
+ Message: message,
+ }
+
+ if err := survey.AskOne(prompt, &response); err != nil {
+ return err
+ }
+
+ if !response {
+ logrus.Fatal("exiting as requested")
+ }
+
+ return nil
+}
diff --git a/cli/internal/validate.go b/cli/internal/validate.go
index 05bc7fdb..46a442d6 100644
--- a/cli/internal/validate.go
+++ b/cli/internal/validate.go
@@ -7,53 +7,19 @@ import (
"coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/AlecAivazis/survey/v2"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
// ValidateRecipe ensures the recipe arg is valid.
-func ValidateRecipe(c *cli.Context, conf *runtime.Config) recipe.Recipe {
- recipeName := c.Args().First()
-
- if recipeName == "" {
- ShowSubcommandHelpAndError(c, errors.New("no recipe name provided"))
- }
-
- chosenRecipe, err := recipe.Get(recipeName, conf)
- if err != nil {
- if c.Command.Name == "generate" {
- if strings.Contains(err.Error(), "missing a compose") {
- logrus.Fatal(err)
- }
- logrus.Warn(err)
- } else {
- if strings.Contains(err.Error(), "template_driver is not allowed") {
- logrus.Warnf("ensure %s recipe compose.* files include \"version: '3.8'\"", recipeName)
- }
- logrus.Fatalf("unable to validate recipe: %s", err)
- }
- }
-
- if err := recipe.EnsureLatest(recipeName, conf); err != nil {
- logrus.Fatal(err)
- }
-
- logrus.Debugf("validated %s as recipe argument", recipeName)
-
- return chosenRecipe
-}
-
-// ValidateRecipeWithPrompt ensures a recipe argument is present before
-// validating, asking for input if required.
-func ValidateRecipeWithPrompt(c *cli.Context, conf *runtime.Config) recipe.Recipe {
+func ValidateRecipe(c *cli.Context) recipe.Recipe {
recipeName := c.Args().First()
if recipeName == "" && !NoInput {
var recipes []string
- catl, err := recipe.ReadRecipeCatalogue(conf)
+ catl, err := recipe.ReadRecipeCatalogue(Offline)
if err != nil {
logrus.Fatal(err)
}
@@ -91,13 +57,19 @@ func ValidateRecipeWithPrompt(c *cli.Context, conf *runtime.Config) recipe.Recip
ShowSubcommandHelpAndError(c, errors.New("no recipe name provided"))
}
- chosenRecipe, err := recipe.Get(recipeName, conf)
+ chosenRecipe, err := recipe.Get(recipeName, Offline)
if err != nil {
- logrus.Fatal(err)
- }
-
- if err := recipe.EnsureLatest(recipeName, conf); err != nil {
- logrus.Fatal(err)
+ if c.Command.Name == "generate" {
+ if strings.Contains(err.Error(), "missing a compose") {
+ logrus.Fatal(err)
+ }
+ logrus.Warn(err)
+ } else {
+ if strings.Contains(err.Error(), "template_driver is not allowed") {
+ logrus.Warnf("ensure %s recipe compose.* files include \"version: '3.8'\"", recipeName)
+ }
+ logrus.Fatalf("unable to validate recipe: %s", err)
+ }
}
logrus.Debugf("validated %s as recipe argument", recipeName)
@@ -106,7 +78,7 @@ func ValidateRecipeWithPrompt(c *cli.Context, conf *runtime.Config) recipe.Recip
}
// ValidateApp ensures the app name arg is valid.
-func ValidateApp(c *cli.Context, conf *runtime.Config) config.App {
+func ValidateApp(c *cli.Context) config.App {
appName := c.Args().First()
if appName == "" {
@@ -118,10 +90,6 @@ func ValidateApp(c *cli.Context, conf *runtime.Config) config.App {
logrus.Fatal(err)
}
- if err := recipe.EnsureExists(app.Recipe, conf); err != nil {
- logrus.Fatal(err)
- }
-
logrus.Debugf("validated %s as app argument", appName)
return app
@@ -190,14 +158,14 @@ func ValidateServer(c *cli.Context) string {
}
}
- if !matched {
- ShowSubcommandHelpAndError(c, errors.New("server doesn't exist?"))
- }
-
if serverName == "" {
ShowSubcommandHelpAndError(c, errors.New("no server provided"))
}
+ if !matched {
+ ShowSubcommandHelpAndError(c, errors.New("server doesn't exist?"))
+ }
+
logrus.Debugf("validated %s as server argument", serverName)
return serverName
diff --git a/cli/recipe/fetch.go b/cli/recipe/fetch.go
index 17d6b59c..4996ba62 100644
--- a/cli/recipe/fetch.go
+++ b/cli/recipe/fetch.go
@@ -4,7 +4,6 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -17,25 +16,26 @@ var recipeFetchCommand = cli.Command{
Description: "Fetchs all recipes without arguments.",
Flags: []cli.Flag{
internal.DebugFlag,
- internal.OfflineFlag,
+ internal.NoInputFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
recipeName := c.Args().First()
if recipeName != "" {
- internal.ValidateRecipe(c, conf)
- return nil // ValidateRecipe ensures latest checkout
+ internal.ValidateRecipe(c)
}
- repos, err := recipe.ReadReposMetadata(conf)
- if err != nil {
+ if err := recipe.EnsureExists(recipeName); err != nil {
logrus.Fatal(err)
}
- if err := recipe.UpdateRepositories(repos, recipeName, conf); err != nil {
+ if err := recipe.EnsureUpToDate(recipeName); err != nil {
+ logrus.Fatal(err)
+ }
+
+ if err := recipe.EnsureLatest(recipeName); err != nil {
logrus.Fatal(err)
}
diff --git a/cli/recipe/lint.go b/cli/recipe/lint.go
index 623872e6..dfeba1a2 100644
--- a/cli/recipe/lint.go
+++ b/cli/recipe/lint.go
@@ -7,8 +7,6 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/lint"
- recipePkg "coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -22,16 +20,12 @@ var recipeLintCommand = cli.Command{
internal.DebugFlag,
internal.OnlyErrorFlag,
internal.OfflineFlag,
+ internal.NoInputFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- recipe := internal.ValidateRecipe(c, conf)
-
- if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
- logrus.Fatal(err)
- }
+ recipe := internal.ValidateRecipe(c)
tableCol := []string{"ref", "rule", "severity", "satisfied", "skipped", "resolve"}
table := formatter.CreateTable(tableCol)
diff --git a/cli/recipe/list.go b/cli/recipe/list.go
index 846a5d11..4ad35476 100644
--- a/cli/recipe/list.go
+++ b/cli/recipe/list.go
@@ -9,7 +9,6 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -34,9 +33,7 @@ var recipeListCommand = cli.Command{
},
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
-
- catl, err := recipe.ReadRecipeCatalogue(conf)
+ catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
if err != nil {
logrus.Fatal(err.Error())
}
diff --git a/cli/recipe/release.go b/cli/recipe/release.go
index 82d490f4..7d25d93c 100644
--- a/cli/recipe/release.go
+++ b/cli/recipe/release.go
@@ -13,7 +13,6 @@ import (
gitPkg "coopcloud.tech/abra/pkg/git"
"coopcloud.tech/abra/pkg/recipe"
recipePkg "coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/tagcmp"
"github.com/AlecAivazis/survey/v2"
"github.com/docker/distribution/reference"
@@ -60,12 +59,15 @@ your SSH keys configured on your account.
Before: internal.SubCommandBefore,
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(
- runtime.WithOffline(internal.Offline),
- runtime.WithEnsureRecipeUpToDate(false),
- )
+ recipe := internal.ValidateRecipe(c)
- recipe := internal.ValidateRecipeWithPrompt(c, conf)
+ if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
+ logrus.Fatal(err)
+ }
+
+ if err := recipePkg.EnsureLatest(recipe.Name); err != nil {
+ logrus.Fatal(err)
+ }
imagesTmp, err := getImageVersions(recipe)
if err != nil {
diff --git a/cli/recipe/sync.go b/cli/recipe/sync.go
index 39f4189d..0bd52fc9 100644
--- a/cli/recipe/sync.go
+++ b/cli/recipe/sync.go
@@ -8,7 +8,6 @@ import (
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/config"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/tagcmp"
"github.com/AlecAivazis/survey/v2"
"github.com/go-git/go-git/v5"
@@ -29,7 +28,6 @@ var recipeSyncCommand = cli.Command{
internal.MajorFlag,
internal.MinorFlag,
internal.PatchFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
Description: `
@@ -42,13 +40,9 @@ Where can be specifed on the command-line or Abra can attempt to
auto-generate it for you. The configuration will be updated on the
local file system.
`,
+ BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(
- runtime.WithOffline(internal.Offline),
- runtime.WithEnsureRecipeUpToDate(false),
- )
-
- recipe := internal.ValidateRecipeWithPrompt(c, conf)
+ recipe := internal.ValidateRecipe(c)
mainApp, err := internal.GetMainAppImage(recipe)
if err != nil {
@@ -203,5 +197,4 @@ likely to change.
return nil
},
- BashComplete: autocomplete.RecipeNameComplete,
}
diff --git a/cli/recipe/upgrade.go b/cli/recipe/upgrade.go
index b152568f..fe7bbf83 100644
--- a/cli/recipe/upgrade.go
+++ b/cli/recipe/upgrade.go
@@ -15,7 +15,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
recipePkg "coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/tagcmp"
"github.com/AlecAivazis/survey/v2"
"github.com/docker/distribution/reference"
@@ -58,8 +57,7 @@ You may invoke this command in "wizard" mode and be prompted for input:
abra recipe upgrade
`,
- BashComplete: autocomplete.RecipeNameComplete,
- ArgsUsage: "",
+ ArgsUsage: "",
Flags: []cli.Flag{
internal.DebugFlag,
internal.NoInputFlag,
@@ -70,12 +68,16 @@ You may invoke this command in "wizard" mode and be prompted for input:
internal.AllTagsFlag,
internal.OfflineFlag,
},
- Before: internal.SubCommandBefore,
+ Before: internal.SubCommandBefore,
+ BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
- recipe := internal.ValidateRecipeWithPrompt(c, conf)
+ recipe := internal.ValidateRecipe(c)
- if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
+ if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
+ logrus.Fatal(err)
+ }
+
+ if err := recipePkg.EnsureLatest(recipe.Name); err != nil {
logrus.Fatal(err)
}
@@ -185,7 +187,7 @@ You may invoke this command in "wizard" mode and be prompted for input:
continue // skip on to the next tag and don't update any compose files
}
- catlVersions, err := recipePkg.VersionsOfService(recipe.Name, service.Name, conf)
+ catlVersions, err := recipePkg.VersionsOfService(recipe.Name, service.Name, internal.Offline)
if err != nil {
logrus.Fatal(err)
}
diff --git a/cli/recipe/version.go b/cli/recipe/version.go
index 4852a93f..5e1e7ca6 100644
--- a/cli/recipe/version.go
+++ b/cli/recipe/version.go
@@ -5,7 +5,6 @@ import (
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/formatter"
recipePkg "coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -18,23 +17,19 @@ var recipeVersionCommand = cli.Command{
Flags: []cli.Flag{
internal.DebugFlag,
internal.OfflineFlag,
+ internal.NoInputFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
- conf := runtime.New(
- runtime.WithOffline(internal.Offline),
- runtime.WithEnsureRecipeUpToDate(false),
- )
+ recipe := internal.ValidateRecipe(c)
- recipe := internal.ValidateRecipe(c, conf)
-
- catalogue, err := recipePkg.ReadRecipeCatalogue(conf)
+ catl, err := recipePkg.ReadRecipeCatalogue(internal.Offline)
if err != nil {
logrus.Fatal(err)
}
- recipeMeta, ok := catalogue[recipe.Name]
+ recipeMeta, ok := catl[recipe.Name]
if !ok {
logrus.Fatalf("%s recipe doesn't exist?", recipe.Name)
}
diff --git a/cli/server/add.go b/cli/server/add.go
index 098bcdae..c4bf6f13 100644
--- a/cli/server/add.go
+++ b/cli/server/add.go
@@ -118,7 +118,6 @@ developer machine.
internal.DebugFlag,
internal.NoInputFlag,
localFlag,
- internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
ArgsUsage: "",
diff --git a/cli/server/list.go b/cli/server/list.go
index 3da5a816..6eaad343 100644
--- a/cli/server/list.go
+++ b/cli/server/list.go
@@ -60,6 +60,17 @@ var serverListCommand = cli.Command{
if err != nil {
logrus.Fatal(err)
}
+
+ if sp.Host == "" {
+ sp.Host = "unknown"
+ }
+ if sp.User == "" {
+ sp.User = "unknown"
+ }
+ if sp.Port == "" {
+ sp.Port = "unknown"
+ }
+
row = []string{serverName, sp.Host, sp.User, sp.Port}
}
}
@@ -73,8 +84,11 @@ var serverListCommand = cli.Command{
}
if problemsFilter {
- if row[1] == "unknown" {
- table.Append(row)
+ for _, val := range row {
+ if val == "unknown" {
+ table.Append(row)
+ break
+ }
}
} else {
table.Append(row)
diff --git a/cli/server/prune.go b/cli/server/prune.go
index a64734d8..8945aadf 100644
--- a/cli/server/prune.go
+++ b/cli/server/prune.go
@@ -20,12 +20,12 @@ var allFilterFlag = &cli.BoolFlag{
Destination: &allFilter,
}
-var volunesFilter bool
+var volumesFilter bool
var volumesFilterFlag = &cli.BoolFlag{
Name: "volumes, v",
Usage: "Prune volumes. This will remove app data, Be Careful!",
- Destination: &volunesFilter,
+ Destination: &volumesFilter,
}
var serverPruneCommand = cli.Command{
@@ -35,7 +35,7 @@ var serverPruneCommand = cli.Command{
Description: `
Prunes unused containers, networks, and dangling images.
-If passing "-v/--volumes" then volumes not connected with a deployed app will
+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.
`,
ArgsUsage: "[]",
@@ -44,12 +44,11 @@ also be removed. This can result in unwanted data loss if not used carefully.
volumesFilterFlag,
internal.DebugFlag,
internal.OfflineFlag,
+ internal.NoInputFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.ServerNameComplete,
Action: func(c *cli.Context) error {
- var args filters.Args
-
serverName := internal.ValidateServer(c)
cl, err := client.New(serverName)
@@ -57,6 +56,8 @@ also be removed. This can result in unwanted data loss if not used carefully.
logrus.Fatal(err)
}
+ var args filters.Args
+
ctx := context.Background()
cr, err := cl.ContainersPrune(ctx, args)
if err != nil {
@@ -75,6 +76,7 @@ also be removed. This can result in unwanted data loss if not used carefully.
pruneFilters := filters.NewArgs()
if allFilter {
+ logrus.Debugf("removing all images, not only dangling ones")
pruneFilters.Add("dangling", "false")
}
@@ -86,7 +88,7 @@ also be removed. This can result in unwanted data loss if not used carefully.
imgSpaceReclaimed := formatter.ByteCountSI(ir.SpaceReclaimed)
logrus.Infof("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed)
- if volunesFilter {
+ if volumesFilter {
vr, err := cl.VolumesPrune(ctx, args)
if err != nil {
logrus.Fatal(err)
diff --git a/cli/server/remove.go b/cli/server/remove.go
index 196dc421..83817c5a 100644
--- a/cli/server/remove.go
+++ b/cli/server/remove.go
@@ -15,7 +15,7 @@ import (
var serverRemoveCommand = cli.Command{
Name: "remove",
Aliases: []string{"rm"},
- ArgsUsage: "[]",
+ ArgsUsage: "",
Usage: "Remove a managed server",
Description: `Remove a managed server.
diff --git a/cli/updater/updater.go b/cli/updater/updater.go
index 3ed7d787..5e6d14ad 100644
--- a/cli/updater/updater.go
+++ b/cli/updater/updater.go
@@ -12,7 +12,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/lint"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/upstream/convert"
"coopcloud.tech/abra/pkg/upstream/stack"
"coopcloud.tech/tagcmp"
@@ -58,8 +57,6 @@ catalogue. If a new patch/minor version is available, a notification is
printed. To include major versions use the --major flag.
`,
Action: func(c *cli.Context) error {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
-
cl, err := client.New("default")
if err != nil {
logrus.Fatal(err)
@@ -78,7 +75,7 @@ printed. To include major versions use the --major flag.
}
if recipeName != "" {
- _, err = getLatestUpgrade(cl, stackName, recipeName, conf)
+ _, err = getLatestUpgrade(cl, stackName, recipeName)
if err != nil {
logrus.Fatal(err)
}
@@ -113,8 +110,6 @@ 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 {
- conf := runtime.New(runtime.WithOffline(internal.Offline))
-
cl, err := client.New("default")
if err != nil {
logrus.Fatal(err)
@@ -123,7 +118,7 @@ update chaos deployments use the "--chaos" flag. Use it with care.
if !updateAll {
stackName := c.Args().Get(0)
recipeName := c.Args().Get(1)
- err = tryUpgrade(cl, stackName, recipeName, conf)
+ err = tryUpgrade(cl, stackName, recipeName)
if err != nil {
logrus.Fatal(err)
}
@@ -143,7 +138,7 @@ update chaos deployments use the "--chaos" flag. Use it with care.
logrus.Fatal(err)
}
- err = tryUpgrade(cl, stackName, recipeName, conf)
+ err = tryUpgrade(cl, stackName, recipeName)
if err != nil {
logrus.Fatal(err)
}
@@ -227,14 +222,13 @@ func getEnv(cl *dockerclient.Client, stackName string) (config.AppEnv, error) {
// getLatestUpgrade returns the latest available version for an app respecting
// the "--major" flag if it is newer than the currently deployed version.
-func getLatestUpgrade(cl *dockerclient.Client, stackName string,
- recipeName string, conf *runtime.Config) (string, error) {
+func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) (string, error) {
deployedVersion, err := getDeployedVersion(cl, stackName, recipeName)
if err != nil {
return "", err
}
- availableUpgrades, err := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion, conf)
+ availableUpgrades, err := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion)
if err != nil {
return "", err
}
@@ -277,8 +271,8 @@ func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName st
// than the deployed version. It only includes major upgrades if the "--major"
// flag is set.
func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string,
- deployedVersion string, conf *runtime.Config) ([]string, error) {
- catl, err := recipe.ReadRecipeCatalogue(conf)
+ deployedVersion string) ([]string, error) {
+ catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
if err != nil {
return nil, err
}
@@ -322,12 +316,12 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName
// processRecipeRepoVersion clones, pulls, checks out the version and lints the
// recipe repository.
-func processRecipeRepoVersion(recipeName, version string, conf *runtime.Config) error {
- if err := recipe.EnsureExists(recipeName, conf); err != nil {
+func processRecipeRepoVersion(recipeName, version string) error {
+ if err := recipe.EnsureExists(recipeName); err != nil {
return err
}
- if err := recipe.EnsureUpToDate(recipeName, conf); err != nil {
+ if err := recipe.EnsureUpToDate(recipeName); err != nil {
return err
}
@@ -335,7 +329,7 @@ func processRecipeRepoVersion(recipeName, version string, conf *runtime.Config)
return err
}
- if r, err := recipe.Get(recipeName, conf); err != nil {
+ if r, err := recipe.Get(recipeName, internal.Offline); err != nil {
return err
} else if err := lint.LintForErrors(r); err != nil {
return err
@@ -392,7 +386,7 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv)
}
// tryUpgrade performs the upgrade if all the requirements are fulfilled.
-func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string, conf *runtime.Config) error {
+func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string) error {
if recipeName == "" {
logrus.Debugf("don't update %s due to missing recipe name", stackName)
return nil
@@ -418,7 +412,7 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string, conf *run
return nil
}
- upgradeVersion, err := getLatestUpgrade(cl, stackName, recipeName, conf)
+ upgradeVersion, err := getLatestUpgrade(cl, stackName, recipeName)
if err != nil {
return err
}
@@ -428,14 +422,14 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string, conf *run
return nil
}
- err = upgrade(cl, stackName, recipeName, upgradeVersion, conf)
+ err = upgrade(cl, stackName, recipeName, upgradeVersion)
return err
}
// upgrade performs all necessary steps to upgrade an app.
func upgrade(cl *dockerclient.Client, stackName, recipeName,
- upgradeVersion string, conf *runtime.Config) error {
+ upgradeVersion string) error {
env, err := getEnv(cl, stackName)
if err != nil {
return err
@@ -448,7 +442,7 @@ func upgrade(cl *dockerclient.Client, stackName, recipeName,
Env: env,
}
- if err = processRecipeRepoVersion(recipeName, upgradeVersion, conf); err != nil {
+ if err = processRecipeRepoVersion(recipeName, upgradeVersion); err != nil {
return err
}
diff --git a/pkg/autocomplete/autocomplete.go b/pkg/autocomplete/autocomplete.go
index c952ba57..807422fb 100644
--- a/pkg/autocomplete/autocomplete.go
+++ b/pkg/autocomplete/autocomplete.go
@@ -5,7 +5,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -28,12 +27,7 @@ func AppNameComplete(c *cli.Context) {
// RecipeNameComplete completes recipe names.
func RecipeNameComplete(c *cli.Context) {
- // defaults since we can't take arguments here... this means auto-completion
- // of recipe names always access the network if e.g. the catalogue needs
- // cloning / updating
- conf := runtime.New()
-
- catl, err := recipe.ReadRecipeCatalogue(conf)
+ catl, err := recipe.ReadRecipeCatalogue(false)
if err != nil {
logrus.Warn(err)
}
diff --git a/pkg/catalogue/catalogue.go b/pkg/catalogue/catalogue.go
index 5a603f5c..413aec61 100644
--- a/pkg/catalogue/catalogue.go
+++ b/pkg/catalogue/catalogue.go
@@ -8,7 +8,6 @@ import (
"coopcloud.tech/abra/pkg/config"
gitPkg "coopcloud.tech/abra/pkg/git"
- "coopcloud.tech/abra/pkg/runtime"
"github.com/go-git/go-git/v5"
"github.com/sirupsen/logrus"
)
@@ -54,13 +53,9 @@ var CatalogueSkipList = map[string]bool{
}
// EnsureCatalogue ensures that the catalogue is cloned locally & present.
-func EnsureCatalogue(conf *runtime.Config) error {
+func EnsureCatalogue() error {
catalogueDir := path.Join(config.ABRA_DIR, "catalogue")
if _, err := os.Stat(catalogueDir); err != nil && os.IsNotExist(err) {
- if conf.Offline {
- return fmt.Errorf("no local copy of the catalogue available, network access required")
- }
-
url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME)
if err := gitPkg.Clone(catalogueDir, url); err != nil {
return err
@@ -72,9 +67,8 @@ func EnsureCatalogue(conf *runtime.Config) error {
return nil
}
-// EnsureUpToDate ensures that the local catalogue has no unstaged changes as
-// is up to date. This is useful to run before doing catalogue generation.
-func EnsureUpToDate(conf *runtime.Config) error {
+// EnsureIsClean makes sure that the catalogue has no unstaged changes.
+func EnsureIsClean() error {
isClean, err := gitPkg.IsClean(config.CATALOGUE_DIR)
if err != nil {
return err
@@ -85,11 +79,11 @@ func EnsureUpToDate(conf *runtime.Config) error {
return fmt.Errorf(msg, config.CATALOGUE_DIR)
}
- if conf.Offline {
- logrus.Debug("attempting to use local catalogue without access network (\"--offline\")")
- return nil
- }
+ return nil
+}
+// EnsureUpToDate ensures that the local catalogue is up to date.
+func EnsureUpToDate() error {
repo, err := git.PlainOpen(config.CATALOGUE_DIR)
if err != nil {
return err
diff --git a/pkg/config/app.go b/pkg/config/app.go
index d86ba4f1..0ad683d2 100644
--- a/pkg/config/app.go
+++ b/pkg/config/app.go
@@ -326,7 +326,7 @@ func TemplateAppEnvSample(recipeName, appName, server, domain string) error {
}
appEnvPath := path.Join(ABRA_DIR, "servers", server, fmt.Sprintf("%s.env", appName))
- if _, err := os.Stat(appEnvPath); os.IsExist(err) {
+ if _, err := os.Stat(appEnvPath); !os.IsNotExist(err) {
return fmt.Errorf("%s already exists?", appEnvPath)
}
diff --git a/pkg/lint/recipe.go b/pkg/lint/recipe.go
index 10233f92..cae797f3 100644
--- a/pkg/lint/recipe.go
+++ b/pkg/lint/recipe.go
@@ -9,7 +9,6 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/recipe"
recipePkg "coopcloud.tech/abra/pkg/recipe"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/tagcmp"
"github.com/docker/distribution/reference"
"github.com/go-git/go-git/v5"
@@ -334,11 +333,7 @@ func LintImagePresent(recipe recipe.Recipe) (bool, error) {
}
func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) {
- // defaults since we can't take arguments here... this means this lint rule
- // always access the network if e.g. the catalogue needs cloning / updating
- conf := runtime.New()
-
- catl, err := recipePkg.ReadRecipeCatalogue(conf)
+ catl, err := recipePkg.ReadRecipeCatalogue(false)
if err != nil {
logrus.Fatal(err)
}
diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go
index ca5aabfc..92023379 100644
--- a/pkg/recipe/recipe.go
+++ b/pkg/recipe/recipe.go
@@ -17,7 +17,6 @@ import (
"coopcloud.tech/abra/pkg/formatter"
gitPkg "coopcloud.tech/abra/pkg/git"
"coopcloud.tech/abra/pkg/limit"
- "coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/upstream/stack"
loader "coopcloud.tech/abra/pkg/upstream/stack"
"coopcloud.tech/abra/pkg/web"
@@ -206,8 +205,8 @@ func (r Recipe) Tags() ([]string, error) {
}
// Get retrieves a recipe.
-func Get(recipeName string, conf *runtime.Config) (Recipe, error) {
- if err := EnsureExists(recipeName, conf); err != nil {
+func Get(recipeName string, offline bool) (Recipe, error) {
+ if err := EnsureExists(recipeName); err != nil {
return Recipe{}, err
}
@@ -233,7 +232,7 @@ func Get(recipeName string, conf *runtime.Config) (Recipe, error) {
return Recipe{}, err
}
- meta, err := GetRecipeMeta(recipeName, conf)
+ meta, err := GetRecipeMeta(recipeName, offline)
if err != nil {
switch err.(type) {
case RecipeMissingFromCatalogue:
@@ -251,19 +250,10 @@ func Get(recipeName string, conf *runtime.Config) (Recipe, error) {
}
// EnsureExists ensures that a recipe is locally cloned
-func EnsureExists(recipeName string, conf *runtime.Config) error {
- if !conf.RecipeExists {
- logrus.Debug("skipping ensuring recipe locally exists")
- return nil
- }
-
+func EnsureExists(recipeName string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
if _, err := os.Stat(recipeDir); os.IsNotExist(err) {
- if conf.Offline {
- return fmt.Errorf("no local copy of %s available, network access required", recipeName)
- }
-
logrus.Debugf("%s does not exist, attemmpting to clone", recipeDir)
url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipeName)
if err := gitPkg.Clone(recipeDir, url); err != nil {
@@ -282,15 +272,6 @@ func EnsureExists(recipeName string, conf *runtime.Config) error {
func EnsureVersion(recipeName, version string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
- isClean, err := gitPkg.IsClean(recipeDir)
- if err != nil {
- return err
- }
-
- if !isClean {
- return fmt.Errorf("%s has locally unstaged changes", recipeName)
- }
-
if err := gitPkg.EnsureGitRepo(recipeDir); err != nil {
return err
}
@@ -342,30 +323,31 @@ func EnsureVersion(recipeName, version string) error {
return nil
}
-// EnsureLatest makes sure the latest commit is checked out for a local recipe repository
-func EnsureLatest(recipeName string, conf *runtime.Config) error {
- if !conf.RecipeLatest {
- logrus.Debug("skipping ensuring recipe is synced with remote")
- return nil
- }
-
+// EnsureIsClean makes sure that the recipe repository has no unstaged changes.
+func EnsureIsClean(recipeName string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
isClean, err := gitPkg.IsClean(recipeDir)
if err != nil {
- return err
+ return fmt.Errorf("unable to check git clean status in %s: %s", recipeDir, err)
}
if !isClean {
- return fmt.Errorf("%s has locally unstaged changes", recipeName)
+ msg := "%s (%s) has locally unstaged changes? please commit/remove your changes before proceeding"
+ return fmt.Errorf(msg, recipeName, recipeDir)
}
+ return nil
+}
+
+// EnsureLatest makes sure the latest commit is checked out for a local recipe repository
+func EnsureLatest(recipeName string) error {
+ recipeDir := path.Join(config.RECIPES_DIR, recipeName)
+
if err := gitPkg.EnsureGitRepo(recipeDir); err != nil {
return err
}
- logrus.Debugf("attempting to open git repository in %s", recipeDir)
-
repo, err := git.PlainOpen(recipeDir)
if err != nil {
return err
@@ -376,24 +358,9 @@ func EnsureLatest(recipeName string, conf *runtime.Config) error {
return err
}
- meta, err := GetRecipeMeta(recipeName, conf)
+ branch, err := gitPkg.GetDefaultBranch(repo, recipeDir)
if err != nil {
- switch err.(type) {
- case RecipeMissingFromCatalogue:
- meta = RecipeMeta{}
- default:
- return err
- }
- }
-
- var branch plumbing.ReferenceName
- if meta.DefaultBranch != "" {
- branch = plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", meta.DefaultBranch))
- } else {
- branch, err = gitPkg.GetDefaultBranch(repo, recipeDir)
- if err != nil {
- return err
- }
+ return err
}
checkOutOpts := &git.CheckoutOptions{
@@ -598,29 +565,9 @@ func GetStringInBetween(recipeName, str, start, end string) (result string, err
}
// EnsureUpToDate ensures that the local repo is synced to the remote
-func EnsureUpToDate(recipeName string, conf *runtime.Config) error {
- if !conf.RecipeLatest {
- logrus.Debug("skipping ensuring recipe is synced with remote")
- return nil
- }
-
+func EnsureUpToDate(recipeName string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
- isClean, err := gitPkg.IsClean(recipeDir)
- if err != nil {
- return fmt.Errorf("unable to check git clean status in %s: %s", recipeDir, err)
- }
-
- if !isClean {
- msg := "%s (%s) has locally unstaged changes? please commit/remove your changes before proceeding"
- return fmt.Errorf(msg, recipeName, recipeDir)
- }
-
- if conf.Offline {
- logrus.Debug("attempting to use local recipe without access network (\"--offline\")")
- return nil
- }
-
repo, err := git.PlainOpen(recipeDir)
if err != nil {
return fmt.Errorf("unable to open %s: %s", recipeDir, err)
@@ -671,15 +618,17 @@ func EnsureUpToDate(recipeName string, conf *runtime.Config) error {
}
// ReadRecipeCatalogue reads the recipe catalogue.
-func ReadRecipeCatalogue(conf *runtime.Config) (RecipeCatalogue, error) {
+func ReadRecipeCatalogue(offline bool) (RecipeCatalogue, error) {
recipes := make(RecipeCatalogue)
- if err := catalogue.EnsureCatalogue(conf); err != nil {
+ if err := catalogue.EnsureCatalogue(); err != nil {
return nil, err
}
- if err := catalogue.EnsureUpToDate(conf); err != nil {
- return nil, err
+ if !offline {
+ if err := catalogue.EnsureUpToDate(); err != nil {
+ return nil, err
+ }
}
if err := readRecipeCatalogueFS(&recipes); err != nil {
@@ -706,10 +655,10 @@ func readRecipeCatalogueFS(target interface{}) error {
}
// VersionsOfService lists the version of a service.
-func VersionsOfService(recipe, serviceName string, conf *runtime.Config) ([]string, error) {
+func VersionsOfService(recipe, serviceName string, offline bool) ([]string, error) {
var versions []string
- catalogue, err := ReadRecipeCatalogue(conf)
+ catalogue, err := ReadRecipeCatalogue(offline)
if err != nil {
return nil, err
}
@@ -743,8 +692,8 @@ func (r RecipeMissingFromCatalogue) Error() string {
}
// GetRecipeMeta retrieves the recipe metadata from the recipe catalogue.
-func GetRecipeMeta(recipeName string, conf *runtime.Config) (RecipeMeta, error) {
- catl, err := ReadRecipeCatalogue(conf)
+func GetRecipeMeta(recipeName string, offline bool) (RecipeMeta, error) {
+ catl, err := ReadRecipeCatalogue(offline)
if err != nil {
return RecipeMeta{}, err
}
@@ -756,10 +705,6 @@ func GetRecipeMeta(recipeName string, conf *runtime.Config) (RecipeMeta, error)
}
}
- if err := EnsureExists(recipeName, conf); err != nil {
- return RecipeMeta{}, err
- }
-
logrus.Debugf("recipe metadata retrieved for %s", recipeName)
return recipeMeta, nil
@@ -843,11 +788,7 @@ type InternalTracker struct {
type RepoCatalogue map[string]RepoMeta
// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
-func ReadReposMetadata(conf *runtime.Config) (RepoCatalogue, error) {
- if conf.Offline {
- return nil, fmt.Errorf("network access required to query recipes metadata")
- }
-
+func ReadReposMetadata() (RepoCatalogue, error) {
reposMeta := make(RepoCatalogue)
pageIdx := 1
@@ -882,7 +823,7 @@ func ReadReposMetadata(conf *runtime.Config) (RepoCatalogue, error) {
}
// GetRecipeVersions retrieves all recipe versions.
-func GetRecipeVersions(recipeName string, conf *runtime.Config) (RecipeVersions, error) {
+func GetRecipeVersions(recipeName string, offline bool) (RecipeVersions, error) {
versions := RecipeVersions{}
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
@@ -920,7 +861,7 @@ func GetRecipeVersions(recipeName string, conf *runtime.Config) (RecipeVersions,
logrus.Debugf("successfully checked out %s in %s", ref.Name(), recipeDir)
- recipe, err := Get(recipeName, conf)
+ recipe, err := Get(recipeName, offline)
if err != nil {
return err
}
@@ -1027,11 +968,7 @@ func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]stri
}
// UpdateRepositories clones and updates all recipe repositories locally.
-func UpdateRepositories(repos RepoCatalogue, recipeName string, conf *runtime.Config) error {
- if conf.Offline {
- return fmt.Errorf("network access required to update recipes")
- }
-
+func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
var barLength int
if recipeName != "" {
barLength = 1
@@ -1065,10 +1002,6 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, conf *runtime.Co
logrus.Fatal(err)
}
- if err := EnsureUpToDate(rm.Name, conf); err != nil {
- logrus.Fatal(err)
- }
-
ch <- rm.Name
retrieveBar.Add(1)
}(repoMeta)
diff --git a/pkg/runtime/config.go b/pkg/runtime/config.go
deleted file mode 100644
index cd0fa2a9..00000000
--- a/pkg/runtime/config.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package runtime
-
-import "github.com/sirupsen/logrus"
-
-// Config is a runtime behaviour modifier.
-type Config struct {
- Offline bool // Whether or not Abra should prefer local / offline access
- RecipeExists bool // Whether or not Abra should ensure the recipe is locally cloned
- RecipeLatest bool // Whether or not Abra should ensure the recipe has the latest commit
-}
-
-// Option is a runtime configuration option.
-type Option func(c *Config)
-
-// New creates a new runtime configuration.
-func New(opts ...Option) *Config {
- conf := &Config{
- Offline: false,
- RecipeExists: true,
- RecipeLatest: true,
- }
-
- for _, optFunc := range opts {
- optFunc(conf)
- }
-
- return conf
-}
-
-// WithOffline ensures Abra attempts to prefer local file system and offline
-// access.
-func WithOffline(offline bool) Option {
- return func(c *Config) {
- if offline {
- logrus.Debugf("runtime config: attempting to run in offline mode")
- }
- c.Offline = offline
- }
-}
-
-// WithEnsureRecipeExists ensures recipe exists locally.
-func WithEnsureRecipeExists(exists bool) Option {
- return func(c *Config) {
- if exists {
- logrus.Debugf("runtime config: ensuring recipe exists")
- }
- c.RecipeExists = exists
- }
-}
-
-// WithEnsureRecipeUpToDate ensures recipe is synced with the remote.
-func WithEnsureRecipeUpToDate(upToDate bool) Option {
- return func(c *Config) {
- if upToDate {
- logrus.Debugf("runtime config: ensuring recipe is synced with remote")
- }
- c.RecipeLatest = upToDate
- }
-}
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 00000000..73858fbc
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,10 @@
+# Test suite
+
+* Unit testing is done in the packages themselves, run `find . -name
+ "*_test.go"` to find those. Use `make test` to run the entire unit test
+ suite. Some unit tests require mocked files which are located in
+ `./tests/resources.`
+
+* Integration tests are in `./tests/integration`. Please see [these
+ docs](https://docs.coopcloud.tech/abra/hack/#integration-tests) for
+ instructions and tips on how to run them.
diff --git a/tests/integration/app_backup.bats b/tests/integration/app_backup.bats
new file mode 100644
index 00000000..bccb4e7a
--- /dev/null
+++ b/tests/integration/app_backup.bats
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+
+setup_file() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file() {
+ _rm_app
+ _rm_server
+}
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "retrieve recipe if missing" {
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+
+ run $ABRA app backup "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'no containers matching'
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+}
+
+@test "detect backup labels" {
+ run $ABRA app backup "$TEST_APP_DOMAIN" --debug
+ assert_failure
+ assert_output --partial 'no containers matching'
+
+ assert_output --partial 'detected backup paths'
+ assert_output --partial 'detected pre-hook command'
+ assert_output --partial 'detected post-hook command'
+}
+
+@test "error if backups not enabled" {
+ run sed -i '/backupbot.backup=true/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
+ assert_success
+
+ run $ABRA app backup "$TEST_APP_DOMAIN" app
+ assert_failure
+ assert_output --partial 'no backup config for app'
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+@test "error if backup paths not configured" {
+ run sed -i '/backupbot.backup.path=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
+ assert_success
+
+ run $ABRA app backup "$TEST_APP_DOMAIN" app
+ assert_failure
+ assert_output --partial 'backup paths are empty for app?'
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+# bats test_tags=slow
+@test "backup single service" {
+ _deploy_app
+
+ run $ABRA app backup "$TEST_APP_DOMAIN" app
+ assert_success
+ assert_output --partial 'running backup for the app service'
+
+ sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
+ assert_output --partial "_$sanitisedDomainName_app"
+
+ assert_exists "$ABRA_DIR/backups"
+ assert bash -c "ls $ABRA_DIR/backups | grep -q $1_$sanitisedDomainName_app"
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_check.bats b/tests/integration/app_check.bats
new file mode 100644
index 00000000..c315ff14
--- /dev/null
+++ b/tests/integration/app_check.bats
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app check
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app check DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "retrieve recipe if missing" {
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+
+ run $ABRA app check "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'all necessary environment variables defined'
+
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+}
+
+@test "error if missing .env.sample" {
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/.env.sample"
+ assert_success
+
+ run $ABRA app check "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial '.env.sample does not exist?'
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+@test "error if missing env var" {
+ run bash -c 'echo "NEW_VAR=foo" >> "$ABRA_DIR/recipes/$TEST_RECIPE/.env.sample"'
+ assert_success
+
+ run $ABRA app check "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial \
+ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env is missing NEW_VAR"
+
+ _checkout_recipe "$TEST_RECIPE"
+}
diff --git a/tests/integration/app_cmd.bats b/tests/integration/app_cmd.bats
new file mode 100644
index 00000000..6ab74d0f
--- /dev/null
+++ b/tests/integration/app_cmd.bats
@@ -0,0 +1,115 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app cmd
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app cmd DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "retrieve recipe if missing" {
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+
+ run $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd --local
+ assert_success
+ assert_output --partial 'baz'
+
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+}
+
+@test "error if missing arguments without passing --local" {
+ run $ABRA app cmd "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'missing arguments'
+}
+
+@test "error if missing arguments when passing --local" {
+ run $ABRA app cmd "$TEST_APP_DOMAIN" --local
+ assert_failure
+ assert_output --partial 'missing arguments'
+}
+
+@test "cannot use --local and --user at same time" {
+ run $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd --local --user root
+ assert_failure
+ assert_output --partial 'cannot use --local & --user together'
+}
+
+@test "error if missing abra.sh" {
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/abra.sh"
+ assert_success
+
+ run $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd --local
+ assert_failure
+ assert_output --partial "$ABRA_DIR/recipes/$TEST_RECIPE/abra.sh does not exist"
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+@test "error if missing command" {
+ run $ABRA app cmd "$TEST_APP_DOMAIN" doesnt_exist --local
+ assert_failure
+ assert_output --partial "doesn't have a doesnt_exist function"
+}
+
+@test "run --local command" {
+ run $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd --local
+ assert_success
+ assert_output --partial 'baz'
+}
+
+@test "run command with single arg" {
+ run $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd_arg --local -- bing
+ assert_success
+ assert_output --partial 'bing'
+}
+
+@test "run command with several args" {
+ run $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd_args --local -- bong bang
+ assert_success
+ assert_output --partial 'bong bang'
+}
+
+# bats test_tags=slow
+@test "run command on service" {
+ _deploy_app
+
+ run $ABRA app cmd "$TEST_APP_DOMAIN" app test_cmd
+ assert_success
+ assert_output --partial 'baz'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "error if missing service" {
+ _deploy_app
+
+ run $ABRA app cmd "$TEST_APP_DOMAIN" doesnt_exist test_cmd
+ assert_failure
+ assert_output --partial 'no service doesnt_exist'
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_config.bats b/tests/integration/app_config.bats
new file mode 100644
index 00000000..6b400248
--- /dev/null
+++ b/tests/integration/app_config.bats
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app config
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app config DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
diff --git a/tests/integration/app_cp.bats b/tests/integration/app_cp.bats
new file mode 100644
index 00000000..4123b530
--- /dev/null
+++ b/tests/integration/app_cp.bats
@@ -0,0 +1,123 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app cp
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app cp DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if missing src/dest arguments" {
+ run $ABRA app cp "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'missing argument'
+
+ run $ABRA app cp "$TEST_APP_DOMAIN" myfile.txt
+ assert_failure
+ assert_output --partial 'missing argument'
+}
+
+@test "either src/dest has correct syntax" {
+ run $ABRA app cp "$TEST_APP_DOMAIN" myfile.txt app
+ assert_failure
+ assert_output --partial 'arguments must take $SERVICE:$PATH form'
+
+ run $ABRA app cp "$TEST_APP_DOMAIN" app .
+ assert_failure
+ assert_output --partial 'arguments must take $SERVICE:$PATH form'
+}
+
+@test "detect 'coming FROM' syntax" {
+ run $ABRA app cp "$TEST_APP_DOMAIN" app:/myfile.txt . --debug
+ assert_failure
+ assert_output --partial 'coming FROM the container'
+}
+
+@test "detect 'going TO' syntax" {
+ run $ABRA app cp "$TEST_APP_DOMAIN" myfile.txt app:/somewhere --debug
+ assert_failure
+ assert_output --partial 'going TO the container'
+}
+
+@test "error if local file missing" {
+ run $ABRA app cp "$TEST_APP_DOMAIN" myfile.txt app:/somewhere
+ assert_failure
+ assert_output --partial 'myfile.txt does not exist locally?'
+}
+
+# bats test_tags=slow
+@test "error if service doesn't exist" {
+ _deploy_app
+
+ run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt"
+ assert_success
+
+ run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" doesnt_exist:/
+ assert_failure
+ assert_output --partial 'no containers matching'
+
+ run rm -rf "$BATS_TMPDIR/myfile.txt"
+ assert_success
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "copy to container" {
+ _deploy_app
+
+ run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt"
+ assert_success
+
+ run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc
+ assert_success
+
+ run rm -rf "$BATS_TMPDIR/myfile.txt"
+ assert_success
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "copy from container" {
+ _deploy_app
+
+ run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt"
+ assert_success
+
+ run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc
+ assert_success
+
+ run rm -rf "$BATS_TMPDIR/myfile.txt"
+ assert_success
+
+ run $ABRA app cp "$TEST_APP_DOMAIN" app:/etc/myfile.txt "$BATS_TMPDIR"
+ assert_success
+ assert_exists "$BATS_TMPDIR/myfile.txt"
+ assert bash -c "cat $BATS_TMPDIR/myfile.txt | grep -q foo"
+
+ run rm -rf "$BATS_TMPDIR/myfile.txt"
+ assert_success
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_deploy.bats b/tests/integration/app_deploy.bats
new file mode 100644
index 00000000..5c04f49a
--- /dev/null
+++ b/tests/integration/app_deploy.bats
@@ -0,0 +1,276 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app deploy
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app deploy DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "bail if unstaged changes and no --chaos" {
+ run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
+ assert_success
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_success
+ assert_output --partial 'foo'
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
+ assert_failure
+ assert_output --partial 'locally unstaged changes'
+ refute_output --partial 'chaos'
+
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+}
+
+# bats test_tags=slow
+@test "do not bail if unstaged changes and --chaos" {
+ run bash -c 'echo "unstaged changes" >> "$ABRA_DIR/recipes/$TEST_RECIPE/foo"'
+ assert_success
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_success
+ assert_output --partial 'foo'
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --chaos --no-input --no-converge-checks
+ assert_success
+ assert_output --partial 'chaos'
+
+ _undeploy_app
+
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+}
+
+# bats test_tags=slow
+@test "ensure recipe up to date if no --offline" {
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ refute_output --partial 'behind 3'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "ensure recipe not up to date if --offline" {
+ latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+ refute [ -z "$latestCommit" ];
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --offline
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ _undeploy_app
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$latestCommit"
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ refute_output --partial 'behind 3'
+}
+
+# bats test_tags=slow
+@test "deploy latest commit if no published versions and no --chaos" {
+ latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
+ assert_success
+ assert_output --partial "$latestCommit"
+ refute_output --partial 'chaos'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "ensure same commit if --chaos" {
+ latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ threeCommitsBack="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --chaos
+ assert_success
+ refute_output --partial "$latestCommit"
+ assert_output --partial "$threeCommitsBack"
+ assert_output --partial 'chaos'
+
+ _undeploy_app
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$latestCommit"
+ assert_success
+ refute_output --partial 'behind 3'
+}
+
+# bats test_tags=slow
+@test "retrieve recipe if missing" {
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
+ assert_success
+
+ _undeploy_app
+
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+}
+
+@test "no deploy if lint error" {
+ run sed -i '/traefik.enable=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
+ assert_success
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --chaos
+ assert_failure
+ assert_output --partial 'failed lint checks'
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+# bats test_tags=slow
+@test "error if already deployed and no --force/--chaos" {
+ _deploy_app
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks
+ assert_failure
+ assert_output --partial 'already deployed'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "re-deploy deployed app if --force/--chaos" {
+ _deploy_app
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --force
+ assert_success
+ assert_output --partial 'already deployed but continuing'
+ assert_output --partial '--force'
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --chaos
+ assert_success
+ assert_output --partial 'already deployed but continuing'
+ assert_output --partial '--chaos'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "deploy latest version from catalogue if no --chaos" {
+ latestVersion=$(jq -r '.gitea.versions[-1] | keys[0]' < "$ABRA_DIR/catalogue/recipes.json")
+ refute [ -z "$latestVersion" ];
+
+ run $ABRA app new gitea \
+ --no-input \
+ --server "$TEST_SERVER" \
+ --domain "gitea.$TEST_SERVER" \
+ --secrets
+ assert_success
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER/gitea.$TEST_SERVER.env"
+
+ run $ABRA app deploy "gitea.$TEST_SERVER" --no-input --no-converge-checks
+ assert_success
+ assert_output --partial "$latestVersion"
+
+ run $ABRA app undeploy "gitea.$TEST_SERVER" --no-input
+ assert_success
+
+ run $ABRA app secret remove "gitea.$TEST_SERVER" --all --no-input
+ assert_success
+
+ # NOTE(d1): to let the stack come down before nuking volumes
+ sleep 5
+
+ run $ABRA app volume remove "gitea.$TEST_SERVER" --no-input
+ assert_success
+
+ run $ABRA app remove "gitea.$TEST_SERVER" --no-input
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/gitea.$TEST_SERVER.env"
+}
+
+# bats test_tags=slow
+@test "skip domain check if missing DOMAIN=" {
+ run cp "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" "$BATS_TMPDIR/$TEST_APP_DOMAIN.env"
+ assert_success
+ assert_exists "$BATS_TMPDIR/$TEST_APP_DOMAIN.env"
+
+ run grep -q "DOMAIN=" "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+ assert_success
+
+ run sed -i '/DOMAIN=.*/d' "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+ assert_success
+
+ run grep -q "DOMAIN=" "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+ assert_failure
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
+ assert_success
+ assert_output --partial 'no DOMAIN=... configured for app'
+
+ run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
+ assert_success
+
+ run mv "$BATS_TMPDIR/$TEST_APP_DOMAIN.env" "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+ assert_success
+}
+
+# bats test_tags=slow
+@test "skip domain check when requested" {
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --no-domain-checks
+ assert_success
+ assert_output --partial 'skipping domain checks as requested'
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_errors.bats b/tests/integration/app_errors.bats
new file mode 100644
index 00000000..813547fd
--- /dev/null
+++ b/tests/integration/app_errors.bats
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app errors
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app errors DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if not deployed" {
+ run $ABRA app errors "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'is not deployed'
+}
+
+# bats test_tags=slow
+@test "report errors" {
+ _deploy_app
+
+ run $ABRA app errors "$TEST_APP_DOMAIN"
+ assert_success
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_list.bats b/tests/integration/app_list.bats
new file mode 100644
index 00000000..0f62e1ee
--- /dev/null
+++ b/tests/integration/app_list.bats
@@ -0,0 +1,130 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "list without status" {
+ run $ABRA app ls
+ assert_success
+ assert_output --partial "$TEST_SERVER"
+ assert_output --partial "$TEST_APP_DOMAIN"
+}
+
+# bats test_tags=slow
+@test "list with status" {
+ run $ABRA app ls --status
+ assert_success
+ assert_output --partial "$TEST_SERVER"
+ assert_output --partial "$TEST_APP_DOMAIN"
+ assert_output --partial "unknown"
+
+ _deploy_app
+
+ run $ABRA app ls --status
+ assert_success
+ assert_output --partial "$TEST_SERVER"
+ assert_output --partial "$TEST_APP_DOMAIN"
+ assert_output --partial "deployed"
+
+ _undeploy_app
+}
+
+@test "filter by server" {
+ run mkdir -p "$ABRA_DIR/servers/foo.com"
+ assert_success
+ assert_exists "$ABRA_DIR/servers/foo.com"
+
+ run cp \
+ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" \
+ "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+ assert_exists "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+
+ run $ABRA app ls
+ assert_success
+ assert_output --partial "$TEST_SERVER"
+ assert_output --partial "foo.com"
+
+ run $ABRA app ls --server foo.com
+ assert_success
+ refute_output --partial "server: $TEST_SERVER |"
+ assert_output --partial "server: foo.com |"
+
+ run rm -rf "$ABRA_DIR/servers/foo.com"
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/foo.com"
+}
+
+@test "filter by recipe" {
+ run mkdir -p "$ABRA_DIR/servers/foo.com"
+ assert_success
+ assert_exists "$ABRA_DIR/servers/foo.com"
+
+ run cp \
+ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" \
+ "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+ assert_exists "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+
+ run sed -i "s/TYPE=$TEST_RECIPE/TYPE=foo-recipe/g" "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+ assert grep -q "TYPE=foo-recipe" "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+
+ run $ABRA app ls
+ assert_success
+ assert_output --partial "$TEST_RECIPE"
+ assert_output --partial "foo-recipe"
+
+ run $ABRA app ls --recipe foo-recipe
+ assert_success
+ refute_output --partial "$TEST_RECIPE"
+ assert_output --partial "foo-recipe"
+}
+
+@test "server stats are correct" {
+ run $ABRA app ls
+ assert_success
+ assert_output --partial "server: $TEST_SERVER"
+ assert_output --partial "total apps: 1"
+
+ run mkdir -p "$ABRA_DIR/servers/foo.com"
+ assert_success
+ assert_exists "$ABRA_DIR/servers/foo.com"
+
+ run cp \
+ "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" \
+ "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+ assert_exists "$ABRA_DIR/servers/foo.com/app.foo.com.env"
+
+ run $ABRA app ls
+ assert_success
+ assert_output --partial "$TEST_SERVER"
+ assert_output --partial "foo.com"
+ assert_output --partial "total servers: 2"
+ assert_output --partial "total apps: 2"
+
+ run rm -rf "$ABRA_DIR/servers/foo.com"
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/foo.com"
+}
+
+@test "output is machine readable" {
+ run $ABRA app ls --machine
+
+ expectedOutput='{"'
+ expectedOutput+="$TEST_SERVER"
+ expectedOutput+='":{"apps":'
+
+ assert_output --partial "$expectedOutput"
+}
diff --git a/tests/integration/app_logs.bats b/tests/integration/app_logs.bats
new file mode 100644
index 00000000..07c68a2a
--- /dev/null
+++ b/tests/integration/app_logs.bats
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app logs
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app logs DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if not deployed" {
+ run $ABRA app logs "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'is not deployed'
+}
diff --git a/tests/integration/app_new.bats b/tests/integration/app_new.bats
new file mode 100644
index 00000000..282b24cc
--- /dev/null
+++ b/tests/integration/app_new.bats
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "create new app" {
+ run $ABRA app new "$TEST_RECIPE" \
+ --no-input \
+ --server "$TEST_SERVER" \
+ --domain "$TEST_APP_DOMAIN"
+ assert_success
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+
+ _rm_app
+}
+
+@test "does not overwrite existing env files" {
+ _new_app
+
+ run $ABRA app new "$TEST_RECIPE" \
+ --no-input \
+ --server "$TEST_SERVER" \
+ --domain "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'already exists'
+
+ _rm_app
+}
+
+@test "generate secrets" {
+ run $ABRA app new "$TEST_RECIPE" \
+ --no-input \
+ --server "$TEST_SERVER" \
+ --domain "$TEST_APP_DOMAIN" \
+ --secrets
+ assert_success
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'test_password'
+}
diff --git a/tests/integration/app_ps.bats b/tests/integration/app_ps.bats
new file mode 100644
index 00000000..edc60cb5
--- /dev/null
+++ b/tests/integration/app_ps.bats
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app ps
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app ps DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if not deployed" {
+ run $ABRA app ps "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'is not deployed'
+}
+
+# bats test_tags=slow
+@test "show ps report" {
+ _deploy_app
+
+ run $ABRA app ps "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'app'
+ assert_output --partial 'healthy'
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_remove.bats b/tests/integration/app_remove.bats
new file mode 100644
index 00000000..63626fd0
--- /dev/null
+++ b/tests/integration/app_remove.bats
@@ -0,0 +1,148 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app deploy
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app deploy DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "do not show ALERTA warning if --force / --no-input" {
+ run $ABRA app rm "$TEST_APP_DOMAIN" --force
+ refute_output --partial 'ALERTA'
+
+ run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
+ refute_output --partial 'ALERTA'
+
+ _new_app
+}
+
+# bats test_tags=slow
+@test "error if still deployed" {
+ _deploy_app
+
+ run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
+ assert_failure
+ assert_output --partial 'is still deployed'
+
+ _undeploy_app
+}
+
+@test "detect no secrets to remove" {
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'test_password'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all --no-input
+ assert_success
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'test_password'
+ assert_output --partial 'false'
+
+ run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
+ assert_success
+ assert_output --partial 'no secrets to remove'
+
+ _new_app
+}
+
+@test "remove secrets" {
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
+ assert_failure
+ assert_output --partial 'already exists'
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'test_password'
+
+ run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
+ assert_success
+ refute_output --partial 'no secrets to remove'
+
+ sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
+ assert_output --partial "$sanitisedDomainName_test_password_v1 removed"
+
+ _new_app
+}
+
+# bats test_tags=slow
+@test "detect no volumes to remove" {
+ _deploy_app
+
+ run $ABRA app volume ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'test-volume'
+
+ _undeploy_app
+
+ # NOTE(d1): to let the stack come down before nuking volumes
+ sleep 5
+
+ run $ABRA app volume rm "$TEST_APP_DOMAIN" --force
+ assert_success
+
+ run $ABRA app volume ls "$TEST_APP_DOMAIN"
+ assert_success
+ refute_output --partial 'test-volume'
+ assert_output --partial 'no volumes created'
+
+ run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
+ assert_success
+ assert_output --partial 'no volumes to remove'
+
+ _new_app
+}
+
+# bats test_tags=slow
+@test "remove volumes" {
+ _deploy_app
+
+ run $ABRA app volume ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'test-volume'
+
+ _undeploy_app
+
+ # NOTE(d1): to let the stack come down before nuking volumes
+ sleep 5
+
+ run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
+ assert_success
+ assert_output --partial 'test-volume'
+ assert_output --partial 'removed'
+
+ _new_app
+}
+
+@test "remove .env file" {
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+
+ run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
+ assert_success
+
+ assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+
+ _new_app
+}
diff --git a/tests/integration/app_restart.bats b/tests/integration/app_restart.bats
new file mode 100644
index 00000000..18ee570f
--- /dev/null
+++ b/tests/integration/app_restart.bats
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app restart
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app restart DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if service missing" {
+ run $ABRA app restart "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'missing service'
+}
+
+@test "error if not deployed" {
+ run $ABRA app restart "$TEST_APP_DOMAIN" app
+ assert_failure
+ assert_output --partial 'is not deployed'
+}
+
+# bats test_tags=slow
+@test "app is restarted" {
+ _deploy_app
+
+ run $ABRA app restart "$TEST_APP_DOMAIN" app --debug
+ assert_success
+ assert_output --regexp 'attempting to scale .* to 0'
+ assert_output --regexp 'attempting to scale .* to 1'
+ assert_output --partial 'service successfully restarted'
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_restore.bats b/tests/integration/app_restore.bats
new file mode 100644
index 00000000..4aed8db9
--- /dev/null
+++ b/tests/integration/app_restore.bats
@@ -0,0 +1,146 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app restore
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app restore DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if missing service" {
+ run $ABRA app restore "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'missing '
+
+ run $ABRA app restore "$TEST_APP_DOMAIN" app
+ assert_failure
+ assert_output --partial 'missing '
+}
+
+@test "error if file doesn't exist" {
+ run $ABRA app restore "$TEST_APP_DOMAIN" app DOESNTEXIST
+ assert_failure
+ assert_output --partial "doesn't exist"
+}
+
+@test "retrieve recipe if missing" {
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+
+ run $ABRA app restore "$TEST_APP_DOMAIN" app DOESNTEXIST
+ assert_failure
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+}
+
+# bats test_tags=slow
+@test "detect labels if restore enabled" {
+ run touch "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.txt"
+
+ run tar -cvf "$BATS_TMPDIR/foo.tar.gz" "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.tar.gz"
+
+ _deploy_app
+
+ run $ABRA app restore "$TEST_APP_DOMAIN" app "$BATS_TMPDIR/foo.tar.gz" --debug
+ assert_success
+ assert_output --partial 'restore config detected'
+ assert_output --partial 'detected pre-hook command'
+ assert_output --partial 'detected post-hook command'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "no error if restore not enabled" {
+ run sed -i '/backupbot.restore=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
+ assert_success
+
+ run touch "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.txt"
+
+ run tar -cvf "$BATS_TMPDIR/foo.tar.gz" "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.tar.gz"
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --chaos
+ assert_success
+
+ run $ABRA app restore "$TEST_APP_DOMAIN" app "$BATS_TMPDIR/foo.tar.gz" --debug
+ assert_success
+ refute_output --partial 'restore config detected'
+ refute_output --partial 'detected pre-hook command'
+ refute_output --partial 'detected post-hook command'
+
+ _undeploy_app
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+# bats test_tags=slow
+@test "error if service doesn't exist" {
+ run touch "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.txt"
+
+ run tar -cvf "$BATS_TMPDIR/foo.tar.gz" "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.tar.gz"
+
+ _deploy_app
+
+ run $ABRA app restore "$TEST_APP_DOMAIN" DOESNTEXIST "$BATS_TMPDIR/foo.tar.gz" --debug
+ assert_failure
+ assert_output --partial 'no containers matching'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "restore backup" {
+ run touch "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.txt"
+
+ run tar -cvf "$BATS_TMPDIR/foo.tar.gz" "$BATS_TMPDIR/foo.txt"
+ assert_success
+ assert_exists "$BATS_TMPDIR/foo.tar.gz"
+
+ _deploy_app
+
+ run $ABRA app restore "$TEST_APP_DOMAIN" app "$BATS_TMPDIR/foo.tar.gz" --debug
+ assert_success
+ assert_output --partial 'restore config detected'
+ assert_output --partial 'detected pre-hook command'
+ assert_output --partial 'detected post-hook command'
+
+ run $ABRA app run "$TEST_APP_DOMAIN" app ls "$BATS_TMPDIR"
+ assert_success
+ assert_output --partial 'foo.txt'
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_rollback.bats b/tests/integration/app_rollback.bats
new file mode 100644
index 00000000..2f2c246e
--- /dev/null
+++ b/tests/integration/app_rollback.bats
@@ -0,0 +1,232 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+# TODO(d1): test "no available downgrades" when this is implemented
+# https://git.coopcloud.tech/coop-cloud/organising/issues/204
+
+@test "validate app argument" {
+ run $ABRA app rollback
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app rollback DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "retrieve recipe if missing" {
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --no-converge-checks
+ assert_failure
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
+}
+
+@test "ensure recipe up to date if no --offline" {
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --no-converge-checks
+ assert_failure
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ refute_output --partial 'behind 3'
+}
+
+@test "ensure recipe not up to date if --offline" {
+ latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+ refute [ -z "$latestCommit" ];
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --offline
+ assert_failure
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$latestCommit"
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ refute_output --partial 'behind 3'
+}
+
+@test "bail if unstaged changes and no --chaos" {
+ run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
+ assert_success
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_success
+ assert_output --partial 'foo'
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --no-converge-checks
+ assert_failure
+ assert_output --partial 'locally unstaged changes'
+ refute_output --partial 'chaos'
+
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+}
+
+# bats test_tags=slow
+@test "do not bail if unstaged changes and --chaos" {
+ run bash -c 'echo "unstaged changes" >> "$ABRA_DIR/recipes/$TEST_RECIPE/foo"'
+ assert_success
+ assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_success
+ assert_output --partial 'foo'
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --chaos --no-input --no-converge-checks
+ assert_success
+ assert_output --partial 'chaos'
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" \
+ --chaos --no-input --no-converge-checks
+ assert_success
+ assert_output --partial 'chaos'
+
+ _undeploy_app
+
+ run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+ assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
+}
+
+# bats test_tags=slow
+@test "ensure same commit if --chaos" {
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout main
+ assert_success
+
+ latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ threeCommitsBack="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --chaos
+ assert_success
+ refute_output --partial "$latestCommit"
+ assert_output --partial "$threeCommitsBack"
+ assert_output --partial 'chaos'
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" \
+ --chaos --no-input --no-converge-checks
+ assert_success
+ refute_output --partial "$latestCommit"
+ assert_output --partial "$threeCommitsBack"
+ assert_output --partial 'chaos'
+
+ _undeploy_app
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$latestCommit"
+ assert_success
+ refute_output --partial 'behind 3'
+}
+
+# bats test_tags=slow
+@test "no rollback if lint error" {
+ _deploy_app
+
+ run sed -i '/traefik.enable=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
+ assert_success
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --chaos
+ assert_failure
+ assert_output --partial 'failed lint checks'
+
+ _undeploy_app
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+@test "error if not already deployed" {
+ run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --chaos
+ assert_failure
+ assert_output --partial 'not deployed'
+}
+
+# bats test_tags=slow
+@test "error if no published release and no --chaos" {
+ _deploy_app
+
+ run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input
+ assert_failure
+ assert_output --partial 'no published releases'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "rollback to previous version" {
+ latestVersion=$(jq -r '.gitea.versions[-1] | keys[0]' < "$ABRA_DIR/catalogue/recipes.json")
+ refute [ -z "$latestVersion" ];
+
+ rollbackVersion=$(jq -r '.gitea.versions[-2] | keys[0]' < "$ABRA_DIR/catalogue/recipes.json")
+ refute [ -z "$rollbackVersion" ];
+
+ run $ABRA app new gitea \
+ --no-input \
+ --server "$TEST_SERVER" \
+ --domain "gitea.$TEST_SERVER" \
+ --secrets
+ assert_success
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER/gitea.$TEST_SERVER.env"
+
+ run $ABRA app deploy "gitea.$TEST_SERVER" --no-input --no-converge-checks
+ assert_success
+ assert_output --partial "$latestVersion"
+
+ run $ABRA app rollback "gitea.$TEST_SERVER" --no-input --no-converge-checks
+ assert_success
+ assert_output --partial "$rollbackVersion"
+
+ run $ABRA app undeploy "gitea.$TEST_SERVER" --no-input
+ assert_success
+
+ run $ABRA app secret remove "gitea.$TEST_SERVER" --all --no-input
+ assert_success
+
+ # NOTE(d1): to let the stack come down before nuking volumes
+ sleep 5
+
+ run $ABRA app volume remove "gitea.$TEST_SERVER" --no-input
+ assert_success
+
+ run $ABRA app remove "gitea.$TEST_SERVER" --no-input
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/gitea.$TEST_SERVER.env"
+}
diff --git a/tests/integration/app_run.bats b/tests/integration/app_run.bats
new file mode 100644
index 00000000..8980abf4
--- /dev/null
+++ b/tests/integration/app_run.bats
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app run
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app run DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if missing service" {
+ run $ABRA app run "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'no provided'
+
+ run $ABRA app run "$TEST_APP_DOMAIN" app
+ assert_failure
+ assert_output --partial 'no provided'
+}
+
+# bats test_tags=slow
+@test "error if service doesn't exist" {
+ _deploy_app
+
+ run $ABRA app run "$TEST_APP_DOMAIN" DOESNTEXIST ls
+ assert_failure
+ assert_output --partial 'no containers matching'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "run command" {
+ _deploy_app
+
+ run $ABRA app run "$TEST_APP_DOMAIN" app ls /
+ assert_success
+ assert_output --partial 'root'
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_secret.bats b/tests/integration/app_secret.bats
new file mode 100644
index 00000000..171b7f44
--- /dev/null
+++ b/tests/integration/app_secret.bats
@@ -0,0 +1,216 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_success
+}
+
+teardown_file(){
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_success
+
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "generate: validate arguments" {
+ run $ABRA app secret generate
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app secret generate DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+
+ run $ABRA app secret generate "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'missing arguments'
+
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" testSecret testVersion --all
+ assert_failure
+ assert_output --partial 'cannot use'
+ assert_output --partial "'--all' together"
+}
+
+@test "generate: single secret no match" {
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" DOESNTEXIST v1
+ assert_failure
+ assert_output --partial "doesn't exist in the env config"
+}
+
+@test "generate: recipe up to date if no --offline" {
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ refute_output --partial 'behind 3'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_success
+}
+
+@test "generate: recipe not up to date if --offline" {
+ latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+ refute [ -z "$latestCommit" ];
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" --all --offline
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$latestCommit"
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ refute_output --partial 'behind 3'
+}
+
+@test "generate: create secrets" {
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'false'
+
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
+ assert_success
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'true'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_success
+}
+
+@test "insert: validate arguments" {
+ run $ABRA app secret insert
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app secret insert "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'missing arguments'
+
+ run $ABRA app secret insert "$TEST_APP_DOMAIN" bar
+ assert_failure
+ assert_output --partial 'missing arguments'
+
+ run $ABRA app secret insert "$TEST_APP_DOMAIN" bar baz
+ assert_failure
+ assert_output --partial 'missing arguments'
+}
+
+@test "insert: create secret" {
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'false'
+
+ run $ABRA app secret insert "$TEST_APP_DOMAIN" test_password v1 foo
+ assert_success
+ assert_output --partial 'successfully stored on server'
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'true'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" test_password
+ assert_success
+}
+
+@test "rm: validate arguments" {
+ run $ABRA app secret rm
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app secret rm DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'no secret(s) specified'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" test_password --all
+ assert_failure
+ assert_output --partial 'cannot use'
+ assert_output --partial "'--all' together"
+}
+
+@test "rm: single secret no match" {
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" foo_password
+ assert_failure
+ assert_output --partial "doesn't exist on server"
+}
+
+@test "rm: no secret match" {
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_failure
+ assert_output --partial 'no secrets to remove'
+}
+
+@test "rm: remove secret" {
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
+ assert_success
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'true'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_success
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'false'
+}
+
+@test "ls: validate arguments" {
+ run $ABRA app secret ls
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app secret ls DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "ls: show secrets" {
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'false'
+
+ run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
+ assert_success
+
+ run $ABRA app secret ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'true'
+
+ run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
+ assert_success
+}
diff --git a/tests/integration/app_services.bats b/tests/integration/app_services.bats
new file mode 100644
index 00000000..9fee6b1d
--- /dev/null
+++ b/tests/integration/app_services.bats
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app services
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app services DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if not deployed" {
+ run $ABRA app services "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'is not deployed'
+}
+
+# bats test_tags=slow
+@test "list services" {
+ _deploy_app
+
+ run $ABRA app services "$TEST_APP_DOMAIN"
+ assert_success
+
+ sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
+ assert_output --partial "$sanitisedDomainName_app"
+ assert_output --partial "nginx"
+
+ _undeploy_app
+}
diff --git a/tests/integration/app_undeploy.bats b/tests/integration/app_undeploy.bats
new file mode 100644
index 00000000..051eb458
--- /dev/null
+++ b/tests/integration/app_undeploy.bats
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app undeploy
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app undeploy DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if not deployed" {
+ run $ABRA app undeploy "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'is not deployed'
+}
+
+# bats test_tags=slow
+@test "undeploy app" {
+ _deploy_app
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "undeploy and prune" {
+ _deploy_app
+
+ run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input --prune
+ assert_success
+}
diff --git a/tests/integration/app_upgrade.bats b/tests/integration/app_upgrade.bats
new file mode 100644
index 00000000..8d0b5133
--- /dev/null
+++ b/tests/integration/app_upgrade.bats
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app upgrade
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app upgrade DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
diff --git a/tests/integration/app_version.bats b/tests/integration/app_version.bats
new file mode 100644
index 00000000..b4cd3910
--- /dev/null
+++ b/tests/integration/app_version.bats
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate app argument" {
+ run $ABRA app version
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app version DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "error if not deployed" {
+ run $ABRA app version "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'is not deployed'
+}
+
+# bats test_tags=slow
+@test "error if version unknown" {
+ run sed -i '/coop-cloud.${STACK_NAME}.version=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
+ assert_success
+
+ run $ABRA app deploy "$TEST_APP_DOMAIN" \
+ --no-input --no-converge-checks --chaos
+ assert_success
+
+ run $ABRA app version "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'failed to determine'
+
+ _checkout_recipe "$TEST_RECIPE"
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "error if no version in catalogue" {
+ _deploy_app
+
+ run $ABRA app version "$TEST_APP_DOMAIN"
+ assert_failure
+ assert_output --partial 'could not retrieve deployed version'
+
+ _undeploy_app
+}
+
+@test "list version" {
+ latestVersion=$(jq -r '.gitea.versions[-1] | keys[0]' < "$ABRA_DIR/catalogue/recipes.json")
+ refute [ -z "$latestVersion" ];
+
+ run $ABRA app new gitea \
+ --no-input \
+ --server "$TEST_SERVER" \
+ --domain "gitea.$TEST_SERVER" \
+ --secrets
+ assert_success
+
+ run $ABRA app deploy "gitea.$TEST_SERVER" \
+ --no-input --no-converge-checks
+ assert_success
+
+ run $ABRA app version "gitea.$TEST_SERVER"
+ assert_success
+ assert_output --partial "$latestVersion"
+
+ run $ABRA app undeploy "gitea.$TEST_SERVER" --no-input
+ assert_success
+
+ run $ABRA app secret remove "gitea.$TEST_SERVER" --all --no-input
+ assert_success
+
+ # NOTE(d1): to let the stack come down before nuking volumes
+ sleep 5
+
+ run $ABRA app volume remove "gitea.$TEST_SERVER" --no-input
+ assert_success
+
+ run $ABRA app remove "gitea.$TEST_SERVER" --no-input
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/gitea.$TEST_SERVER.env"
+}
diff --git a/tests/integration/app_volume.bats b/tests/integration/app_volume.bats
new file mode 100644
index 00000000..9c0c9d5b
--- /dev/null
+++ b/tests/integration/app_volume.bats
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+ _new_app
+}
+
+teardown_file(){
+ _rm_app
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "ls validate app argument" {
+ run $ABRA app volume ls
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app volume ls DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+@test "list no volumes" {
+ run $ABRA app volume ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'no volumes created'
+}
+
+# bats test_tags=slow
+@test "list volumes" {
+ _deploy_app
+
+ run $ABRA app volume ls "$TEST_APP_DOMAIN"
+ assert_success
+ assert_output --partial 'test-volume'
+
+ _undeploy_app
+}
+
+@test "rm validate app argument" {
+ run $ABRA app volume rm
+ assert_failure
+ assert_output --partial 'no app provided'
+
+ run $ABRA app volume rm DOESNTEXIST
+ assert_failure
+ assert_output --partial 'cannot find app'
+}
+
+# bats test_tags=slow
+@test "rm error if deployed" {
+ _deploy_app
+
+ run $ABRA app volume rm "$TEST_APP_DOMAIN" --force
+ assert_failure
+ assert_output --partial 'is still deployed'
+
+ _undeploy_app
+}
+
+# bats test_tags=slow
+@test "remove volumes" {
+ _deploy_app
+
+ _undeploy_app
+
+ # NOTE(d1): to let the stack come down before nuking volumes
+ sleep 5
+
+ run $ABRA app volume rm "$TEST_APP_DOMAIN" --force
+ assert_success
+ assert_output --partial 'volumes removed successfully'
+}
+
+# bats test_tags=slow
+@test "remove no volumes" {
+ _deploy_app
+
+ _undeploy_app
+
+ # NOTE(d1): to let the stack come down before nuking volumes
+ sleep 5
+
+ run $ABRA app volume rm "$TEST_APP_DOMAIN" --force
+ assert_success
+ assert_output --partial 'volumes removed successfully'
+
+ run $ABRA app volume rm "$TEST_APP_DOMAIN" --force
+ assert_success
+ assert_output --partial 'no volumes removed'
+}
diff --git a/tests/integration/autocomplete.bats b/tests/integration/autocomplete.bats
index 6d33bf4f..3f3e7d7a 100644
--- a/tests/integration/autocomplete.bats
+++ b/tests/integration/autocomplete.bats
@@ -1,35 +1,30 @@
#!/usr/bin/env bash
-setup() {
- DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
- source "$DIR/helpers.sh"
- _setup_env
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
}
@test "bash autocompletion" {
run $ABRA autocomplete bash
assert_success
- assert [ -f "$ABRA_DIR/autocompletion/bash" ]
+ assert_exists "$ABRA_DIR/autocompletion/bash"
}
@test "zsh autocompletion" {
run $ABRA autocomplete zsh
assert_success
- assert [ -f "$ABRA_DIR/autocompletion/zsh" ]
+ assert_exists "$ABRA_DIR/autocompletion/zsh"
}
@test "fish autocompletion" {
run $ABRA autocomplete fish
assert_success
- assert [ -f "$ABRA_DIR/autocompletion/fish" ]
+ assert_exists "$ABRA_DIR/autocompletion/fish"
}
@test "fizsh autocompletion" {
run $ABRA autocomplete fizsh
assert_success
- assert [ -f "$ABRA_DIR/autocompletion/zsh" ]
-}
-
-teardown(){
- _default_teardown
+ assert_exists "$ABRA_DIR/autocompletion/zsh"
}
diff --git a/tests/integration/catalogue.bats b/tests/integration/catalogue.bats
index c98ab365..be5e8d57 100644
--- a/tests/integration/catalogue.bats
+++ b/tests/integration/catalogue.bats
@@ -1,21 +1,18 @@
#!/usr/bin/env bash
-setup() {
- DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
- source "$DIR/helpers.sh"
- _setup_env
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
}
-@test "catalogue generate" {
+# bats test_tags=slow
+@test "generate entire catalogue" {
run $ABRA catalogue generate
assert_success
}
-@test "catalogue generate specific recipe" {
+# bats test_tags=slow
+@test "generate only specific recipe" {
run $ABRA catalogue generate gitea
assert_success
}
-
-teardown(){
- _default_teardown
-}
diff --git a/tests/integration/dirs.bats b/tests/integration/dirs.bats
index 0ba86678..dfe0a71d 100644
--- a/tests/integration/dirs.bats
+++ b/tests/integration/dirs.bats
@@ -1,12 +1,15 @@
#!/usr/bin/env bash
-setup() {
- DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
- source "$DIR/helpers.sh"
- _setup_env
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+
+ if [[ -d "$ABRA_DIR" ]]; then
+ rm -rf "$ABRA_DIR"
+ fi
}
-@test "ABRA_DIR can be overriden" {
+@test "ABRA_DIR is overriden" {
ABRA_DIR="$HOME/.abra_foo"
run $ABRA app ls
@@ -15,22 +18,25 @@ setup() {
# checks if it should create these base directories and that is what we want
assert_failure
- assert [ -d "$HOME/.abra_foo" ]
+ assert_exists "$HOME/.abra_foo"
+
+ run rm -rf "$ABRA_DIR"
+ assert_success
}
-@test "abra directories created" {
+@test "abra directory is created" {
run $ABRA app ls
# no servers yet, so will fail. however, it will run the required code which
# checks if it should create these base directories and that is what we want
assert_failure
- assert [ -d "$ABRA_DIR" ]
- assert [ -d "$ABRA_DIR/servers" ]
- assert [ -d "$ABRA_DIR/recipes" ]
- assert [ -d "$ABRA_DIR/backups" ]
- assert [ -d "$ABRA_DIR/vendor" ]
- assert [ -d "$ABRA_DIR/catalogue" ]
+ assert_exists "$ABRA_DIR"
+ assert_exists "$ABRA_DIR/servers"
+ assert_exists "$ABRA_DIR/recipes"
+ assert_exists "$ABRA_DIR/backups"
+ assert_exists "$ABRA_DIR/vendor"
+ assert_exists "$ABRA_DIR/catalogue"
}
@test "catalogue recipe is a git repository" {
@@ -42,10 +48,6 @@ setup() {
assert_output --partial 'local recipe catalogue is missing'
- assert [ -d "$ABRA_DIR/catalogue" ]
- assert [ -d "$ABRA_DIR/catalogue/.git" ]
-}
-
-teardown(){
- _default_teardown
+ assert_exists "$ABRA_DIR/catalogue"
+ assert_exists "$ABRA_DIR/catalogue/.git"
}
diff --git a/tests/integration/helpers.sh b/tests/integration/helpers.sh
deleted file mode 100644
index f3aa6ead..00000000
--- a/tests/integration/helpers.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env bash
-
-_build_abra() {
- if [[ ! -e "$ROOT/abra" ]]; then
- cd "$ROOT" && make build-abra
- return 0
- fi
- return 0
-}
-
-_build_kadabra() {
- if [[ ! -e "$ROOT/kadabra" ]]; then
- cd "$ROOT" && make build-kadabra
- return 0
- fi
- return 0
-}
-
-_setup_env(){
- load '/usr/lib/bats/bats-support/load'
- load '/usr/lib/bats/bats-assert/load'
-
- ROOT="$DIR/../.."
- ABRA="$ROOT/abra"
- KADABRA="$ROOT/kadabra"
-
- ABRA_DIR="$HOME/.abra_test"
-
- _build_abra
- _build_kadabra
-}
-
-_default_teardown(){
- rm -rf "$ABRA_DIR"
-}
diff --git a/tests/integration/helpers/app.bash b/tests/integration/helpers/app.bash
new file mode 100644
index 00000000..17dbbc2f
--- /dev/null
+++ b/tests/integration/helpers/app.bash
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+_new_app() {
+ run $ABRA app new "$TEST_RECIPE" \
+ --no-input \
+ --server "$TEST_SERVER" \
+ --domain "$TEST_APP_DOMAIN" \
+ --secrets
+ assert_success
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
+}
+
+_deploy_app() {
+ run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
+ assert_success
+
+ run $ABRA app ls --server "$TEST_SERVER" --status
+ assert_success
+ assert_output --partial "$TEST_APP_DOMAIN"
+ assert_output --partial 'deployed'
+}
+
+_undeploy_app() {
+ run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
+ assert_success
+
+ run $ABRA app ls --server "$TEST_SERVER" --status
+ assert_success
+ assert_output --partial "$TEST_APP_DOMAIN"
+ assert_output --partial 'unknown'
+}
+
+_rm_app() {
+ # NOTE(d1): not asserting outcomes on teardown here since some might fail
+ # depending on what the test created. all commands run through anyway
+ if [[ -f "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" ]]; then
+ run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
+ run $ABRA app secret remove "$TEST_APP_DOMAIN" --all --no-input
+ run $ABRA app volume remove "$TEST_APP_DOMAIN" --no-input
+ run $ABRA app remove "$TEST_APP_DOMAIN" --no-input
+ fi
+}
diff --git a/tests/integration/helpers/common.bash b/tests/integration/helpers/common.bash
new file mode 100644
index 00000000..379569ea
--- /dev/null
+++ b/tests/integration/helpers/common.bash
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+_common_setup() {
+ load '/usr/lib/bats/bats-support/load'
+ load '/usr/lib/bats/bats-assert/load'
+ load '/usr/lib/bats/bats-file/load'
+
+ load "$PWD/tests/integration/helpers/app"
+ load "$PWD/tests/integration/helpers/git"
+ load "$PWD/tests/integration/helpers/recipe"
+ load "$PWD/tests/integration/helpers/server"
+
+ export ABRA="$PWD/abra"
+ export KADABRA="$PWD/kadabra"
+
+ export TEST_APP_NAME="$(basename "${BATS_TEST_FILENAME//./_}")"
+ export TEST_APP_DOMAIN="$TEST_APP_NAME.$TEST_SERVER"
+ export TEST_RECIPE="abra-integration-test-recipe"
+}
diff --git a/tests/integration/helpers/git.bash b/tests/integration/helpers/git.bash
new file mode 100644
index 00000000..a8f41f4d
--- /dev/null
+++ b/tests/integration/helpers/git.bash
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+_checkout_recipe() {
+ if [[ -z "$1" ]]; then
+ echo 'forgot to pass argument to function?'
+ exit 1
+ fi
+
+ run git -C "$ABRA_DIR/recipes/$1" checkout .
+ assert_success
+}
diff --git a/tests/integration/helpers/recipe.bash b/tests/integration/helpers/recipe.bash
new file mode 100644
index 00000000..4341f65c
--- /dev/null
+++ b/tests/integration/helpers/recipe.bash
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+_fetch_recipe() {
+ if [[ -z "$1" ]]; then
+ echo 'forgot to pass argument to function?'
+ exit 1
+ fi
+
+ if [[ ! -d "$ABRA_DIR/recipes/$1" ]]; then
+ run mkdir -p "$ABRA_DIR/recipes"
+ assert_success
+
+ run git clone "https://git.coopcloud.tech/coop-cloud/$1" "$ABRA_DIR/recipes/$1"
+ assert_success
+ fi
+}
diff --git a/tests/integration/helpers/server.bash b/tests/integration/helpers/server.bash
new file mode 100644
index 00000000..edeb9115
--- /dev/null
+++ b/tests/integration/helpers/server.bash
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+_add_server() {
+ run $ABRA server add "$TEST_SERVER"
+ assert_success
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER"
+}
+
+_rm_server() {
+ run $ABRA server remove --no-input "$TEST_SERVER"
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER"
+}
+
+_rm_default_server(){
+ run rm -rf "$ABRA_DIR/servers/default"
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/default"
+}
diff --git a/tests/integration/install.bats b/tests/integration/install.bats
index 6385e11f..a92ce234 100644
--- a/tests/integration/install.bats
+++ b/tests/integration/install.bats
@@ -1,38 +1,37 @@
#!/usr/bin/env bash
setup() {
- DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
- source "$DIR/helpers.sh"
- _setup_env
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
if [[ -f "$HOME/.local/bin/abra" ]]; then
mv "$HOME/.local/bin/abra" "$HOME/.local/bin/abra_before_test"
fi
}
-@test "install from script" {
- run bash -c 'curl https://install.abra.coopcloud.tech | bash'
- assert_success
-
- assert [ -f "$HOME/.local/bin/abra" ]
- run $HOME/.local/bin/abra -v
- assert_output --partial 'beta'
-}
-
-@test "install release candidate from script" {
- run bash -c 'curl https://install.abra.coopcloud.tech | bash -s -- --rc'
- assert_success
-
- assert [ -f "$HOME/.local/bin/abra" ]
- run $HOME/.local/bin/abra -v
- assert_output --partial '-rc'
-}
-
teardown(){
- _default_teardown
-
if [[ -f "$HOME/.local/bin/abra_before_test" ]]; then
rm -rf "$HOME/.local/bin/abra"
mv "$HOME/.local/bin/abra_before_test" "$HOME/.local/bin/abra"
fi
}
+
+# bats test_tags=slow
+@test "install from script" {
+ run bash -c 'curl https://install.abra.coopcloud.tech | bash'
+ assert_success
+
+ assert_exists "$HOME/.local/bin/abra"
+ run "$HOME/.local/bin/abra" -v
+ assert_output --partial 'beta'
+}
+
+# bats test_tags=slow
+@test "install release candidate from script" {
+ run bash -c 'curl https://install.abra.coopcloud.tech | bash -s -- --rc'
+ assert_success
+
+ assert_exists "$HOME/.local/bin/abra"
+ run "$HOME/.local/bin/abra" -v
+ assert_output --partial '-rc'
+}
diff --git a/tests/integration/recipe.bats b/tests/integration/recipe.bats
deleted file mode 100644
index 833ace07..00000000
--- a/tests/integration/recipe.bats
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/env bash
-
-setup() {
- DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
- source "$DIR/helpers.sh"
- _setup_env
-
- mkdir -p "$HOME/.abra_test/servers/example.com"
- if ! grep -R -q "example.com" "$HOME/.docker"; then
- docker context create --docker host=ssh://foo@example.com:222 example.com
- fi
-}
-
-@test "create new recipe" {
- run $ABRA recipe new foobar
- assert_success
- assert_output --partial 'Your new foobar recipe has been created'
-
- run $ABRA app new foobar \
- --no-input \
- --server example.com \
- --domain foobar.example.com
- assert_success
- assert_output --partial 'A new foobar app has been created!'
-}
-
-@test "recipe fetch" {
- run $ABRA recipe fetch matrix-synapse
- assert_success
- assert [ -d "$ABRA_DIR/recipes/matrix-synapse" ]
-}
-
-@test "recipe sync allows unstaged changes" {
- run $ABRA recipe fetch matrix-synapse
- assert_success
-
- run echo "unstaged changes" >> "$ABRA_DIR/recipes/matrix-synapse/foo"
- assert_success
-
- run $ABRA recipe sync matrix-synapse --patch
- assert_success
-}
-
-@test "recipe sync detects unstaged label changes" {
- run $ABRA recipe fetch matrix-synapse
- assert_success
-
- run $ABRA recipe sync matrix-synapse --patch
- assert_success
-
- run $ABRA recipe sync matrix-synapse --patch
- assert_success
- assert_output --partial 'is already set, nothing to do?'
-}
-
-@test "recipe list" {
- run $ABRA recipe list
- assert_success
- NUM_RECIPES=$(jq length "$ABRA_DIR/catalogue/recipes.json")
- assert_output --partial "total recipes: $NUM_RECIPES"
-}
-
-@test "recipe list with pattern" {
- run $ABRA recipe list --pattern cloud
- assert_success
- assert_output --partial 'nextcloud'
- refute_output --partial 'matrix-synapse'
-}
-
-@test "recipe lint" {
- run $ABRA recipe lint gitea
- assert_success
-}
-
-@test "recipe versions" {
- run $ABRA recipe versions gitea
- assert_success
- assert_output --partial '2.3.2+1.20.3-rootless'
-}
-
-teardown() {
- _default_teardown
-
- if grep -R -q "example.com" "$HOME/.docker"; then
- docker context rm example.com
- fi
-}
diff --git a/tests/integration/recipe_fetch.bats b/tests/integration/recipe_fetch.bats
new file mode 100644
index 00000000..a64306ab
--- /dev/null
+++ b/tests/integration/recipe_fetch.bats
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "recipe fetch" {
+ run rm -rf "$ABRA_DIR/recipes/matrix-synapse"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/matrix-synapse"
+
+ run $ABRA recipe fetch matrix-synapse
+ assert_success
+ assert_exists "$ABRA_DIR/recipes/matrix-synapse"
+}
diff --git a/tests/integration/recipe_lint.bats b/tests/integration/recipe_lint.bats
new file mode 100644
index 00000000..63a3e824
--- /dev/null
+++ b/tests/integration/recipe_lint.bats
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "recipe lint" {
+ run $ABRA recipe lint gitea
+ assert_success
+ assert_output --partial 'compose config has expected version'
+}
+
+@test "recipe lint warns on error" {
+ run $ABRA recipe lint "$TEST_RECIPE"
+ assert_success
+ refute_output --partial 'watch out, some critical errors are present'
+
+ run sed -i '/traefik.enable=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
+ assert_success
+
+ run $ABRA recipe lint "$TEST_RECIPE"
+ assert_success --partial 'watch out, some critical errors are present'
+
+ _checkout_recipe "$TEST_RECIPE"
+}
+
+@test "recipe lint uses latest commit" {
+ _fetch_recipe "$TEST_RECIPE"
+
+ latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)"
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run $ABRA recipe lint "$TEST_RECIPE"
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ assert_output --partial 'behind 3'
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$latestCommit"
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
+ refute_output --partial 'behind 3'
+}
diff --git a/tests/integration/recipe_list.bats b/tests/integration/recipe_list.bats
new file mode 100644
index 00000000..ed2b7eb1
--- /dev/null
+++ b/tests/integration/recipe_list.bats
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "recipe list" {
+ run $ABRA recipe list
+ assert_success
+ NUM_RECIPES=$(jq length "$ABRA_DIR/catalogue/recipes.json")
+ assert_output --partial "total recipes: $NUM_RECIPES"
+}
+
+@test "recipe list with pattern" {
+ run $ABRA recipe list --pattern cloud
+ assert_success
+ assert_output --partial 'nextcloud'
+ refute_output --partial 'matrix-synapse'
+}
diff --git a/tests/integration/recipe_new.bats b/tests/integration/recipe_new.bats
new file mode 100644
index 00000000..6149f053
--- /dev/null
+++ b/tests/integration/recipe_new.bats
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+teardown(){
+ if [[ -d "$ABRA_DIR/recipes/foobar" ]]; then
+ run rm -rf "$ABRA_DIR/recipes/foobar"
+ assert_success
+ fi
+}
+
+@test "create new recipe" {
+ run $ABRA recipe new foobar
+ assert_success
+ assert_output --partial 'Your new foobar recipe has been created'
+ assert_exists "$ABRA_DIR/recipes/foobar"
+}
+
+@test "create new app from new recipe" {
+ run $ABRA recipe new foobar
+ assert_success
+
+ 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!'
+}
diff --git a/tests/integration/recipe_release.bats b/tests/integration/recipe_release.bats
new file mode 100644
index 00000000..0b70ef53
--- /dev/null
+++ b/tests/integration/recipe_release.bats
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+# bats test_tags=slow
+@test "pull in latest changes" {
+ run $ABRA recipe fetch matrix-synapse
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/matrix-synapse" reset --hard HEAD~3
+ assert_success
+
+ run $ABRA recipe release matrix-synapse --no-input
+ assert_failure
+ assert_output --regexp 'latest git tag .* are the same'
+
+ run git -C "$ABRA_DIR/recipes/matrix-synapse" status
+ refute_output --partial 'behind 3'
+}
diff --git a/tests/integration/recipe_sync.bats b/tests/integration/recipe_sync.bats
new file mode 100644
index 00000000..241a2fbe
--- /dev/null
+++ b/tests/integration/recipe_sync.bats
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "allow unstaged changes" {
+ run $ABRA recipe fetch matrix-synapse
+ assert_success
+
+ run echo "unstaged changes" >> "$ABRA_DIR/recipes/matrix-synapse/foo"
+ assert_success
+
+ run git -C "$ABRA_DIR/recipes/matrix-synapse" status
+ assert_success
+ assert_output --partial 'foo'
+
+ run $ABRA recipe sync matrix-synapse --patch
+ assert_success
+
+ run rm -rf "$ABRA_DIR/recipes/matrix-synapse/foo"
+ assert_success
+ assert_not_exists "$ABRA_DIR/recipes/matrix-synapse/foo"
+}
+
+@test "detect unstaged label changes" {
+ run $ABRA recipe fetch matrix-synapse
+ assert_success
+
+ run $ABRA recipe sync matrix-synapse --patch
+ assert_success
+
+ run $ABRA recipe sync matrix-synapse --patch
+ assert_success
+ assert_output --partial 'is already set, nothing to do?'
+}
+
+# TODO(d1): implement
diff --git a/tests/integration/recipe_upgrade.bats b/tests/integration/recipe_upgrade.bats
new file mode 100644
index 00000000..627b24e1
--- /dev/null
+++ b/tests/integration/recipe_upgrade.bats
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+# TODO(d1): implement
diff --git a/tests/integration/recipe_version.bats b/tests/integration/recipe_version.bats
new file mode 100644
index 00000000..5d5bfca7
--- /dev/null
+++ b/tests/integration/recipe_version.bats
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+setup() {
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "recipe versions" {
+ run $ABRA recipe versions gitea
+ assert_success
+ assert_output --partial '2.3.2+1.20.3-rootless'
+}
diff --git a/tests/integration/server_add.bats b/tests/integration/server_add.bats
new file mode 100644
index 00000000..72d9a1cc
--- /dev/null
+++ b/tests/integration/server_add.bats
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "add new server" {
+ run $ABRA server add "$TEST_SERVER"
+ assert_success
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER"
+
+ run $ABRA server ls
+ assert_output --partial "$TEST_SERVER"
+
+ assert bash -c "docker context ls | grep -q $TEST_SERVER"
+}
+
+@test "error if using domain and --local together" {
+ run $ABRA server add "$TEST_SERVER" --local
+ assert_failure
+ assert_output --partial 'cannot use and --local together'
+}
+
+@test "create local server" {
+ run docker swarm init
+ assert_success
+
+ run $ABRA server add --local
+ assert_success
+ assert_exists "$ABRA_DIR/servers/default"
+ assert bash -c "docker context ls | grep -q default"
+ assert_output --partial 'local server added'
+
+ docker swarm leave --force
+ assert_success
+
+ _rm_default_server
+}
+
+@test "create local server fails when no docker swarm" {
+ run $ABRA server add --local
+ assert_failure
+ assert_not_exists "$ABRA_DIR/servers/default"
+ assert_output --partial 'swarm mode not enabled on local server'
+}
+
+@test "cleanup when cannot add server" {
+ run $ABRA server add example.com
+ assert_failure
+ assert_not_exists "$ABRA_DIR/servers/example.com"
+ refute bash -c "docker context ls | grep -q example.com"
+}
diff --git a/tests/integration/server_list.bats b/tests/integration/server_list.bats
new file mode 100644
index 00000000..8396c215
--- /dev/null
+++ b/tests/integration/server_list.bats
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "list server" {
+ run "$ABRA" server ls
+ assert_success
+ assert_output --partial "$TEST_SERVER"
+}
+
+@test "show 'local' when --local server created" {
+ run docker swarm init
+ assert_success
+
+ run $ABRA server add --local
+ assert_success
+ assert_exists "$ABRA_DIR/servers/default"
+
+ run "$ABRA" server ls
+ assert_success
+ assert_output --partial 'default'
+ assert_output --partial 'local'
+ assert_output --partial 'n/a'
+
+ run docker swarm leave --force
+ assert_success
+
+ _rm_default_server
+}
+
+@test "filter by problem" {
+ run "$ABRA" server ls --problems
+ assert_success
+ assert_output --partial 'all servers wired up correctly'
+
+ run docker context create --docker host=ssh://incorrect nowhere.com
+ assert_success
+
+ run mkdir -p "$ABRA_DIR/servers/nowhere.com"
+ assert_success
+
+ run "$ABRA" server ls --problems
+ assert_success
+ assert_output --partial 'unknown'
+
+ run rm -rf "$ABRA_DIR/servers/nowhere.com"
+ assert_success
+
+ run docker context rm nowhere.com
+ assert_success
+}
+
+@test "machine readable output" {
+ run "$ABRA" server ls --machine
+ assert_success
+
+ expectedOutput='[{"name":"'
+ expectedOutput+="$TEST_SERVER"
+ expectedOutput+='"'
+
+ assert_output --partial "$expectedOutput"
+}
diff --git a/tests/integration/server_prune.bats b/tests/integration/server_prune.bats
new file mode 100644
index 00000000..0b908df0
--- /dev/null
+++ b/tests/integration/server_prune.bats
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate server" {
+ run $ABRA server prune --no-input
+ assert_failure
+ assert_output --partial 'no server provided'
+
+ run $ABRA server prune foo
+ assert_failure
+ assert_output --partial "server doesn't exist"
+}
+
+@test "prune containers, networks and images" {
+ run $ABRA server prune "$TEST_SERVER"
+ assert_success
+ assert_output --partial 'containers pruned'
+ assert_output --partial 'networks pruned'
+ assert_output --partial 'images pruned'
+}
+
+@test "prune all images" {
+ run $ABRA server prune "$TEST_SERVER" --all --debug
+ assert_success
+ assert_output --partial 'removing all images, not only dangling ones'
+}
+
+@test "prune volumes" {
+ run $ABRA server prune "$TEST_SERVER" --volumes
+ assert_success
+ assert_output --partial 'volumes pruned'
+}
diff --git a/tests/integration/server_remove.bats b/tests/integration/server_remove.bats
new file mode 100644
index 00000000..2d3b7f8a
--- /dev/null
+++ b/tests/integration/server_remove.bats
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+setup_file(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+ _add_server
+}
+
+teardown_file(){
+ _rm_server
+}
+
+setup(){
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
+}
+
+@test "validate server" {
+ run $ABRA server remove --no-input
+ assert_failure
+ assert_output --partial 'no server provided'
+
+ run $ABRA server remove foo
+ assert_failure
+ assert_output --partial "server doesn't exist"
+}
+
+@test "remove server" {
+ assert_exists "$ABRA_DIR/servers/$TEST_SERVER"
+ assert bash -c "docker context ls | grep -q $TEST_SERVER"
+
+ run $ABRA server remove --no-input "$TEST_SERVER"
+ assert_success
+ assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER"
+ refute bash -c "docker context ls | grep -q $TEST_SERVER"
+}
diff --git a/tests/integration/setup_suite.bash b/tests/integration/setup_suite.bash
new file mode 100644
index 00000000..f0438864
--- /dev/null
+++ b/tests/integration/setup_suite.bash
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+setup_suite(){
+ if [[ -z "${TEST_SERVER}" ]]; then
+ echo 'set $TEST_SERVER before running the test suite' >&3
+ exit 1
+ fi
+
+ if [[ -z "${ABRA_DIR}" ]]; then
+ echo 'set $ABRA_DIR before running the test suite' >&3
+ exit 1
+ fi
+
+ if [[ ! -f "$PWD/abra" ]]; then
+ make build-abra
+ fi
+
+ if [[ ! -f "$PWD/kadabra" ]]; then
+ make build-kadabra
+ fi
+
+ if [[ -d "$ABRA_DIR" ]]; then
+ rm -rf "$ABRA_DIR"
+ fi
+
+ # NOTE(d1): hack to copy over a local copy of the catalogue from the typical
+ # $HOME/.abra directory if it exists. This avoids a costly git clone over the
+ # network for every test invocation
+ if [[ ! -d "$ABRA_DIR/catalogue" ]]; then
+ if [[ -d "$HOME/.abra/catalogue" ]]; then
+ mkdir -p "$ABRA_DIR"
+ cp -r "$HOME/.abra/catalogue" "$ABRA_DIR"
+ git -C "$ABRA_DIR/catalogue" checkout .
+ fi
+ fi
+}
+
+teardown_suite(){
+ if [[ -d "$ABRA_DIR" ]]; then
+ rm -rf "$ABRA_DIR"
+ fi
+}
diff --git a/tests/integration/upgrade.bats b/tests/integration/upgrade.bats
index 6022cfde..5dbe65f2 100644
--- a/tests/integration/upgrade.bats
+++ b/tests/integration/upgrade.bats
@@ -1,40 +1,39 @@
#!/usr/bin/env bash
setup() {
- DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
- source "$DIR/helpers.sh"
- _setup_env
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
if [[ -f "$HOME/.local/bin/abra" ]]; then
mv "$HOME/.local/bin/abra" "$HOME/.local/bin/abra_before_test"
fi
}
-@test "abra upgrade" {
- run $ABRA upgrade
- assert_success
- assert_output --partial 'Public interest infrastructure'
-
- assert [ -f "$HOME/.local/bin/abra" ]
- run $HOME/.local/bin/abra -v
- assert_output --partial 'beta'
-}
-
-@test "abra upgrade release candidate" {
- run $ABRA upgrade --rc
- assert_success
- assert_output --partial 'Public interest infrastructure'
-
- assert [ -f "$HOME/.local/bin/abra" ]
- run $HOME/.local/bin/abra -v
- assert_output --partial '-rc'
-}
-
teardown(){
- _default_teardown
-
if [[ -f "$HOME/.local/bin/abra_before_test" ]]; then
rm -rf "$HOME/.local/bin/abra"
mv "$HOME/.local/bin/abra_before_test" "$HOME/.local/bin/abra"
fi
}
+
+# bats test_tags=slow
+@test "abra upgrade" {
+ run $ABRA upgrade
+ assert_success
+ assert_output --partial 'Public interest infrastructure'
+
+ assert_exists "$HOME/.local/bin/abra"
+ run "$HOME/.local/bin/abra" -v
+ assert_output --partial 'beta'
+}
+
+# bats test_tags=slow
+@test "abra upgrade release candidate" {
+ run $ABRA upgrade --rc
+ assert_success
+ assert_output --partial 'Public interest infrastructure'
+
+ assert_exists "$HOME/.local/bin/abra"
+ run "$HOME/.local/bin/abra" -v
+ assert_output --partial '-rc'
+}
diff --git a/tests/integration/version.bats b/tests/integration/version.bats
index dcc77a08..71da7632 100644
--- a/tests/integration/version.bats
+++ b/tests/integration/version.bats
@@ -1,9 +1,8 @@
#!/usr/bin/env bash
setup() {
- DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
- source "$DIR/helpers.sh"
- _setup_env
+ load "$PWD/tests/integration/helpers/common"
+ _common_setup
}
@test "abra version" {
@@ -11,7 +10,3 @@ setup() {
assert_success
assert_output --partial 'dev'
}
-
-teardown(){
- _default_teardown
-}
diff --git a/tests/manual/.envrc.sample b/tests/manual/.envrc.sample
deleted file mode 100644
index f16b6c15..00000000
--- a/tests/manual/.envrc.sample
+++ /dev/null
@@ -1,4 +0,0 @@
-GANDI_TOKEN=...
-HCLOUD_TOKEN=...
-REGISTRY_PASSWORD=...
-REGISTRY_USERNAME=...
diff --git a/tests/manual/.gitignore b/tests/manual/.gitignore
deleted file mode 100644
index 98d8a5a6..00000000
--- a/tests/manual/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-logs
diff --git a/tests/manual/README.md b/tests/manual/README.md
deleted file mode 100644
index f51e2573..00000000
--- a/tests/manual/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# integration tests
-
-> You need to be a member of Autonomic Co-op to run these tests, sorry!
-
-`testfunctions.sh` contains the functions necessary to save and manipulate
-logs. Run `test_all.sh logdir` to run tests specified in that file and save the
-logs to `logdir`.
-
-When creating new tests, make sure the test command is a one-liner (you can use
-`;` to separate commands). Include `testfunctions.sh` and then write your tests
-like this:
-
-```
-run_test '$ABRA other stuff here'
-```
-
-By default, the testing script will ask after every command if the execution
-succeeded. If you reply `n`, it will log the test in the `logdir`. If you want
-all tests to run without questions, run `export logall=yes` before executing
-the test script.
-
-To run tests, you'll need to prepare your environment:
-
-```
-cp .envrc.sample .envrc # fill out values...
-direnv allow
-./test_all.sh logs
-```
diff --git a/tests/manual/app.sh b/tests/manual/app.sh
deleted file mode 100755
index 93f43c98..00000000
--- a/tests/manual/app.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-source ./testfunctions.sh
-source ./common.sh
-
-run_test '$ABRA app ls'
-
-run_test '$ABRA app ls --status'
-
-run_test '$ABRA app ls --type wordpress'
-
-run_test '$ABRA app ls --type wordpress --server swarm.autonomic.zone'
-
-run_test '$ABRA app ls --type wordpress --server swarm.autonomic.zone --status'
diff --git a/tests/manual/cmd.sh b/tests/manual/cmd.sh
deleted file mode 100755
index 03280e52..00000000
--- a/tests/manual/cmd.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-source ./testfunctions.sh
-source ./common.sh
-
-create_server_app_recipe
-
-run_test '$ABRA app cmd foo.com test --local'
-
-run_test '$ABRA app cmd foo.com test --local -- foo'
-
-run_test '$ABRA app cmd foo.com test --local -- foo bar baz'
-
-clean_server_app_recipe
diff --git a/tests/manual/common.sh b/tests/manual/common.sh
deleted file mode 100755
index a53a2c63..00000000
--- a/tests/manual/common.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-set -e
-
-create_server_app_recipe() {
- ln -srf ../resources/testapp ~/.abra/servers/foo.com
- ln -srf ../resources/testrecipe ~/.abra/recipes
-}
-
-clean_server_app_recipe() {
- unlink ~/.abra/servers/foo.com
- unlink ~/.abra/recipes/testrecipe
-}
-
-function init() {
- ABRA="$(pwd)/../../abra"
- INSTALLER_URL="https://git.coopcloud.tech/coop-cloud/abra/raw/branch/main/scripts/installer/installer"
-
- export PATH=$PATH:$HOME/.local/bin
-
- echo "choosing $ABRA as abra command"
- echo "choosing $INSTALLER_URL as abra installer url"
-}
-
-init "$@"
diff --git a/tests/manual/records.sh b/tests/manual/records.sh
deleted file mode 100755
index a40856c5..00000000
--- a/tests/manual/records.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-source ./testfunctions.sh
-source ./common.sh
-
-run_test "$ABRA record new \
- --provider gandi \
- --record-type A \
- --record-name integration-tests \
- --record-value 192.157.2.21 \
- --no-input coopcloud.tech \
- "
-
-run_test '$ABRA record list --provider gandi coopcloud.tech'
-
-run_test "$ABRA record rm \
- --provider gandi \
- --record-type A \
- --record-name integration-tests \
- --no-input coopcloud.tech
- "
diff --git a/tests/manual/server.sh b/tests/manual/server.sh
deleted file mode 100755
index 6a1d0381..00000000
--- a/tests/manual/server.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-source ./testfunctions.sh
-source ./common.sh
-
-run_test '$ABRA server new --provider hetzner-cloud --hetzner-name integration-tests --no-input'
-
-run_test '$ABRA server ls'
-
-run_test '$ABRA server rm --provider hetzner-cloud --hetzner-name int-core --server --no-input'
diff --git a/tests/manual/test_all.sh b/tests/manual/test_all.sh
deleted file mode 100755
index 23187234..00000000
--- a/tests/manual/test_all.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-if [ -z $1 ]; then
- echo "usage: ./test_all.sh logdir"
- exit
-fi
-
-res_dir=$1/
-if [[ ! -d "$res_dir" ]]; then
- mkdir "$res_dir"
-fi
-
-# Usage: run_test [number] [name] [command]
-run_test () {
- logfile="$res_dir/$1-$2.log"
- echo $logfile
-}
-
-testScripts=("app.sh" "autocomplete.sh" "catalogue.sh" "install.sh" "recipe.sh" "records.sh" "server.sh", "cmd.sh")
-
-for i in "${testScripts[@]}"; do
- cmd="./$i $res_dir${i/sh/log}"
- eval $cmd
-done
diff --git a/tests/manual/testfunctions.sh b/tests/manual/testfunctions.sh
deleted file mode 100644
index d23d1b5c..00000000
--- a/tests/manual/testfunctions.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-if [ -z $1 ]; then
- logfile=/dev/null
-else
- logfile=$1
-fi
-
-if [ -z $logall ]; then
- logall=no
-fi
-
-run_test () {
- if [ -z "$@" ]; then
- echo "run_test needs a command to run"
- else
- tempLogfile=$(mktemp)
- cmd=$(eval echo "$@")
- echo -e "\\n------------ INPUT -------------------" | tee -a $tempLogfile
- echo "$" "$cmd" | tee -a $tempLogfile
- echo "------------ OUTPUT ------------------" | tee -a $tempLogfile
- eval $cmd 2>&1 | tee -a $tempLogfile
- if [ $logall = "yes" ]; then
- cat $tempLogfile >> $logfile
- echo -e "\\n\\n" >> $logfile
- else
- read -N 1 -p "Did the test pass? [y/n]: " pass
- if [ $pass = 'n' ]; then
- cat $tempLogfile >> $logfile
- echo -e "\\n\\n" >> $logfile
- fi
- fi
- rm $tempLogfile
- fi
-}
diff --git a/tests/resources/testapp/foo.com.env b/tests/resources/testapp/foo.com.env
deleted file mode 100644
index 66deff11..00000000
--- a/tests/resources/testapp/foo.com.env
+++ /dev/null
@@ -1 +0,0 @@
-TYPE=test
diff --git a/tests/resources/testapp/testapp b/tests/resources/testapp/testapp
deleted file mode 120000
index 945c9b46..00000000
--- a/tests/resources/testapp/testapp
+++ /dev/null
@@ -1 +0,0 @@
-.
\ No newline at end of file
diff --git a/tests/resources/testrecipe/abra.sh b/tests/resources/testrecipe/abra.sh
deleted file mode 100644
index 70c2cb5e..00000000
--- a/tests/resources/testrecipe/abra.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-test(){
- echo "1: $1"
- echo "2: $2"
- echo "all: $@"
-}
diff --git a/tests/resources/testrecipe/compose.yml b/tests/resources/testrecipe/compose.yml
deleted file mode 100644
index f240b68c..00000000
--- a/tests/resources/testrecipe/compose.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-version: "3.8"
-
-services:
- app: []