test: moar integration tests [ci skip]
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				continuous-integration/drone/pr Build is failing
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	continuous-integration/drone/pr Build is failing
				
			This commit is contained in:
		| @ -1,4 +0,0 @@ | ||||
| GANDI_TOKEN=... | ||||
| HCLOUD_TOKEN=... | ||||
| REGISTRY_PASSWORD=... | ||||
| REGISTRY_USERNAME=... | ||||
| @ -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
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -5,5 +5,5 @@ | ||||
| /kadabra | ||||
| abra | ||||
| dist/ | ||||
| tests/integration/.abra/catalogue | ||||
| tests/integration/.bats | ||||
| vendor/ | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										19
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								Makefile
									
									
									
									
									
								
							| @ -9,12 +9,18 @@ export GOPRIVATE=coopcloud.tech | ||||
|  | ||||
| all: format check build test | ||||
|  | ||||
| run: | ||||
| run-abra: | ||||
| 	@go run -ldflags=$(LDFLAGS) $(ABRA) | ||||
|  | ||||
| install: | ||||
| run-kadabra: | ||||
| 	@go run -ldflags=$(LDFLAGS) $(KADABRA) | ||||
|  | ||||
| install-abra: | ||||
| 	@go install -ldflags=$(LDFLAGS) $(ABRA) | ||||
|  | ||||
| install-kadaabra: | ||||
| 	@go install -ldflags=$(LDFLAGS) $(KADABRA) | ||||
|  | ||||
| build-abra: | ||||
| 	@go build -v -ldflags=$(LDFLAGS) $(ABRA) | ||||
|  | ||||
| @ -36,15 +42,8 @@ check: | ||||
| 	@test -z $$(gofmt -l .) || \ | ||||
| 	(echo "gofmt: formatting issue - run 'make format' to resolve" && exit 1) | ||||
|  | ||||
| test: | ||||
| unit-test: | ||||
| 	@go test ./... -cover -v | ||||
|  | ||||
| loc: | ||||
| 	@find . -name "*.go" | xargs wc -l | ||||
|  | ||||
| loc-author: | ||||
| 	@git ls-files -z | \ | ||||
| 		xargs -0rn 1 -P "$$(nproc)" -I{} sh -c 'git blame -w -M -C -C --line-porcelain -- {} | grep -I --line-buffered "^author "' | \ | ||||
|    	sort -f | \ | ||||
|    	uniq -ic | \ | ||||
|    	sort -n | ||||
|  | ||||
| @ -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! | ||||
|  | ||||
| @ -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] | ||||
|  | ||||
| @ -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, | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
| 			} | ||||
|  | ||||
| @ -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, | ||||
| } | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| 	} | ||||
|  | ||||
| @ -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) | ||||
| 			} | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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} | ||||
|  | ||||
| @ -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) | ||||
| 		} | ||||
|  | ||||
|  | ||||
| @ -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?")) | ||||
|  | ||||
| @ -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{ | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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) | ||||
| 		} | ||||
|  | ||||
|  | ||||
| @ -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, | ||||
| } | ||||
|  | ||||
| @ -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, | ||||
| } | ||||
|  | ||||
| @ -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{ | ||||
|  | ||||
| @ -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
									
								
							
							
						
						
									
										180
									
								
								cli/internal/deploy.go
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
| 		} | ||||
|  | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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()) | ||||
| 		} | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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, | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
| 			} | ||||
|  | ||||
| @ -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) | ||||
| 		} | ||||
|  | ||||
| @ -118,7 +118,6 @@ developer machine. | ||||
| 		internal.DebugFlag, | ||||
| 		internal.NoInputFlag, | ||||
| 		localFlag, | ||||
| 		internal.OfflineFlag, | ||||
| 	}, | ||||
| 	Before:    internal.SubCommandBefore, | ||||
| 	ArgsUsage: "<domain>", | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
|  | ||||
| @ -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 | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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
									
								
							
							
						
						
									
										10
									
								
								tests/README.md
									
									
									
									
									
										Normal 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. | ||||
							
								
								
									
										80
									
								
								tests/integration/app_backup.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								tests/integration/app_backup.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										62
									
								
								tests/integration/app_check.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								tests/integration/app_check.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										115
									
								
								tests/integration/app_cmd.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								tests/integration/app_cmd.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										26
									
								
								tests/integration/app_config.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tests/integration/app_config.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										123
									
								
								tests/integration/app_cp.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								tests/integration/app_cp.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										276
									
								
								tests/integration/app_deploy.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								tests/integration/app_deploy.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										43
									
								
								tests/integration/app_errors.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tests/integration/app_errors.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										130
									
								
								tests/integration/app_list.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								tests/integration/app_list.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										33
									
								
								tests/integration/app_logs.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tests/integration/app_logs.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										55
									
								
								tests/integration/app_new.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								tests/integration/app_new.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										46
									
								
								tests/integration/app_ps.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								tests/integration/app_ps.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										148
									
								
								tests/integration/app_remove.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								tests/integration/app_remove.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										53
									
								
								tests/integration/app_restart.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								tests/integration/app_restart.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										146
									
								
								tests/integration/app_restore.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								tests/integration/app_restore.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										232
									
								
								tests/integration/app_rollback.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								tests/integration/app_rollback.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										60
									
								
								tests/integration/app_run.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								tests/integration/app_run.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										216
									
								
								tests/integration/app_secret.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								tests/integration/app_secret.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										48
									
								
								tests/integration/app_services.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								tests/integration/app_services.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										48
									
								
								tests/integration/app_undeploy.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								tests/integration/app_undeploy.bats
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										27
									
								
								tests/integration/app_upgrade.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tests/integration/app_upgrade.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										98
									
								
								tests/integration/app_version.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								tests/integration/app_version.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										98
									
								
								tests/integration/app_volume.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								tests/integration/app_volume.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
