test: moar integration tests [ci skip]
continuous-integration/drone/pr Build is failing Details

This commit is contained in:
decentral1se 2023-09-07 18:50:25 +02:00
parent 7a9224b2b2
commit 0be532692d
Signed by: decentral1se
GPG Key ID: 03789458B3D0C410
112 changed files with 3412 additions and 1142 deletions

View File

@ -1,4 +0,0 @@
GANDI_TOKEN=...
HCLOUD_TOKEN=...
REGISTRY_PASSWORD=...
REGISTRY_USERNAME=...

View File

@ -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

2
.gitignore vendored
View File

@ -5,5 +5,5 @@
/kadabra
abra
dist/
tests/integration/.abra/catalogue
tests/integration/.bats
vendor/

View File

@ -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

View File

@ -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

View File

@ -8,6 +8,6 @@ The Co-op Cloud utility belt 🎩🐇
<a href="https://github.com/egonelbre/gophers"><img align="right" width="150" src="https://github.com/egonelbre/gophers/raw/master/.thumb/sketch/adventure/poking-fire.png"/></a>
`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!

View File

@ -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]

View File

@ -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: "<domain>",
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,
}

View File

@ -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)
}

View File

@ -20,9 +20,9 @@ var appConfigCommand = cli.Command{
ArgsUsage: "<domain>",
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,
}

View File

@ -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 <domain> app:/myfile.txt .
abra app cp <domain> 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 {

View File

@ -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 <domain>" 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:
// "<service> <command> <arguments>|<service> <command> <arguments>|... "
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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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: "[<recipe>]",
Before: internal.SubCommandBefore,
ArgsUsage: "[<recipe>]",
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

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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}

View File

@ -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)
}

View File

@ -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: "<domain> <service> <args>...",
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 <service> provided?"))

View File

@ -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: "<domain> [<secret-name>]",
@ -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{

View File

@ -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: "<domain>",
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 {

View File

@ -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)
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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{

View File

@ -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: "[<recipe>]",
ArgsUsage: "[<recipe>]",
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.

180
cli/internal/deploy.go Normal file
View File

@ -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:
// "<service> <command> <arguments>|<service> <command> <arguments>|... "
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
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)

View File

@ -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())
}

View File

@ -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 {

View File

@ -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 <version> can be specifed on the command-line or Abra can attempt to
auto-generate it for you. The <recipe> configuration will be updated on the
local file system.
`,
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,
}

View File

@ -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: "<recipe>",
ArgsUsage: "<recipe>",
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)
}

View File

@ -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)
}

View File

@ -118,7 +118,6 @@ developer machine.
internal.DebugFlag,
internal.NoInputFlag,
localFlag,
internal.OfflineFlag,
},
Before: internal.SubCommandBefore,
ArgsUsage: "<domain>",

View File

@ -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)

View File

@ -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: "[<server>]",
@ -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)

View File

@ -15,7 +15,7 @@ import (
var serverRemoveCommand = cli.Command{
Name: "remove",
Aliases: []string{"rm"},
ArgsUsage: "[<server>]",
ArgsUsage: "<server>",
Usage: "Remove a managed server",
Description: `Remove a managed server.

View File

@ -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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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
}
}

10
tests/README.md Normal file
View File

@ -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.

View File

@ -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
}

View File

@ -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"
}

View File

@ -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
}

View File

@ -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'
}

View File

@ -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 <src> argument'
run $ABRA app cp "$TEST_APP_DOMAIN" myfile.txt
assert_failure
assert_output --partial 'missing <dest> 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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
}

View File

@ -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'
}

View File

@ -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'
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 <service>'
run $ABRA app restore "$TEST_APP_DOMAIN" app
assert_failure
assert_output --partial 'missing <file>'
}
@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
}

View File

@ -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"
}

View File

@ -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 <service> provided'
run $ABRA app run "$TEST_APP_DOMAIN" app
assert_failure
assert_output --partial 'no <args> 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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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'
}

View File

@ -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"
}

View File

@ -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'
}

View File

@ -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"
}

View File

@ -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
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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
}

View File

@ -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"
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
}

View File

@ -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'
}

View File

@ -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
}

View File

@ -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"
}

View File

@ -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'
}

View File

@ -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'
}

View File

@ -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!'
}

View File

@ -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'
}

View File

@ -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

View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
setup() {
load "$PWD/tests/integration/helpers/common"
_common_setup
}
# TODO(d1): implement

View File

@ -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'
}

View File

@ -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 <domain> 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"
}

View File

@ -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"
}

View File

@ -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'
}

View File

@ -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"
}

View File

@ -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
}

View File

@ -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'
}

View File

@ -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
}

View File

@ -1,4 +0,0 @@
GANDI_TOKEN=...
HCLOUD_TOKEN=...
REGISTRY_PASSWORD=...
REGISTRY_USERNAME=...

View File

@ -1 +0,0 @@
logs

Some files were not shown because too many files have changed in this diff Show More