| @ -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" | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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" | ||||
| } | ||||
|  | ||||
| @ -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" | ||||
| } | ||||
							
								
								
									
										42
									
								
								tests/integration/helpers/app.bash
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/integration/helpers/app.bash
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										19
									
								
								tests/integration/helpers/common.bash
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tests/integration/helpers/common.bash
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										11
									
								
								tests/integration/helpers/git.bash
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/integration/helpers/git.bash
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										16
									
								
								tests/integration/helpers/recipe.bash
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/integration/helpers/recipe.bash
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										19
									
								
								tests/integration/helpers/server.bash
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tests/integration/helpers/server.bash
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
| @ -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' | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
							
								
								
									
										16
									
								
								tests/integration/recipe_fetch.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/integration/recipe_fetch.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										50
									
								
								tests/integration/recipe_lint.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								tests/integration/recipe_lint.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										20
									
								
								tests/integration/recipe_list.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tests/integration/recipe_list.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										42
									
								
								tests/integration/recipe_new.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/integration/recipe_new.bats
									
									
									
									
									
										Normal 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!' | ||||
| } | ||||
							
								
								
									
										22
									
								
								tests/integration/recipe_release.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/integration/recipe_release.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										39
									
								
								tests/integration/recipe_sync.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								tests/integration/recipe_sync.bats
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										8
									
								
								tests/integration/recipe_upgrade.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/integration/recipe_upgrade.bats
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| setup() { | ||||
|   load "$PWD/tests/integration/helpers/common" | ||||
|   _common_setup | ||||
| } | ||||
|  | ||||
| # TODO(d1): implement | ||||
							
								
								
									
										12
									
								
								tests/integration/recipe_version.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/integration/recipe_version.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										53
									
								
								tests/integration/server_add.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								tests/integration/server_add.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										75
									
								
								tests/integration/server_list.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								tests/integration/server_list.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										46
									
								
								tests/integration/server_prune.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								tests/integration/server_prune.bats
									
									
									
									
									
										Normal 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' | ||||
| } | ||||
							
								
								
									
										36
									
								
								tests/integration/server_remove.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								tests/integration/server_remove.bats
									
									
									
									
									
										Normal 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" | ||||
| } | ||||
							
								
								
									
										42
									
								
								tests/integration/setup_suite.bash
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/integration/setup_suite.bash
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
| @ -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' | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -1,4 +0,0 @@ | ||||
| GANDI_TOKEN=... | ||||
| HCLOUD_TOKEN=... | ||||
| REGISTRY_PASSWORD=... | ||||
| REGISTRY_USERNAME=... | ||||
							
								
								
									
										1
									
								
								tests/manual/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								tests/manual/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| logs | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user