forked from toolshed/abra
Compare commits
81 Commits
0.8.0-rc2-
...
0.8.0-beta
Author | SHA1 | Date | |
---|---|---|---|
1e4f1b4ade
|
|||
306fe02d1c
|
|||
e4610f8ad5
|
|||
e1f900de14
|
|||
d5b18d74ef
|
|||
776a83d8d1
|
|||
810cea8269
|
|||
c0f3e6f2a4
|
|||
7b240059b0
|
|||
c456d13881
|
|||
c7c553164d
|
|||
7616528f4e
|
|||
6cd85f7239
|
|||
b1774cc44b
|
|||
e438fc6e8e
|
|||
c065ceb1f0
|
|||
ce4b775428
|
|||
d02f659bf8
|
|||
f3ded88ed8
|
|||
bf648eeb5d
|
|||
533edbf172
|
|||
78b8cf9725
|
|||
f0560ca975
|
|||
ce7b4733d7
|
|||
575bfbb0fb
|
|||
510ce66570
|
|||
82631d9ab1
|
|||
358490e939
|
|||
79b9cc9be7
|
|||
9b6eb613aa
|
|||
8f1231e409
|
|||
aa37c936eb
|
|||
3d1158a425
|
|||
8788558cf1
|
|||
76035e003e
|
|||
b708382d26
|
|||
557b670fc5
|
|||
e116148c49
|
|||
d5593b69e0
|
|||
0be532692d
|
|||
7a9224b2b2
|
|||
e73d1a8359 | |||
f8c49c82c8
|
|||
ab7edd2a62
|
|||
b1888dcf0f
|
|||
e5e122296f | |||
83bf148304
|
|||
d80b882b83 | |||
c345c6f5f1 | |||
f8c4fd72a3
|
|||
10f612f998
|
|||
58e78e4d7c | |||
25258d3d64 | |||
b3bd058962 | |||
b4fd7fd77c | |||
64cfdae6b7 | |||
0a765794f2 | |||
18dc6e9434 | |||
4ba4107288 | |||
d9b4f4ef3b
|
|||
c365dcf96d | |||
0c6a7cc0b8 | |||
6640cfab64
|
|||
71addcd1b2 | |||
60c0e55e3d | |||
e42139fd83 | |||
2d826e47d0 | |||
2db172ea5a | |||
2077658f6a | |||
502e26b534 | |||
e22b692ada | |||
5ae73f700e
|
|||
63d419caae
|
|||
179b66d65c
|
|||
c9144d90f3
|
|||
ebf5d82c56
|
|||
8bb98ed0ed
|
|||
23f5745cb8
|
|||
2cd453ae8d
|
|||
e42cc0f91d
|
|||
1de45a6508 |
38
.drone.yml
38
.drone.yml
@ -3,19 +3,19 @@ kind: pipeline
|
|||||||
name: coopcloud.tech/abra
|
name: coopcloud.tech/abra
|
||||||
steps:
|
steps:
|
||||||
- name: make check
|
- name: make check
|
||||||
image: golang:1.20
|
image: golang:1.21
|
||||||
commands:
|
commands:
|
||||||
- make check
|
- make check
|
||||||
|
|
||||||
- name: make build
|
- name: make build
|
||||||
image: golang:1.20
|
image: golang:1.21
|
||||||
commands:
|
commands:
|
||||||
- make build
|
- make build
|
||||||
depends_on:
|
depends_on:
|
||||||
- make check
|
- make check
|
||||||
|
|
||||||
- name: make test
|
- name: make test
|
||||||
image: golang:1.20
|
image: golang:1.21
|
||||||
commands:
|
commands:
|
||||||
- make test
|
- make test
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -47,22 +47,22 @@ steps:
|
|||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
|
|
||||||
- name: publish image
|
# - name: publish image
|
||||||
image: plugins/docker
|
# image: plugins/docker
|
||||||
settings:
|
# settings:
|
||||||
auto_tag: true
|
# auto_tag: true
|
||||||
username: 3wordchant
|
# username: 3wordchant
|
||||||
password:
|
# password:
|
||||||
from_secret: git_coopcloud_tech_token_3wc
|
# from_secret: git_coopcloud_tech_token_3wc
|
||||||
repo: git.coopcloud.tech/coop-cloud/abra
|
# repo: git.coopcloud.tech/coop-cloud/abra
|
||||||
tags: dev
|
# tags: dev
|
||||||
registry: git.coopcloud.tech
|
# registry: git.coopcloud.tech
|
||||||
when:
|
# when:
|
||||||
event:
|
# event:
|
||||||
exclude:
|
# exclude:
|
||||||
- pull_request
|
# - pull_request
|
||||||
depends_on:
|
# depends_on:
|
||||||
- make check
|
# - make check
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
GANDI_TOKEN=...
|
|
||||||
HCLOUD_TOKEN=...
|
|
||||||
REGISTRY_PASSWORD=...
|
|
||||||
REGISTRY_USERNAME=...
|
|
@ -1,6 +1,7 @@
|
|||||||
go env -w GOPRIVATE=coopcloud.tech
|
go env -w GOPRIVATE=coopcloud.tech
|
||||||
|
|
||||||
# export PASSWORD_STORE_DIR=$(pwd)/../../autonomic/passwords/passwords/
|
# export PASSWORD_STORE_DIR=$(pwd)/../../autonomic/passwords/passwords/
|
||||||
# export HCLOUD_TOKEN=$(pass show logins/hetzner/cicd/api_key)
|
|
||||||
# export CAPSUL_TOKEN=...
|
# export ABRA_DIR="$HOME/.abra_test"
|
||||||
# export GITEA_TOKEN=...
|
# 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
|
/kadabra
|
||||||
abra
|
abra
|
||||||
dist/
|
dist/
|
||||||
tests/integration/.abra/catalogue
|
tests/integration/.bats
|
||||||
vendor/
|
vendor/
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# authors
|
# authors
|
||||||
|
|
||||||
> If you're looking at this and you hack on `abra` and you're not listed here,
|
> 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
|
- 3wordchant
|
||||||
- cassowary
|
- cassowary
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.20-alpine AS build
|
FROM golang:1.21-alpine AS build
|
||||||
|
|
||||||
ENV GOPRIVATE coopcloud.tech
|
ENV GOPRIVATE coopcloud.tech
|
||||||
|
|
||||||
|
22
Makefile
22
Makefile
@ -9,15 +9,24 @@ export GOPRIVATE=coopcloud.tech
|
|||||||
|
|
||||||
all: format check build test
|
all: format check build test
|
||||||
|
|
||||||
run:
|
run-abra:
|
||||||
@go run -ldflags=$(LDFLAGS) $(ABRA)
|
@go run -ldflags=$(LDFLAGS) $(ABRA)
|
||||||
|
|
||||||
install:
|
run-kadabra:
|
||||||
|
@go run -ldflags=$(LDFLAGS) $(KADABRA)
|
||||||
|
|
||||||
|
install-abra:
|
||||||
@go install -ldflags=$(LDFLAGS) $(ABRA)
|
@go install -ldflags=$(LDFLAGS) $(ABRA)
|
||||||
|
|
||||||
build-dev:
|
install-kadaabra:
|
||||||
|
@go install -ldflags=$(LDFLAGS) $(KADABRA)
|
||||||
|
|
||||||
|
build-abra:
|
||||||
@go build -v -ldflags=$(LDFLAGS) $(ABRA)
|
@go build -v -ldflags=$(LDFLAGS) $(ABRA)
|
||||||
|
|
||||||
|
build-kadabra:
|
||||||
|
@go build -v -ldflags=$(LDFLAGS) $(KADABRA)
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@go build -v -ldflags=$(DIST_LDFLAGS) $(ABRA)
|
@go build -v -ldflags=$(DIST_LDFLAGS) $(ABRA)
|
||||||
@go build -v -ldflags=$(DIST_LDFLAGS) $(KADABRA)
|
@go build -v -ldflags=$(DIST_LDFLAGS) $(KADABRA)
|
||||||
@ -38,10 +47,3 @@ test:
|
|||||||
|
|
||||||
loc:
|
loc:
|
||||||
@find . -name "*.go" | xargs wc -l
|
@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>
|
<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!
|
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/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
containerPkg "coopcloud.tech/abra/pkg/container"
|
containerPkg "coopcloud.tech/abra/pkg/container"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/upstream/container"
|
"coopcloud.tech/abra/pkg/upstream/container"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
@ -42,6 +41,7 @@ var appBackupCommand = cli.Command{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
@ -72,17 +72,27 @@ 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.
|
This single file can be used to restore your app. See "abra app restore" for more.
|
||||||
`,
|
`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
recipe, err := recipePkg.Get(app.Recipe, internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
recipe, err := recipe.Get(app.Recipe, conf)
|
if !internal.Chaos {
|
||||||
if err != nil {
|
if err := recipePkg.EnsureIsClean(app.Recipe); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !internal.Offline {
|
||||||
|
if err := recipePkg.EnsureUpToDate(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureLatest(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
backupConfigs := make(map[string]backupConfig)
|
backupConfigs := make(map[string]backupConfig)
|
||||||
@ -114,6 +124,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)
|
serviceName := c.Args().Get(1)
|
||||||
if serviceName != "" {
|
if serviceName != "" {
|
||||||
backupConfig, ok := backupConfigs[serviceName]
|
backupConfig, ok := backupConfigs[serviceName]
|
||||||
|
@ -8,7 +8,8 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -16,16 +17,37 @@ import (
|
|||||||
var appCheckCommand = cli.Command{
|
var appCheckCommand = cli.Command{
|
||||||
Name: "check",
|
Name: "check",
|
||||||
Aliases: []string{"chk"},
|
Aliases: []string{"chk"},
|
||||||
Usage: "Check if app is configured correctly",
|
Usage: "Check if an app is configured correctly",
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
if err := recipe.EnsureExists(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !internal.Chaos {
|
||||||
|
if err := recipePkg.EnsureIsClean(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !internal.Offline {
|
||||||
|
if err := recipePkg.EnsureUpToDate(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureLatest(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
envSamplePath := path.Join(config.RECIPES_DIR, app.Recipe, ".env.sample")
|
envSamplePath := path.Join(config.RECIPES_DIR, app.Recipe, ".env.sample")
|
||||||
if _, err := os.Stat(envSamplePath); err != nil {
|
if _, err := os.Stat(envSamplePath); err != nil {
|
||||||
@ -56,5 +78,4 @@ var appCheckCommand = cli.Command{
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -40,18 +41,33 @@ Example:
|
|||||||
internal.RemoteUserFlag,
|
internal.RemoteUserFlag,
|
||||||
internal.TtyFlag,
|
internal.TtyFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
if err := recipe.EnsureExists(app.Recipe); err != nil {
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !internal.Chaos {
|
||||||
|
if err := recipePkg.EnsureIsClean(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !internal.Offline {
|
||||||
|
if err := recipePkg.EnsureUpToDate(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureLatest(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if internal.LocalCmd && internal.RemoteUser != "" {
|
if internal.LocalCmd && internal.RemoteUser != "" {
|
||||||
internal.ShowSubcommandHelpAndError(c, errors.New("cannot use --local & --user together"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("cannot use --local & --user together"))
|
||||||
}
|
}
|
||||||
@ -67,6 +83,10 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if internal.LocalCmd {
|
if internal.LocalCmd {
|
||||||
|
if !(len(c.Args()) >= 2) {
|
||||||
|
internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments"))
|
||||||
|
}
|
||||||
|
|
||||||
cmdName := c.Args().Get(1)
|
cmdName := c.Args().Get(1)
|
||||||
if err := internal.EnsureCommand(abraSh, app.Recipe, cmdName); err != nil {
|
if err := internal.EnsureCommand(abraSh, app.Recipe, cmdName); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -78,6 +98,7 @@ Example:
|
|||||||
for k, v := range app.Env {
|
for k, v := range app.Env {
|
||||||
exportEnv = exportEnv + fmt.Sprintf("%s='%s'; ", k, v)
|
exportEnv = exportEnv + fmt.Sprintf("%s='%s'; ", k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceAndExec string
|
var sourceAndExec string
|
||||||
if hasCmdArgs {
|
if hasCmdArgs {
|
||||||
logrus.Debugf("parsed following command arguments: %s", parsedCmdArgs)
|
logrus.Debugf("parsed following command arguments: %s", parsedCmdArgs)
|
||||||
@ -98,6 +119,10 @@ Example:
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if !(len(c.Args()) >= 3) {
|
||||||
|
internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments"))
|
||||||
|
}
|
||||||
|
|
||||||
targetServiceName := c.Args().Get(1)
|
targetServiceName := c.Args().Get(1)
|
||||||
|
|
||||||
cmdName := c.Args().Get(2)
|
cmdName := c.Args().Get(2)
|
||||||
@ -129,6 +154,11 @@ Example:
|
|||||||
logrus.Debug("did not detect any command arguments")
|
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 {
|
if err := internal.RunCmdRemote(cl, app, abraSh, targetServiceName, cmdName, parsedCmdArgs); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,9 @@ var appConfigCommand = cli.Command{
|
|||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
appName := c.Args().First()
|
appName := c.Args().First()
|
||||||
|
|
||||||
@ -61,5 +61,4 @@ var appConfigCommand = cli.Command{
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/container"
|
"coopcloud.tech/abra/pkg/container"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
@ -28,10 +27,9 @@ var appCpCommand = cli.Command{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Usage: "Copy files to/from a running app service",
|
Usage: "Copy files to/from a deployed app service",
|
||||||
Description: `
|
Description: `
|
||||||
Copy files to and from any app service file system.
|
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:
|
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 {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
src := c.Args().Get(1)
|
src := c.Args().Get(1)
|
||||||
dst := c.Args().Get(2)
|
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")
|
logrus.Debugf("assuming transfer is going TO the container")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isToContainer {
|
if isToContainer {
|
||||||
if _, err := os.Stat(dstPath); os.IsNotExist(err) {
|
if _, err := os.Stat(srcPath); os.IsNotExist(err) {
|
||||||
logrus.Fatalf("%s does not exist locally?", dstPath)
|
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 {
|
if err := configureAndCp(c, cl, app, srcPath, dstPath, service, isToContainer); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureAndCp(
|
func configureAndCp(
|
||||||
@ -126,10 +123,6 @@ func configureAndCp(
|
|||||||
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
|
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
|
||||||
|
|
||||||
if isToContainer {
|
if isToContainer {
|
||||||
if _, err := os.Stat(srcPath); err != nil {
|
|
||||||
logrus.Fatalf("%s does not exist?", srcPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
|
toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
|
||||||
content, err := archive.TarWithOptions(srcPath, toTarOpts)
|
content, err := archive.TarWithOptions(srcPath, toTarOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3,14 +3,9 @@ package app
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
@ -20,8 +15,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/upstream/stack"
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
|
||||||
dockerClient "github.com/docker/docker/client"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -30,7 +23,7 @@ var appDeployCommand = cli.Command{
|
|||||||
Name: "deploy",
|
Name: "deploy",
|
||||||
Aliases: []string{"d"},
|
Aliases: []string{"d"},
|
||||||
Usage: "Deploy an app",
|
Usage: "Deploy an app",
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain> [<version>]",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
@ -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
|
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.
|
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
|
including unstaged changes and can be useful for live hacking and testing new
|
||||||
recipes.
|
recipes.
|
||||||
`,
|
`,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
stackName := app.StackName()
|
stackName := app.StackName()
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
if err := recipe.EnsureExists(app.Recipe); err != nil {
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !internal.Chaos {
|
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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := recipe.Get(app.Recipe, conf)
|
r, err := recipe.Get(app.Recipe, internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -80,6 +81,11 @@ recipes.
|
|||||||
|
|
||||||
logrus.Debugf("checking whether %s is already deployed", stackName)
|
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)
|
isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -94,8 +100,17 @@ recipes.
|
|||||||
}
|
}
|
||||||
|
|
||||||
version := deployedVersion
|
version := deployedVersion
|
||||||
if version == "unknown" && !internal.Chaos {
|
specificVersion := c.Args().Get(1)
|
||||||
catl, err := recipe.ReadRecipeCatalogue(conf)
|
if specificVersion != "" {
|
||||||
|
version = specificVersion
|
||||||
|
logrus.Debugf("choosing %s as version to deploy", version)
|
||||||
|
if err := recipe.EnsureVersion(app.Recipe, version); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !internal.Chaos && specificVersion == "" {
|
||||||
|
catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -103,7 +118,21 @@ recipes.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(versions) > 0 {
|
|
||||||
|
if len(versions) == 0 && !internal.Chaos {
|
||||||
|
logrus.Warn("no published versions in catalogue, trying local recipe repository")
|
||||||
|
recipeVersions, err := recipe.GetRecipeVersions(app.Recipe, internal.Offline)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
for _, recipeVersion := range recipeVersions {
|
||||||
|
for version := range recipeVersion {
|
||||||
|
versions = append(versions, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(versions) > 0 && !internal.Chaos {
|
||||||
version = versions[len(versions)-1]
|
version = versions[len(versions)-1]
|
||||||
logrus.Debugf("choosing %s as version to deploy", version)
|
logrus.Debugf("choosing %s as version to deploy", version)
|
||||||
if err := recipe.EnsureVersion(app.Recipe, version); err != nil {
|
if err := recipe.EnsureVersion(app.Recipe, version); err != nil {
|
||||||
@ -116,22 +145,6 @@ recipes.
|
|||||||
}
|
}
|
||||||
version = formatter.SmallSHA(head.String())
|
version = formatter.SmallSHA(head.String())
|
||||||
logrus.Warn("no versions detected, using latest commit")
|
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 {
|
|
||||||
if err := recipe.EnsureVersion(app.Recipe, version); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,10 +166,11 @@ recipes.
|
|||||||
app.Env[k] = v
|
app.Env[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
composeFiles, err := config.GetAppComposeFiles(app.Recipe, app.Env)
|
composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deployOpts := stack.Deploy{
|
deployOpts := stack.Deploy{
|
||||||
Composefiles: composeFiles,
|
Composefiles: composeFiles,
|
||||||
Namespace: stackName,
|
Namespace: stackName,
|
||||||
@ -167,13 +181,14 @@ recipes.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.ExposeAllEnv(stackName, compose, app.Env)
|
config.ExposeAllEnv(stackName, compose, app.Env)
|
||||||
config.SetRecipeLabel(compose, stackName, app.Recipe)
|
config.SetRecipeLabel(compose, stackName, app.Recipe)
|
||||||
config.SetChaosLabel(compose, stackName, internal.Chaos)
|
config.SetChaosLabel(compose, stackName, internal.Chaos)
|
||||||
config.SetChaosVersionLabel(compose, stackName, version)
|
config.SetChaosVersionLabel(compose, stackName, version)
|
||||||
config.SetUpdateLabel(compose, stackName, app.Env)
|
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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,175 +218,10 @@ recipes.
|
|||||||
postDeployCmds, ok := app.Env["POST_DEPLOY_CMDS"]
|
postDeployCmds, ok := app.Env["POST_DEPLOY_CMDS"]
|
||||||
if ok && !internal.DontWaitConverge {
|
if ok && !internal.DontWaitConverge {
|
||||||
logrus.Debugf("run the following post-deploy commands: %s", postDeployCmds)
|
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)
|
logrus.Fatalf("attempting to run post deploy commands, saw: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 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 := 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/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
@ -56,8 +55,7 @@ the logs.
|
|||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -74,25 +72,23 @@ the logs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !internal.Watch {
|
if !internal.Watch {
|
||||||
if err := checkErrors(c, cl, app, conf); err != nil {
|
if err := checkErrors(c, cl, app); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := checkErrors(c, cl, app, conf); err != nil {
|
if err := checkErrors(c, cl, app); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkErrors(c *cli.Context, cl *dockerClient.Client, app config.App, conf *runtime.Config) error {
|
func checkErrors(c *cli.Context, cl *dockerClient.Client, app config.App) error {
|
||||||
recipe, err := recipe.Get(app.Recipe, conf)
|
recipe, err := recipe.Get(app.Recipe, internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/tagcmp"
|
"coopcloud.tech/tagcmp"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -84,8 +83,6 @@ can take some time.
|
|||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
|
||||||
|
|
||||||
appFiles, err := config.LoadAppFiles(listAppServer)
|
appFiles, err := config.LoadAppFiles(listAppServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -113,7 +110,7 @@ can take some time.
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
catl, err = recipe.ReadRecipeCatalogue(conf)
|
catl, err = recipe.ReadRecipeCatalogue(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/service"
|
"coopcloud.tech/abra/pkg/service"
|
||||||
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
@ -79,19 +79,27 @@ var appLogsCommand = cli.Command{
|
|||||||
internal.StdErrOnlyFlag,
|
internal.StdErrOnlyFlag,
|
||||||
internal.SinceLogsFlag,
|
internal.SinceLogsFlag,
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
stackName := app.StackName()
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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
|
logOpts.Since = internal.SinceLogs
|
||||||
|
|
||||||
serviceName := c.Args().Get(1)
|
serviceName := c.Args().Get(1)
|
||||||
|
105
cli/app/new.go
105
cli/app/new.go
@ -2,10 +2,8 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/app"
|
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
@ -13,7 +11,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/jsontable"
|
"coopcloud.tech/abra/pkg/jsontable"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/secret"
|
"coopcloud.tech/abra/pkg/secret"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
@ -54,15 +51,26 @@ var appNewCommand = cli.Command{
|
|||||||
internal.PassFlag,
|
internal.PassFlag,
|
||||||
internal.SecretsFlag,
|
internal.SecretsFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
ArgsUsage: "[<recipe>]",
|
ArgsUsage: "[<recipe>]",
|
||||||
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
recipe := internal.ValidateRecipe(c)
|
||||||
recipe := internal.ValidateRecipeWithPrompt(c, conf)
|
|
||||||
|
|
||||||
if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
|
if !internal.Chaos {
|
||||||
logrus.Fatal(err)
|
if err := recipePkg.EnsureIsClean(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if !internal.Offline {
|
||||||
|
if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := recipePkg.EnsureLatest(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ensureServerFlag(); err != nil {
|
if err := ensureServerFlag(); err != nil {
|
||||||
@ -85,29 +93,43 @@ var appNewCommand = cli.Command{
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := promptForSecrets(internal.Domain); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cl, err := client.New(internal.NewAppServer)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var secrets AppSecrets
|
var secrets AppSecrets
|
||||||
var secretTable *jsontable.JSONTable
|
var secretTable *jsontable.JSONTable
|
||||||
if internal.Secrets {
|
if internal.Secrets {
|
||||||
secrets, err := createSecrets(cl, sanitisedAppName)
|
sampleEnv, err := recipe.SampleEnv()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
composeFiles, err := config.GetComposeFiles(recipe.Name, sampleEnv)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsConfig, err := secret.ReadSecretsConfig(sampleEnv, composeFiles, recipe.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := promptForSecrets(recipe.Name, secretsConfig); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cl, err := client.New(internal.NewAppServer)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets, err = createSecrets(cl, secretsConfig, sanitisedAppName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secretCols := []string{"Name", "Value"}
|
secretCols := []string{"Name", "Value"}
|
||||||
secretTable = formatter.CreateTable(secretCols)
|
secretTable = formatter.CreateTable(secretCols)
|
||||||
for secret := range secrets {
|
for name, val := range secrets {
|
||||||
secretTable.Append([]string{secret, secrets[secret]})
|
secretTable.Append([]string{name, val})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if internal.NewAppServer == "default" {
|
if internal.NewAppServer == "default" {
|
||||||
@ -118,7 +140,6 @@ var appNewCommand = cli.Command{
|
|||||||
table := formatter.CreateTable(tableCol)
|
table := formatter.CreateTable(tableCol)
|
||||||
table.Append([]string{internal.NewAppServer, recipe.Name, internal.Domain})
|
table.Append([]string{internal.NewAppServer, recipe.Name, internal.Domain})
|
||||||
|
|
||||||
fmt.Println("")
|
|
||||||
fmt.Println(fmt.Sprintf("A new %s app has been created! Here is an overview:", recipe.Name))
|
fmt.Println(fmt.Sprintf("A new %s app has been created! Here is an overview:", recipe.Name))
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
table.Render()
|
table.Render()
|
||||||
@ -128,40 +149,31 @@ var appNewCommand = cli.Command{
|
|||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
fmt.Println("You can deploy this app by running the following:")
|
fmt.Println("You can deploy this app by running the following:")
|
||||||
fmt.Println(fmt.Sprintf("\n abra app deploy %s", internal.Domain))
|
fmt.Println(fmt.Sprintf("\n abra app deploy %s", internal.Domain))
|
||||||
fmt.Println("")
|
|
||||||
|
|
||||||
if len(secrets) > 0 {
|
if len(secrets) > 0 {
|
||||||
|
fmt.Println("")
|
||||||
fmt.Println("Here are your generated secrets:")
|
fmt.Println("Here are your generated secrets:")
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
secretTable.Render()
|
secretTable.Render()
|
||||||
fmt.Println("")
|
logrus.Warn("generated secrets are not shown again, please take note of them NOW")
|
||||||
logrus.Warn("generated secrets are not shown again, please take note of them *now*")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppSecrets represents all app secrest
|
// AppSecrets represents all app secrest
|
||||||
type AppSecrets map[string]string
|
type AppSecrets map[string]string
|
||||||
|
|
||||||
// createSecrets creates all secrets for a new app.
|
// createSecrets creates all secrets for a new app.
|
||||||
func createSecrets(cl *dockerClient.Client, sanitisedAppName string) (AppSecrets, error) {
|
func createSecrets(cl *dockerClient.Client, secretsConfig map[string]string, sanitisedAppName string) (AppSecrets, error) {
|
||||||
appEnvPath := path.Join(
|
// NOTE(d1): trim to match app.StackName() implementation
|
||||||
config.ABRA_DIR,
|
if len(sanitisedAppName) > 45 {
|
||||||
"servers",
|
logrus.Debugf("trimming %s to %s to avoid runtime limits", sanitisedAppName, sanitisedAppName[:45])
|
||||||
internal.NewAppServer,
|
sanitisedAppName = sanitisedAppName[:45]
|
||||||
fmt.Sprintf("%s.env", internal.Domain),
|
|
||||||
)
|
|
||||||
|
|
||||||
appEnv, err := config.ReadEnv(appEnvPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
secretEnvVars := secret.ReadSecretEnvVars(appEnv)
|
secrets, err := secret.GenerateSecrets(cl, secretsConfig, sanitisedAppName, internal.NewAppServer)
|
||||||
secrets, err := secret.GenerateSecrets(cl, secretEnvVars, sanitisedAppName, internal.NewAppServer)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -179,6 +191,7 @@ func createSecrets(cl *dockerClient.Client, sanitisedAppName string) (AppSecrets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return secrets, nil
|
return secrets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,15 +215,9 @@ func ensureDomainFlag(recipe recipe.Recipe, server string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// promptForSecrets asks if we should generate secrets for a new app.
|
// promptForSecrets asks if we should generate secrets for a new app.
|
||||||
func promptForSecrets(appName string) error {
|
func promptForSecrets(recipeName string, secretsConfig map[string]string) error {
|
||||||
app, err := app.Get(appName)
|
if len(secretsConfig) == 0 {
|
||||||
if err != nil {
|
logrus.Debugf("%s has no secrets to generate, skipping...", recipeName)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
secretEnvVars := secret.ReadSecretEnvVars(app.Env)
|
|
||||||
if len(secretEnvVars) == 0 {
|
|
||||||
logrus.Debugf("%s has no secrets to generate, skipping...", app.Recipe)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/service"
|
"coopcloud.tech/abra/pkg/service"
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/buger/goterm"
|
"github.com/buger/goterm"
|
||||||
@ -30,13 +29,11 @@ var appPsCommand = cli.Command{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.WatchFlag,
|
internal.WatchFlag,
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
@ -49,8 +48,7 @@ flag.
|
|||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
if !internal.Force && !internal.NoInput {
|
if !internal.Force && !internal.NoInput {
|
||||||
response := false
|
response := false
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
upstream "coopcloud.tech/abra/pkg/upstream/service"
|
upstream "coopcloud.tech/abra/pkg/upstream/service"
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -28,8 +27,7 @@ var appRestartCommand = cli.Command{
|
|||||||
Description: `This command restarts a service within a deployed app.`,
|
Description: `This command restarts a service within a deployed app.`,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
serviceNameShort := c.Args().Get(1)
|
serviceNameShort := c.Args().Get(1)
|
||||||
if serviceNameShort == "" {
|
if serviceNameShort == "" {
|
||||||
@ -42,6 +40,15 @@ var appRestartCommand = cli.Command{
|
|||||||
logrus.Fatal(err)
|
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)
|
serviceName := fmt.Sprintf("%s_%s", app.StackName(), serviceNameShort)
|
||||||
|
|
||||||
logrus.Debugf("attempting to scale %s to 0 (restart logic)", serviceName)
|
logrus.Debugf("attempting to scale %s to 0 (restart logic)", serviceName)
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
containerPkg "coopcloud.tech/abra/pkg/container"
|
containerPkg "coopcloud.tech/abra/pkg/container"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/upstream/container"
|
"coopcloud.tech/abra/pkg/upstream/container"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
@ -36,6 +36,7 @@ var appRestoreCommand = cli.Command{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
@ -49,21 +50,37 @@ restoring the backup.
|
|||||||
Unlike "abra app backup", restore must be run on a per-service basis. You can
|
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
|
not restore all services in one go. Backup files produced by Abra are
|
||||||
compressed archives which use absolute paths. This allows Abra to restore
|
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:
|
Example:
|
||||||
|
|
||||||
abra app restore example.com app ~/.abra/backups/example_com_app_609341138.tar.gz
|
abra app restore example.com app ~/.abra/backups/example_com_app_609341138.tar.gz
|
||||||
`,
|
`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
recipe, err := recipe.Get(app.Recipe, internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !internal.Chaos {
|
||||||
|
if err := recipePkg.EnsureIsClean(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !internal.Offline {
|
||||||
|
if err := recipePkg.EnsureUpToDate(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureLatest(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serviceName := c.Args().Get(1)
|
serviceName := c.Args().Get(1)
|
||||||
if serviceName == "" {
|
if serviceName == "" {
|
||||||
internal.ShowSubcommandHelpAndError(c, errors.New("missing <service>?"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("missing <service>?"))
|
||||||
@ -80,11 +97,6 @@ Example:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recipe, err := recipe.Get(app.Recipe, conf)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreConfigs := make(map[string]restoreConfig)
|
restoreConfigs := make(map[string]restoreConfig)
|
||||||
for _, service := range recipe.Config.Services {
|
for _, service := range recipe.Config.Services {
|
||||||
if restoreEnabled, ok := service.Deploy.Labels["backupbot.restore"]; ok {
|
if restoreEnabled, ok := service.Deploy.Labels["backupbot.restore"]; ok {
|
||||||
@ -114,6 +126,11 @@ Example:
|
|||||||
rsConfig = restoreConfig{}
|
rsConfig = restoreConfig{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cl, err := client.New(app.Server)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := runRestore(cl, app, backupPath, serviceName, rsConfig); err != nil {
|
if err := runRestore(cl, app, backupPath, serviceName, rsConfig); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -170,8 +187,8 @@ func runRestore(cl *dockerClient.Client, app config.App, backupPath, serviceName
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// we use absolute paths so tar knows what to do. it will restore files
|
// NOTE(d1): we use absolute paths so tar knows what to do. it will restore
|
||||||
// according to the paths set in the compresed archive
|
// files according to the paths set in the compressed archive
|
||||||
restorePath := "/"
|
restorePath := "/"
|
||||||
|
|
||||||
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
|
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"coopcloud.tech/tagcmp"
|
"coopcloud.tech/tagcmp"
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ var appRollbackCommand = cli.Command{
|
|||||||
Name: "rollback",
|
Name: "rollback",
|
||||||
Aliases: []string{"rl"},
|
Aliases: []string{"rl"},
|
||||||
Usage: "Roll an app back to a previous version",
|
Usage: "Roll an app back to a previous version",
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain> [<version>]",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
@ -43,23 +42,36 @@ useful if the container runtime has gotten into a weird state.
|
|||||||
This action could be destructive, please ensure you have a copy of your app
|
This action could be destructive, please ensure you have a copy of your app
|
||||||
data beforehand.
|
data beforehand.
|
||||||
|
|
||||||
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
|
including unstaged changes and can be useful for live hacking and testing new
|
||||||
recipes.
|
recipes.
|
||||||
`,
|
`,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
stackName := app.StackName()
|
stackName := app.StackName()
|
||||||
|
|
||||||
|
if err := recipe.EnsureExists(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if !internal.Chaos {
|
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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := recipe.Get(app.Recipe, conf)
|
r, err := recipe.Get(app.Recipe, internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -84,7 +96,7 @@ recipes.
|
|||||||
logrus.Fatalf("%s is not deployed?", app.Name)
|
logrus.Fatalf("%s is not deployed?", app.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
catl, err := recipe.ReadRecipeCatalogue(conf)
|
catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -95,16 +107,41 @@ recipes.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(versions) == 0 && !internal.Chaos {
|
if len(versions) == 0 && !internal.Chaos {
|
||||||
logrus.Fatalf("no published releases for %s in the recipe catalogue?", app.Recipe)
|
logrus.Warn("no published versions in catalogue, trying local recipe repository")
|
||||||
|
recipeVersions, err := recipe.GetRecipeVersions(app.Recipe, internal.Offline)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
for _, recipeVersion := range recipeVersions {
|
||||||
|
for version := range recipeVersion {
|
||||||
|
versions = append(versions, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var availableDowngrades []string
|
var availableDowngrades []string
|
||||||
if deployedVersion == "unknown" {
|
if deployedVersion == "unknown" {
|
||||||
availableDowngrades = versions
|
availableDowngrades = versions
|
||||||
logrus.Warnf("failed to determine version of deployed %s", app.Name)
|
logrus.Warnf("failed to determine deployed version of %s", app.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if deployedVersion != "unknown" && !internal.Chaos {
|
specificVersion := c.Args().Get(1)
|
||||||
|
if specificVersion != "" {
|
||||||
|
parsedDeployedVersion, err := tagcmp.Parse(deployedVersion)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||||
|
logrus.Fatalf("%s is not a downgrade for %s?", deployedVersion, specificVersion)
|
||||||
|
}
|
||||||
|
availableDowngrades = append(availableDowngrades, specificVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if deployedVersion != "unknown" && !internal.Chaos && specificVersion == "" {
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
parsedDeployedVersion, err := tagcmp.Parse(deployedVersion)
|
parsedDeployedVersion, err := tagcmp.Parse(deployedVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -114,12 +151,12 @@ recipes.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if parsedVersion != parsedDeployedVersion && parsedVersion.IsLessThan(parsedDeployedVersion) {
|
if parsedVersion.IsLessThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||||
availableDowngrades = append(availableDowngrades, version)
|
availableDowngrades = append(availableDowngrades, version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(availableDowngrades) == 0 {
|
if len(availableDowngrades) == 0 && !internal.Force {
|
||||||
logrus.Info("no available downgrades, you're on oldest ✌️")
|
logrus.Info("no available downgrades, you're on oldest ✌️")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -127,9 +164,9 @@ recipes.
|
|||||||
|
|
||||||
var chosenDowngrade string
|
var chosenDowngrade string
|
||||||
if len(availableDowngrades) > 0 && !internal.Chaos {
|
if len(availableDowngrades) > 0 && !internal.Chaos {
|
||||||
if internal.Force || internal.NoInput {
|
if internal.Force || internal.NoInput || specificVersion != "" {
|
||||||
chosenDowngrade = availableDowngrades[len(availableDowngrades)-1]
|
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 {
|
} else {
|
||||||
prompt := &survey.Select{
|
prompt := &survey.Select{
|
||||||
Message: fmt.Sprintf("Please select a downgrade (current version: %s):", deployedVersion),
|
Message: fmt.Sprintf("Please select a downgrade (current version: %s):", deployedVersion),
|
||||||
@ -165,7 +202,7 @@ recipes.
|
|||||||
app.Env[k] = v
|
app.Env[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
composeFiles, err := config.GetAppComposeFiles(app.Recipe, app.Env)
|
composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -185,7 +222,8 @@ recipes.
|
|||||||
config.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
config.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
||||||
config.SetUpdateLabel(compose, stackName, app.Env)
|
config.SetUpdateLabel(compose, stackName, app.Env)
|
||||||
|
|
||||||
if err := NewVersionOverview(app, deployedVersion, chosenDowngrade, ""); err != nil {
|
// NOTE(d1): no release notes implemeneted for rolling back
|
||||||
|
if err := internal.NewVersionOverview(app, deployedVersion, chosenDowngrade, ""); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
containerPkg "coopcloud.tech/abra/pkg/container"
|
containerPkg "coopcloud.tech/abra/pkg/container"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/upstream/container"
|
"coopcloud.tech/abra/pkg/upstream/container"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
@ -38,15 +37,13 @@ var appRunCommand = cli.Command{
|
|||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
noTTYFlag,
|
noTTYFlag,
|
||||||
userFlag,
|
userFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
ArgsUsage: "<domain> <service> <args>...",
|
ArgsUsage: "<domain> <service> <args>...",
|
||||||
Usage: "Run a command in a service container",
|
Usage: "Run a command in a service container",
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
if len(c.Args()) < 2 {
|
if len(c.Args()) < 2 {
|
||||||
internal.ShowSubcommandHelpAndError(c, errors.New("no <service> provided?"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("no <service> provided?"))
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/secret"
|
"coopcloud.tech/abra/pkg/secret"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
@ -43,19 +43,35 @@ var appSecretGenerateCommand = cli.Command{
|
|||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
allSecretsFlag,
|
allSecretsFlag,
|
||||||
internal.PassFlag,
|
internal.PassFlag,
|
||||||
|
internal.MachineReadableFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
if err := recipe.EnsureExists(app.Recipe); err != nil {
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(c.Args()) == 1 && !allSecrets {
|
if len(c.Args()) == 1 && !allSecrets {
|
||||||
err := errors.New("missing arguments <secret>/<version> or '--all'")
|
err := errors.New("missing arguments <secret>/<version> or '--all'")
|
||||||
internal.ShowSubcommandHelpAndError(c, err)
|
internal.ShowSubcommandHelpAndError(c, err)
|
||||||
@ -66,18 +82,26 @@ var appSecretGenerateCommand = cli.Command{
|
|||||||
internal.ShowSubcommandHelpAndError(c, err)
|
internal.ShowSubcommandHelpAndError(c, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsConfig, err := secret.ReadSecretsConfig(app.Env, composeFiles, app.Recipe)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
secretsToCreate := make(map[string]string)
|
secretsToCreate := make(map[string]string)
|
||||||
secretEnvVars := secret.ReadSecretEnvVars(app.Env)
|
|
||||||
if allSecrets {
|
if allSecrets {
|
||||||
secretsToCreate = secretEnvVars
|
secretsToCreate = secretsConfig
|
||||||
} else {
|
} else {
|
||||||
secretName := c.Args().Get(1)
|
secretName := c.Args().Get(1)
|
||||||
secretVersion := c.Args().Get(2)
|
secretVersion := c.Args().Get(2)
|
||||||
matches := false
|
matches := false
|
||||||
for sec := range secretEnvVars {
|
for name := range secretsConfig {
|
||||||
parsed := secret.ParseSecretEnvVarName(sec)
|
if secretName == name {
|
||||||
if secretName == parsed {
|
secretsToCreate[name] = secretVersion
|
||||||
secretsToCreate[sec] = secretVersion
|
|
||||||
matches = true
|
matches = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,6 +111,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)
|
secretVals, err := secret.GenerateSecrets(cl, secretsToCreate, app.StackName(), app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -110,8 +139,13 @@ var appSecretGenerateCommand = cli.Command{
|
|||||||
for name, val := range secretVals {
|
for name, val := range secretVals {
|
||||||
table.Append([]string{name, val})
|
table.Append([]string{name, val})
|
||||||
}
|
}
|
||||||
table.Render()
|
|
||||||
logrus.Warn("generated secrets are not shown again, please take note of them *now*")
|
if internal.MachineReadable {
|
||||||
|
table.JSONRender()
|
||||||
|
} else {
|
||||||
|
table.Render()
|
||||||
|
}
|
||||||
|
logrus.Warn("generated secrets are not shown again, please take note of them NOW")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@ -124,7 +158,6 @@ var appSecretInsertCommand = cli.Command{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.PassFlag,
|
internal.PassFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
ArgsUsage: "<domain> <secret-name> <version> <data>",
|
ArgsUsage: "<domain> <secret-name> <version> <data>",
|
||||||
@ -142,18 +175,17 @@ Example:
|
|||||||
|
|
||||||
`,
|
`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
if len(c.Args()) != 4 {
|
||||||
|
internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments?"))
|
||||||
|
}
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Args()) != 4 {
|
|
||||||
internal.ShowSubcommandHelpAndError(c, errors.New("missing arguments?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
name := c.Args().Get(1)
|
name := c.Args().Get(1)
|
||||||
version := c.Args().Get(2)
|
version := c.Args().Get(2)
|
||||||
data := c.Args().Get(3)
|
data := c.Args().Get(3)
|
||||||
@ -204,6 +236,7 @@ var appSecretRmCommand = cli.Command{
|
|||||||
rmAllSecretsFlag,
|
rmAllSecretsFlag,
|
||||||
internal.PassRemoveFlag,
|
internal.PassRemoveFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
ArgsUsage: "<domain> [<secret-name>]",
|
ArgsUsage: "<domain> [<secret-name>]",
|
||||||
@ -216,9 +249,37 @@ Example:
|
|||||||
abra app secret remove myapp db_pass
|
abra app secret remove myapp db_pass
|
||||||
`,
|
`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
secrets := secret.ReadSecretEnvVars(app.Env)
|
if err := recipe.EnsureExists(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsConfig, err := secret.ReadSecretsConfig(app.Env, composeFiles, app.Recipe)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if c.Args().Get(1) != "" && rmAllSecrets {
|
if c.Args().Get(1) != "" && rmAllSecrets {
|
||||||
internal.ShowSubcommandHelpAndError(c, errors.New("cannot use '<secret-name>' and '--all' together"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("cannot use '<secret-name>' and '--all' together"))
|
||||||
@ -250,15 +311,13 @@ Example:
|
|||||||
|
|
||||||
match := false
|
match := false
|
||||||
secretToRm := c.Args().Get(1)
|
secretToRm := c.Args().Get(1)
|
||||||
for sec := range secrets {
|
for secretName, secretValue := range secretsConfig {
|
||||||
secretName := secret.ParseSecretEnvVarName(sec)
|
val, err := secret.ParseSecretValue(secretValue)
|
||||||
|
|
||||||
secVal, err := secret.ParseSecretEnvVarValue(secrets[sec])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, secVal.Version)
|
secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, val.Version)
|
||||||
if _, ok := remoteSecretNames[secretRemoteName]; ok {
|
if _, ok := remoteSecretNames[secretRemoteName]; ok {
|
||||||
if secretToRm != "" {
|
if secretToRm != "" {
|
||||||
if secretName == secretToRm {
|
if secretName == secretToRm {
|
||||||
@ -296,13 +355,43 @@ var appSecretLsCommand = cli.Command{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Usage: "List all secrets",
|
Usage: "List all secrets",
|
||||||
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
secrets := secret.ReadSecretEnvVars(app.Env)
|
if err := recipe.EnsureExists(app.Recipe); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsConfig, err := secret.ReadSecretsConfig(app.Env, composeFiles, app.Recipe)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"}
|
tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"}
|
||||||
table := formatter.CreateTable(tableCol)
|
table := formatter.CreateTable(tableCol)
|
||||||
@ -327,18 +416,17 @@ var appSecretLsCommand = cli.Command{
|
|||||||
remoteSecretNames[cont.Spec.Annotations.Name] = true
|
remoteSecretNames[cont.Spec.Annotations.Name] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for sec := range secrets {
|
for secretName, secretValue := range secretsConfig {
|
||||||
createdRemote := false
|
createdRemote := false
|
||||||
secretName := secret.ParseSecretEnvVarName(sec)
|
val, err := secret.ParseSecretValue(secretValue)
|
||||||
secVal, err := secret.ParseSecretEnvVarValue(secrets[sec])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, secVal.Version)
|
secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, val.Version)
|
||||||
if _, ok := remoteSecretNames[secretRemoteName]; ok {
|
if _, ok := remoteSecretNames[secretRemoteName]; ok {
|
||||||
createdRemote = true
|
createdRemote = true
|
||||||
}
|
}
|
||||||
tableRow := []string{secretName, secVal.Version, secretRemoteName, strconv.FormatBool(createdRemote)}
|
tableRow := []string{secretName, val.Version, secretRemoteName, strconv.FormatBool(createdRemote)}
|
||||||
table.Append(tableRow)
|
table.Append(tableRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +438,6 @@ var appSecretLsCommand = cli.Command{
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var appSecretCommand = cli.Command{
|
var appSecretCommand = cli.Command{
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/service"
|
"coopcloud.tech/abra/pkg/service"
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
@ -24,13 +23,11 @@ var appServicesCommand = cli.Command{
|
|||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
@ -87,7 +86,6 @@ var appUndeployCommand = cli.Command{
|
|||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
pruneFlag,
|
pruneFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Usage: "Undeploy an app",
|
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.
|
Passing "-p/--prune" does not remove those volumes.
|
||||||
`,
|
`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
stackName := app.StackName()
|
stackName := app.StackName()
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
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)
|
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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"coopcloud.tech/tagcmp"
|
"coopcloud.tech/tagcmp"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
@ -22,7 +22,7 @@ var appUpgradeCommand = cli.Command{
|
|||||||
Name: "upgrade",
|
Name: "upgrade",
|
||||||
Aliases: []string{"up"},
|
Aliases: []string{"up"},
|
||||||
Usage: "Upgrade an app",
|
Usage: "Upgrade an app",
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain> [<version>]",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
@ -47,37 +47,47 @@ useful if the container runtime has gotten into a weird state.
|
|||||||
This action could be destructive, please ensure you have a copy of your app
|
This action could be destructive, please ensure you have a copy of your app
|
||||||
data beforehand.
|
data beforehand.
|
||||||
|
|
||||||
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
|
including unstaged changes and can be useful for live hacking and testing new
|
||||||
recipes.
|
recipes.
|
||||||
`,
|
`,
|
||||||
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
stackName := app.StackName()
|
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)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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)
|
isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -87,37 +97,63 @@ recipes.
|
|||||||
logrus.Fatalf("%s is not deployed?", app.Name)
|
logrus.Fatalf("%s is not deployed?", app.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
catl, err := recipe.ReadRecipeCatalogue(conf)
|
catl, err := recipePkg.ReadRecipeCatalogue(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
versions, err := recipe.GetRecipeCatalogueVersions(app.Recipe, catl)
|
versions, err := recipePkg.GetRecipeCatalogueVersions(app.Recipe, catl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(versions) == 0 && !internal.Chaos {
|
if len(versions) == 0 && !internal.Chaos {
|
||||||
logrus.Fatalf("no published releases for %s in the recipe catalogue?", app.Recipe)
|
logrus.Warn("no published versions in catalogue, trying local recipe repository")
|
||||||
|
recipeVersions, err := recipePkg.GetRecipeVersions(app.Recipe, internal.Offline)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
|
for _, recipeVersion := range recipeVersions {
|
||||||
|
for version := range recipeVersion {
|
||||||
|
versions = append(versions, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var availableUpgrades []string
|
var availableUpgrades []string
|
||||||
if deployedVersion == "unknown" {
|
if deployedVersion == "unknown" {
|
||||||
availableUpgrades = versions
|
availableUpgrades = versions
|
||||||
logrus.Warnf("failed to determine version of deployed %s", app.Name)
|
logrus.Warnf("failed to determine deployed version of %s", app.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if deployedVersion != "unknown" && !internal.Chaos {
|
specificVersion := c.Args().Get(1)
|
||||||
|
if specificVersion != "" {
|
||||||
|
parsedDeployedVersion, err := tagcmp.Parse(deployedVersion)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||||
|
logrus.Fatalf("%s is not an upgrade for %s?", deployedVersion, specificVersion)
|
||||||
|
}
|
||||||
|
availableUpgrades = append(availableUpgrades, specificVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedDeployedVersion, err := tagcmp.Parse(deployedVersion)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if deployedVersion != "unknown" && !internal.Chaos && specificVersion == "" {
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
parsedDeployedVersion, err := tagcmp.Parse(deployedVersion)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
parsedVersion, err := tagcmp.Parse(version)
|
parsedVersion, err := tagcmp.Parse(version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) {
|
if parsedVersion.IsGreaterThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||||
availableUpgrades = append(availableUpgrades, version)
|
availableUpgrades = append(availableUpgrades, version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +166,7 @@ recipes.
|
|||||||
|
|
||||||
var chosenUpgrade string
|
var chosenUpgrade string
|
||||||
if len(availableUpgrades) > 0 && !internal.Chaos {
|
if len(availableUpgrades) > 0 && !internal.Chaos {
|
||||||
if internal.Force || internal.NoInput {
|
if internal.Force || internal.NoInput || specificVersion != "" {
|
||||||
chosenUpgrade = availableUpgrades[len(availableUpgrades)-1]
|
chosenUpgrade = availableUpgrades[len(availableUpgrades)-1]
|
||||||
logrus.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
|
logrus.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
|
||||||
} else {
|
} else {
|
||||||
@ -152,13 +188,30 @@ recipes.
|
|||||||
// if release notes written after git tag published, read them before we
|
// 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
|
// 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
|
// when we obviously will forget to write release notes before publishing
|
||||||
releaseNotes, err := GetReleaseNotes(app.Recipe, chosenUpgrade)
|
var releaseNotes string
|
||||||
if err != nil {
|
for _, version := range versions {
|
||||||
return err
|
parsedVersion, err := tagcmp.Parse(version)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(parsedVersion.Equals(parsedDeployedVersion)) && parsedVersion.IsLessThan(parsedChosenUpgrade) {
|
||||||
|
note, err := internal.GetReleaseNotes(app.Recipe, version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if note != "" {
|
||||||
|
releaseNotes += fmt.Sprintf("%s\n", note)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !internal.Chaos {
|
if !internal.Chaos {
|
||||||
if err := recipe.EnsureVersion(app.Recipe, chosenUpgrade); err != nil {
|
if err := recipePkg.EnsureVersion(app.Recipe, chosenUpgrade); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +219,7 @@ recipes.
|
|||||||
if internal.Chaos {
|
if internal.Chaos {
|
||||||
logrus.Warn("chaos mode engaged")
|
logrus.Warn("chaos mode engaged")
|
||||||
var err error
|
var err error
|
||||||
chosenUpgrade, err = recipe.ChaosVersion(app.Recipe)
|
chosenUpgrade, err = recipePkg.ChaosVersion(app.Recipe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -181,7 +234,7 @@ recipes.
|
|||||||
app.Env[k] = v
|
app.Env[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
composeFiles, err := config.GetAppComposeFiles(app.Recipe, app.Env)
|
composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -201,7 +254,7 @@ recipes.
|
|||||||
config.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
|
config.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
|
||||||
config.SetUpdateLabel(compose, stackName, app.Env)
|
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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,12 +271,11 @@ recipes.
|
|||||||
postDeployCmds, ok := app.Env["POST_UPGRADE_CMDS"]
|
postDeployCmds, ok := app.Env["POST_UPGRADE_CMDS"]
|
||||||
if ok && !internal.DontWaitConverge {
|
if ok && !internal.DontWaitConverge {
|
||||||
logrus.Debugf("run the following post-deploy commands: %s", postDeployCmds)
|
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)
|
logrus.Fatalf("attempting to run post deploy commands, saw: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,30 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/upstream/stack"
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func sortServiceByName(versions [][]string) func(i, j int) bool {
|
||||||
|
return func(i, j int) bool {
|
||||||
|
// NOTE(d1): corresponds to the `tableCol` definition below
|
||||||
|
if versions[i][1] == "app" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return versions[i][1] < versions[j][1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getImagePath returns the image name
|
// getImagePath returns the image name
|
||||||
func getImagePath(image string) (string, error) {
|
func getImagePath(image string) (string, error) {
|
||||||
img, err := reference.ParseNormalizedNamed(image)
|
img, err := reference.ParseNormalizedNamed(image)
|
||||||
@ -40,16 +51,11 @@ var appVersionCommand = cli.Command{
|
|||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Usage: "Show app versions",
|
Usage: "Show version info of a deployed app",
|
||||||
Description: `
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
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.
|
|
||||||
`,
|
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
stackName := app.StackName()
|
stackName := app.StackName()
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
@ -64,15 +70,15 @@ version.
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if deployedVersion == "unknown" {
|
|
||||||
logrus.Fatalf("failed to determine version of deployed %s", app.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isDeployed {
|
if !isDeployed {
|
||||||
logrus.Fatalf("%s is not deployed?", app.Name)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
recipeMeta, err := recipe.GetRecipeMeta(app.Recipe, internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -88,17 +94,24 @@ version.
|
|||||||
logrus.Fatalf("could not retrieve deployed version (%s) from recipe catalogue?", deployedVersion)
|
logrus.Fatalf("could not retrieve deployed version (%s) from recipe catalogue?", deployedVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableCol := []string{"version", "service", "image"}
|
tableCol := []string{"version", "service", "image", "tag"}
|
||||||
table := formatter.CreateTable(tableCol)
|
table := formatter.CreateTable(tableCol)
|
||||||
table.SetAutoMergeCellsByColumnIndex([]int{0})
|
|
||||||
|
|
||||||
|
var versions [][]string
|
||||||
for serviceName, versionMeta := range versionsMeta {
|
for serviceName, versionMeta := range versionsMeta {
|
||||||
table.Append([]string{deployedVersion, serviceName, versionMeta.Image})
|
versions = append(versions, []string{deployedVersion, serviceName, versionMeta.Image, versionMeta.Tag})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort.Slice(versions, sortServiceByName(versions))
|
||||||
|
|
||||||
|
for _, version := range versions {
|
||||||
|
table.Append(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.SetAutoMergeCellsByColumnIndex([]int{0})
|
||||||
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||||
table.Render()
|
table.Render()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -20,14 +20,12 @@ var appVolumeListCommand = cli.Command{
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Usage: "List volumes associated with an app",
|
Usage: "List volumes associated with an app",
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -83,18 +81,26 @@ Passing "--force/-f" will select all volumes for removal. Be careful.
|
|||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
internal.ForceFlag,
|
internal.ForceFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
|
BashComplete: autocomplete.AppNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
app := internal.ValidateApp(c)
|
||||||
app := internal.ValidateApp(c, conf)
|
|
||||||
|
|
||||||
cl, err := client.New(app.Server)
|
cl, err := client.New(app.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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)
|
filters, err := app.Filters(false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -124,16 +130,19 @@ Passing "--force/-f" will select all volumes for removal. Be careful.
|
|||||||
volumesToRemove = volumeNames
|
volumesToRemove = volumeNames
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.RemoveVolumes(cl, context.Background(), app.Server, volumesToRemove, internal.Force)
|
if len(volumesToRemove) > 0 {
|
||||||
if err != nil {
|
err = client.RemoveVolumes(cl, context.Background(), app.Server, volumesToRemove, internal.Force)
|
||||||
logrus.Fatal(err)
|
if err != nil {
|
||||||
}
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
logrus.Info("volumes removed successfully")
|
logrus.Info("volumes removed successfully")
|
||||||
|
} else {
|
||||||
|
logrus.Info("no volumes removed")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.AppNameComplete,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var appVolumeCommand = cli.Command{
|
var appVolumeCommand = cli.Command{
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -29,6 +28,7 @@ var catalogueGenerateCommand = cli.Command{
|
|||||||
internal.PublishFlag,
|
internal.PublishFlag,
|
||||||
internal.DryFlag,
|
internal.DryFlag,
|
||||||
internal.SkipUpdatesFlag,
|
internal.SkipUpdatesFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
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
|
that you have permission to git push to these repositories and have your SSH
|
||||||
keys configured on your account.
|
keys configured on your account.
|
||||||
`,
|
`,
|
||||||
ArgsUsage: "[<recipe>]",
|
ArgsUsage: "[<recipe>]",
|
||||||
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
|
||||||
recipeName := c.Args().First()
|
recipeName := c.Args().First()
|
||||||
|
|
||||||
if recipeName != "" {
|
if recipeName != "" {
|
||||||
internal.ValidateRecipe(c, conf)
|
internal.ValidateRecipe(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := catalogue.EnsureUpToDate(conf); err != nil {
|
if !internal.Chaos {
|
||||||
logrus.Fatal(err)
|
if err := catalogue.EnsureIsClean(); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repos, err := recipe.ReadReposMetadata(conf)
|
repos, err := recipe.ReadReposMetadata()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -83,7 +85,7 @@ keys configured on your account.
|
|||||||
|
|
||||||
if !internal.SkipUpdates {
|
if !internal.SkipUpdates {
|
||||||
logrus.Warn(logMsg)
|
logrus.Warn(logMsg)
|
||||||
if err := recipe.UpdateRepositories(repos, recipeName, conf); err != nil {
|
if err := recipe.UpdateRepositories(repos, recipeName); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +103,7 @@ keys configured on your account.
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
versions, err := recipe.GetRecipeVersions(recipeMeta.Name, conf)
|
versions, err := recipe.GetRecipeVersions(recipeMeta.Name, internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warn(err)
|
logrus.Warn(err)
|
||||||
}
|
}
|
||||||
@ -137,7 +139,7 @@ keys configured on your account.
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
catlFS, err := recipe.ReadRecipeCatalogue(conf)
|
catlFS, err := recipe.ReadRecipeCatalogue(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -211,7 +213,6 @@ keys configured on your account.
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CatalogueCommand defines the `abra catalogue` command and sub-commands.
|
// CatalogueCommand defines the `abra catalogue` command and sub-commands.
|
||||||
|
19
cli/cli.go
19
cli/cli.go
@ -12,10 +12,10 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/catalogue"
|
"coopcloud.tech/abra/cli/catalogue"
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/cli/recipe"
|
"coopcloud.tech/abra/cli/recipe"
|
||||||
"coopcloud.tech/abra/cli/record"
|
|
||||||
"coopcloud.tech/abra/cli/server"
|
"coopcloud.tech/abra/cli/server"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
"coopcloud.tech/abra/pkg/git"
|
||||||
"coopcloud.tech/abra/pkg/web"
|
"coopcloud.tech/abra/pkg/web"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -158,7 +158,6 @@ func newAbraApp(version, commit string) *cli.App {
|
|||||||
server.ServerCommand,
|
server.ServerCommand,
|
||||||
recipe.RecipeCommand,
|
recipe.RecipeCommand,
|
||||||
catalogue.CatalogueCommand,
|
catalogue.CatalogueCommand,
|
||||||
record.RecordCommand,
|
|
||||||
UpgradeCommand,
|
UpgradeCommand,
|
||||||
AutoCompleteCommand,
|
AutoCompleteCommand,
|
||||||
},
|
},
|
||||||
@ -170,10 +169,10 @@ func newAbraApp(version, commit string) *cli.App {
|
|||||||
app.Before = func(c *cli.Context) error {
|
app.Before = func(c *cli.Context) error {
|
||||||
paths := []string{
|
paths := []string{
|
||||||
config.ABRA_DIR,
|
config.ABRA_DIR,
|
||||||
path.Join(config.SERVERS_DIR),
|
config.SERVERS_DIR,
|
||||||
path.Join(config.RECIPES_DIR),
|
config.RECIPES_DIR,
|
||||||
path.Join(config.VENDOR_DIR),
|
config.VENDOR_DIR,
|
||||||
path.Join(config.BACKUP_DIR),
|
config.BACKUP_DIR,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
@ -185,6 +184,14 @@ func newAbraApp(version, commit string) *cli.App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(config.CATALOGUE_DIR); os.IsNotExist(err) {
|
||||||
|
url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME)
|
||||||
|
logrus.Warnf("local recipe catalogue is missing, retrieving now")
|
||||||
|
if err := git.Clone(config.CATALOGUE_DIR, url); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logrus.Debugf("abra version %s, commit %s", version, commit)
|
logrus.Debugf("abra version %s, commit %s", version, commit)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -68,17 +68,6 @@ var TtyFlag = &cli.BoolFlag{
|
|||||||
Destination: &Tty,
|
Destination: &Tty,
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSProvider specifies a DNS provider.
|
|
||||||
var DNSProvider string
|
|
||||||
|
|
||||||
// DNSProviderFlag selects a DNS provider.
|
|
||||||
var DNSProviderFlag = &cli.StringFlag{
|
|
||||||
Name: "provider, p",
|
|
||||||
Value: "",
|
|
||||||
Usage: "DNS provider",
|
|
||||||
Destination: &DNSProvider,
|
|
||||||
}
|
|
||||||
|
|
||||||
var NoInput bool
|
var NoInput bool
|
||||||
var NoInputFlag = &cli.BoolFlag{
|
var NoInputFlag = &cli.BoolFlag{
|
||||||
Name: "no-input, n",
|
Name: "no-input, n",
|
||||||
@ -86,163 +75,6 @@ var NoInputFlag = &cli.BoolFlag{
|
|||||||
Destination: &NoInput,
|
Destination: &NoInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
var DNSType string
|
|
||||||
|
|
||||||
var DNSTypeFlag = &cli.StringFlag{
|
|
||||||
Name: "record-type, rt",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Domain name record type (e.g. A)",
|
|
||||||
Destination: &DNSType,
|
|
||||||
}
|
|
||||||
|
|
||||||
var DNSName string
|
|
||||||
|
|
||||||
var DNSNameFlag = &cli.StringFlag{
|
|
||||||
Name: "record-name, rn",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Domain name record name (e.g. mysubdomain)",
|
|
||||||
Destination: &DNSName,
|
|
||||||
}
|
|
||||||
|
|
||||||
var DNSValue string
|
|
||||||
|
|
||||||
var DNSValueFlag = &cli.StringFlag{
|
|
||||||
Name: "record-value, rv",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Domain name record value (e.g. 192.168.1.1)",
|
|
||||||
Destination: &DNSValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
var DNSTTL string
|
|
||||||
var DNSTTLFlag = &cli.StringFlag{
|
|
||||||
Name: "record-ttl, rl",
|
|
||||||
Value: "600s",
|
|
||||||
Usage: "Domain name TTL value (seconds)",
|
|
||||||
Destination: &DNSTTL,
|
|
||||||
}
|
|
||||||
|
|
||||||
var DNSPriority int
|
|
||||||
|
|
||||||
var DNSPriorityFlag = &cli.IntFlag{
|
|
||||||
Name: "record-priority, rp",
|
|
||||||
Value: 10,
|
|
||||||
Usage: "Domain name priority value",
|
|
||||||
Destination: &DNSPriority,
|
|
||||||
}
|
|
||||||
|
|
||||||
var ServerProvider string
|
|
||||||
|
|
||||||
var ServerProviderFlag = &cli.StringFlag{
|
|
||||||
Name: "provider, p",
|
|
||||||
Usage: "3rd party server provider",
|
|
||||||
Destination: &ServerProvider,
|
|
||||||
}
|
|
||||||
|
|
||||||
var CapsulInstanceURL string
|
|
||||||
|
|
||||||
var CapsulInstanceURLFlag = &cli.StringFlag{
|
|
||||||
Name: "capsul-url, cu",
|
|
||||||
Value: "yolo.servers.coop",
|
|
||||||
Usage: "capsul instance URL",
|
|
||||||
Destination: &CapsulInstanceURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
var CapsulName string
|
|
||||||
|
|
||||||
var CapsulNameFlag = &cli.StringFlag{
|
|
||||||
Name: "capsul-name, cn",
|
|
||||||
Value: "",
|
|
||||||
Usage: "capsul name",
|
|
||||||
Destination: &CapsulName,
|
|
||||||
}
|
|
||||||
|
|
||||||
var CapsulType string
|
|
||||||
|
|
||||||
var CapsulTypeFlag = &cli.StringFlag{
|
|
||||||
Name: "capsul-type, ct",
|
|
||||||
Value: "f1-xs",
|
|
||||||
Usage: "capsul type",
|
|
||||||
Destination: &CapsulType,
|
|
||||||
}
|
|
||||||
|
|
||||||
var CapsulImage string
|
|
||||||
|
|
||||||
var CapsulImageFlag = &cli.StringFlag{
|
|
||||||
Name: "capsul-image, ci",
|
|
||||||
Value: "debian10",
|
|
||||||
Usage: "capsul image",
|
|
||||||
Destination: &CapsulImage,
|
|
||||||
}
|
|
||||||
|
|
||||||
var CapsulSSHKeys cli.StringSlice
|
|
||||||
var CapsulSSHKeysFlag = &cli.StringSliceFlag{
|
|
||||||
Name: "capsul-ssh-keys, cs",
|
|
||||||
Usage: "capsul SSH key",
|
|
||||||
Value: &CapsulSSHKeys,
|
|
||||||
}
|
|
||||||
|
|
||||||
var CapsulAPIToken string
|
|
||||||
|
|
||||||
var CapsulAPITokenFlag = &cli.StringFlag{
|
|
||||||
Name: "capsul-token, ca",
|
|
||||||
Usage: "capsul API token",
|
|
||||||
EnvVar: "CAPSUL_TOKEN",
|
|
||||||
Destination: &CapsulAPIToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
var HetznerCloudName string
|
|
||||||
|
|
||||||
var HetznerCloudNameFlag = &cli.StringFlag{
|
|
||||||
Name: "hetzner-name, hn",
|
|
||||||
Value: "",
|
|
||||||
Usage: "hetzner cloud name",
|
|
||||||
Destination: &HetznerCloudName,
|
|
||||||
}
|
|
||||||
|
|
||||||
var HetznerCloudType string
|
|
||||||
|
|
||||||
var HetznerCloudTypeFlag = &cli.StringFlag{
|
|
||||||
Name: "hetzner-type, ht",
|
|
||||||
Usage: "hetzner cloud type",
|
|
||||||
Destination: &HetznerCloudType,
|
|
||||||
Value: "cx11",
|
|
||||||
}
|
|
||||||
|
|
||||||
var HetznerCloudImage string
|
|
||||||
|
|
||||||
var HetznerCloudImageFlag = &cli.StringFlag{
|
|
||||||
Name: "hetzner-image, hi",
|
|
||||||
Usage: "hetzner cloud image",
|
|
||||||
Value: "debian-10",
|
|
||||||
Destination: &HetznerCloudImage,
|
|
||||||
}
|
|
||||||
|
|
||||||
var HetznerCloudSSHKeys cli.StringSlice
|
|
||||||
|
|
||||||
var HetznerCloudSSHKeysFlag = &cli.StringSliceFlag{
|
|
||||||
Name: "hetzner-ssh-keys, hs",
|
|
||||||
Usage: "hetzner cloud SSH keys (e.g. me@foo.com)",
|
|
||||||
Value: &HetznerCloudSSHKeys,
|
|
||||||
}
|
|
||||||
|
|
||||||
var HetznerCloudLocation string
|
|
||||||
|
|
||||||
var HetznerCloudLocationFlag = &cli.StringFlag{
|
|
||||||
Name: "hetzner-location, hl",
|
|
||||||
Usage: "hetzner cloud server location",
|
|
||||||
Value: "hel1",
|
|
||||||
Destination: &HetznerCloudLocation,
|
|
||||||
}
|
|
||||||
|
|
||||||
var HetznerCloudAPIToken string
|
|
||||||
|
|
||||||
var HetznerCloudAPITokenFlag = &cli.StringFlag{
|
|
||||||
Name: "hetzner-token, ha",
|
|
||||||
Usage: "hetzner cloud API token",
|
|
||||||
EnvVar: "HCLOUD_TOKEN",
|
|
||||||
Destination: &HetznerCloudAPIToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug stores the variable from DebugFlag.
|
// Debug stores the variable from DebugFlag.
|
||||||
var Debug bool
|
var Debug bool
|
||||||
|
|
||||||
|
173
cli/internal/deploy.go
Normal file
173
cli/internal/deploy.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
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 != "" && newVersion != "" {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Print(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
|
||||||
|
}
|
||||||
|
withTitle := fmt.Sprintf("%s release notes:\n%s", version, string(releaseNotes))
|
||||||
|
return withTitle, 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
|
||||||
|
}
|
@ -2,60 +2,24 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/app"
|
"coopcloud.tech/abra/pkg/app"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateRecipe ensures the recipe arg is valid.
|
// ValidateRecipe ensures the recipe arg is valid.
|
||||||
func ValidateRecipe(c *cli.Context, conf *runtime.Config) recipe.Recipe {
|
func ValidateRecipe(c *cli.Context) 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 {
|
|
||||||
recipeName := c.Args().First()
|
recipeName := c.Args().First()
|
||||||
|
|
||||||
if recipeName == "" && !NoInput {
|
if recipeName == "" && !NoInput {
|
||||||
var recipes []string
|
var recipes []string
|
||||||
|
|
||||||
catl, err := recipe.ReadRecipeCatalogue(conf)
|
catl, err := recipe.ReadRecipeCatalogue(Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -93,13 +57,19 @@ func ValidateRecipeWithPrompt(c *cli.Context, conf *runtime.Config) recipe.Recip
|
|||||||
ShowSubcommandHelpAndError(c, errors.New("no recipe name provided"))
|
ShowSubcommandHelpAndError(c, errors.New("no recipe name provided"))
|
||||||
}
|
}
|
||||||
|
|
||||||
chosenRecipe, err := recipe.Get(recipeName, conf)
|
chosenRecipe, err := recipe.Get(recipeName, Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
if c.Command.Name == "generate" {
|
||||||
}
|
if strings.Contains(err.Error(), "missing a compose") {
|
||||||
|
logrus.Fatal(err)
|
||||||
if err := recipe.EnsureLatest(recipeName, conf); err != nil {
|
}
|
||||||
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)
|
logrus.Debugf("validated %s as recipe argument", recipeName)
|
||||||
@ -108,7 +78,7 @@ func ValidateRecipeWithPrompt(c *cli.Context, conf *runtime.Config) recipe.Recip
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateApp ensures the app name arg is valid.
|
// 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()
|
appName := c.Args().First()
|
||||||
|
|
||||||
if appName == "" {
|
if appName == "" {
|
||||||
@ -120,10 +90,6 @@ func ValidateApp(c *cli.Context, conf *runtime.Config) config.App {
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := recipe.EnsureExists(app.Recipe, conf); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("validated %s as app argument", appName)
|
logrus.Debugf("validated %s as app argument", appName)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
@ -192,309 +158,15 @@ func ValidateServer(c *cli.Context) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matched {
|
|
||||||
ShowSubcommandHelpAndError(c, errors.New("server doesn't exist?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverName == "" {
|
if serverName == "" {
|
||||||
ShowSubcommandHelpAndError(c, errors.New("no server provided"))
|
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)
|
logrus.Debugf("validated %s as server argument", serverName)
|
||||||
|
|
||||||
return serverName
|
return serverName
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureDNSProvider ensures a DNS provider is chosen.
|
|
||||||
func EnsureDNSProvider() error {
|
|
||||||
if DNSProvider == "" && !NoInput {
|
|
||||||
prompt := &survey.Select{
|
|
||||||
Message: "Select DNS provider",
|
|
||||||
Options: []string{"gandi"},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &DNSProvider); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if DNSProvider == "" {
|
|
||||||
return fmt.Errorf("missing DNS provider?")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureDNSTypeFlag ensures a DNS type flag is present.
|
|
||||||
func EnsureDNSTypeFlag(c *cli.Context) error {
|
|
||||||
if DNSType == "" && !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "Specify DNS record type",
|
|
||||||
Default: "A",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &DNSType); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if DNSType == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, errors.New("no record type provided"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureDNSNameFlag ensures a DNS name flag is present.
|
|
||||||
func EnsureDNSNameFlag(c *cli.Context) error {
|
|
||||||
if DNSName == "" && !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "Specify DNS record name",
|
|
||||||
Default: "mysubdomain",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &DNSName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if DNSName == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, errors.New("no record name provided"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureDNSValueFlag ensures a DNS value flag is present.
|
|
||||||
func EnsureDNSValueFlag(c *cli.Context) error {
|
|
||||||
if DNSValue == "" && !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "Specify DNS record value",
|
|
||||||
Default: "192.168.1.2",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &DNSValue); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if DNSValue == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, errors.New("no record value provided"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureZoneArgument ensures a zone argument is present.
|
|
||||||
func EnsureZoneArgument(c *cli.Context) (string, error) {
|
|
||||||
zone := c.Args().First()
|
|
||||||
|
|
||||||
if zone == "" && !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "Specify a domain name zone",
|
|
||||||
Default: "example.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &zone); err != nil {
|
|
||||||
return zone, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if zone == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, errors.New("no zone value provided"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return zone, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureServerProvider ensures a 3rd party server provider is chosen.
|
|
||||||
func EnsureServerProvider() error {
|
|
||||||
if ServerProvider == "" && !NoInput {
|
|
||||||
prompt := &survey.Select{
|
|
||||||
Message: "Select server provider",
|
|
||||||
Options: []string{"capsul", "hetzner-cloud"},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &ServerProvider); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ServerProvider == "" {
|
|
||||||
return fmt.Errorf("missing server provider?")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureNewCapsulVPSFlags ensure all flags are present.
|
|
||||||
func EnsureNewCapsulVPSFlags(c *cli.Context) error {
|
|
||||||
if CapsulName == "" && !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify capsul name",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &CapsulName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify capsul instance URL",
|
|
||||||
Default: CapsulInstanceURL,
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &CapsulInstanceURL); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify capsul type",
|
|
||||||
Default: CapsulType,
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &CapsulType); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify capsul image",
|
|
||||||
Default: CapsulImage,
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &CapsulImage); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(CapsulSSHKeys.Value()) == 0 && !NoInput {
|
|
||||||
var sshKeys string
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify capsul SSH keys (e.g. me@foo.com)",
|
|
||||||
Default: "",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &CapsulSSHKeys); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
CapsulSSHKeys = cli.StringSlice(strings.Split(sshKeys, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
if CapsulAPIToken == "" && !NoInput {
|
|
||||||
token, ok := os.LookupEnv("CAPSUL_TOKEN")
|
|
||||||
if !ok {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify capsul API token",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &CapsulAPIToken); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CapsulAPIToken = token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if CapsulName == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul name?"))
|
|
||||||
}
|
|
||||||
if CapsulInstanceURL == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul instance url?"))
|
|
||||||
}
|
|
||||||
if CapsulType == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul type?"))
|
|
||||||
}
|
|
||||||
if CapsulImage == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul image?"))
|
|
||||||
}
|
|
||||||
if len(CapsulSSHKeys.Value()) == 0 {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul ssh keys?"))
|
|
||||||
}
|
|
||||||
if CapsulAPIToken == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul API token?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureNewHetznerCloudVPSFlags ensure all flags are present.
|
|
||||||
func EnsureNewHetznerCloudVPSFlags(c *cli.Context) error {
|
|
||||||
if HetznerCloudName == "" && !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud VPS name",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &HetznerCloudName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud VPS type",
|
|
||||||
Default: HetznerCloudType,
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &HetznerCloudType); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud VPS image",
|
|
||||||
Default: HetznerCloudImage,
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &HetznerCloudImage); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(HetznerCloudSSHKeys.Value()) == 0 && !NoInput {
|
|
||||||
var sshKeys string
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud SSH keys (e.g. me@foo.com)",
|
|
||||||
Default: "",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &sshKeys); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
HetznerCloudSSHKeys = cli.StringSlice(strings.Split(sshKeys, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud VPS location",
|
|
||||||
Default: HetznerCloudLocation,
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &HetznerCloudLocation); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if HetznerCloudAPIToken == "" && !NoInput {
|
|
||||||
token, ok := os.LookupEnv("HCLOUD_TOKEN")
|
|
||||||
if !ok {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud API token",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &HetznerCloudAPIToken); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
HetznerCloudAPIToken = token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if HetznerCloudName == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS name?"))
|
|
||||||
}
|
|
||||||
if HetznerCloudType == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS type?"))
|
|
||||||
}
|
|
||||||
if HetznerCloudImage == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud image?"))
|
|
||||||
}
|
|
||||||
if HetznerCloudLocation == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS location?"))
|
|
||||||
}
|
|
||||||
if HetznerCloudAPIToken == "" {
|
|
||||||
ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud API token?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -4,38 +4,38 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var recipeFetchCommand = cli.Command{
|
var recipeFetchCommand = cli.Command{
|
||||||
Name: "fetch",
|
Name: "fetch",
|
||||||
Usage: "Fetch recipe local copies",
|
Usage: "Fetch recipe(s)",
|
||||||
Aliases: []string{"f"},
|
Aliases: []string{"f"},
|
||||||
ArgsUsage: "[<recipe>]",
|
ArgsUsage: "[<recipe>]",
|
||||||
Description: "Fetchs all recipes without arguments.",
|
Description: "Retrieves all recipes if no <recipe> argument is passed",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
internal.NoInputFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
|
||||||
recipeName := c.Args().First()
|
recipeName := c.Args().First()
|
||||||
|
|
||||||
if recipeName != "" {
|
if recipeName != "" {
|
||||||
internal.ValidateRecipe(c, conf)
|
internal.ValidateRecipe(c)
|
||||||
return nil // ValidateRecipe ensures latest checkout
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repos, err := recipe.ReadReposMetadata(conf)
|
if err := recipe.EnsureExists(recipeName); err != nil {
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -22,17 +21,34 @@ var recipeLintCommand = cli.Command{
|
|||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OnlyErrorFlag,
|
internal.OnlyErrorFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.NoInputFlag,
|
||||||
|
internal.ChaosFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
recipe := internal.ValidateRecipe(c)
|
||||||
recipe := internal.ValidateRecipe(c, conf)
|
|
||||||
|
|
||||||
if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
|
if err := recipePkg.EnsureExists(recipe.Name); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !internal.Chaos {
|
||||||
|
if err := recipePkg.EnsureIsClean(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !internal.Offline {
|
||||||
|
if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureLatest(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tableCol := []string{"ref", "rule", "severity", "satisfied", "skipped", "resolve"}
|
tableCol := []string{"ref", "rule", "severity", "satisfied", "skipped", "resolve"}
|
||||||
table := formatter.CreateTable(tableCol)
|
table := formatter.CreateTable(tableCol)
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -34,9 +33,7 @@ var recipeListCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
|
||||||
|
|
||||||
catl, err := recipe.ReadRecipeCatalogue(conf)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err.Error())
|
logrus.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/tagcmp"
|
"coopcloud.tech/tagcmp"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
@ -60,8 +59,7 @@ your SSH keys configured on your account.
|
|||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
recipe := internal.ValidateRecipe(c)
|
||||||
recipe := internal.ValidateRecipeWithPrompt(c, conf)
|
|
||||||
|
|
||||||
imagesTmp, err := getImageVersions(recipe)
|
imagesTmp, err := getImageVersions(recipe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/tagcmp"
|
"coopcloud.tech/tagcmp"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
@ -29,7 +28,6 @@ var recipeSyncCommand = cli.Command{
|
|||||||
internal.MajorFlag,
|
internal.MajorFlag,
|
||||||
internal.MinorFlag,
|
internal.MinorFlag,
|
||||||
internal.PatchFlag,
|
internal.PatchFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
Description: `
|
Description: `
|
||||||
@ -42,9 +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
|
auto-generate it for you. The <recipe> configuration will be updated on the
|
||||||
local file system.
|
local file system.
|
||||||
`,
|
`,
|
||||||
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
recipe := internal.ValidateRecipe(c)
|
||||||
recipe := internal.ValidateRecipeWithPrompt(c, conf)
|
|
||||||
|
|
||||||
mainApp, err := internal.GetMainAppImage(recipe)
|
mainApp, err := internal.GetMainAppImage(recipe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -66,6 +64,9 @@ local file system.
|
|||||||
nextTag := c.Args().Get(1)
|
nextTag := c.Args().Get(1)
|
||||||
if len(tags) == 0 && nextTag == "" {
|
if len(tags) == 0 && nextTag == "" {
|
||||||
logrus.Warnf("no git tags found for %s", recipe.Name)
|
logrus.Warnf("no git tags found for %s", recipe.Name)
|
||||||
|
if internal.NoInput {
|
||||||
|
logrus.Fatalf("unable to continue, input required for initial version")
|
||||||
|
}
|
||||||
fmt.Println(fmt.Sprintf(`
|
fmt.Println(fmt.Sprintf(`
|
||||||
The following options are two types of initial semantic version that you can
|
The following options are two types of initial semantic version that you can
|
||||||
pick for %s that will be published in the recipe catalogue. This follows the
|
pick for %s that will be published in the recipe catalogue. This follows the
|
||||||
@ -199,5 +200,4 @@ likely to change.
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/tagcmp"
|
"coopcloud.tech/tagcmp"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/docker/distribution/reference"
|
"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
|
abra recipe upgrade
|
||||||
`,
|
`,
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
ArgsUsage: "<recipe>",
|
||||||
ArgsUsage: "<recipe>",
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
@ -68,14 +66,25 @@ You may invoke this command in "wizard" mode and be prompted for input:
|
|||||||
internal.MajorFlag,
|
internal.MajorFlag,
|
||||||
internal.MachineReadableFlag,
|
internal.MachineReadableFlag,
|
||||||
internal.AllTagsFlag,
|
internal.AllTagsFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
recipe := internal.ValidateRecipe(c)
|
||||||
recipe := internal.ValidateRecipeWithPrompt(c, conf)
|
|
||||||
|
|
||||||
if err := recipePkg.EnsureUpToDate(recipe.Name, conf); err != nil {
|
if err := recipePkg.EnsureIsClean(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureExists(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureUpToDate(recipe.Name); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := recipePkg.EnsureLatest(recipe.Name); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +194,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
|
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 {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,85 @@
|
|||||||
package recipe
|
package recipe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
"github.com/olekukonko/tablewriter"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func sortServiceByName(versions [][]string) func(i, j int) bool {
|
||||||
|
return func(i, j int) bool {
|
||||||
|
// NOTE(d1): corresponds to the `tableCol` definition below
|
||||||
|
if versions[i][1] == "app" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return versions[i][1] < versions[j][1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var recipeVersionCommand = cli.Command{
|
var recipeVersionCommand = cli.Command{
|
||||||
Name: "versions",
|
Name: "versions",
|
||||||
Aliases: []string{"v"},
|
Aliases: []string{"v"},
|
||||||
Usage: "List recipe versions",
|
Usage: "List recipe versions",
|
||||||
ArgsUsage: "<recipe>",
|
ArgsUsage: "<recipe>",
|
||||||
|
Description: "Versions are read from the recipe catalogue.",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.NoInputFlag,
|
||||||
|
internal.MachineReadableFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.RecipeNameComplete,
|
BashComplete: autocomplete.RecipeNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
recipe := internal.ValidateRecipe(c)
|
||||||
recipe := internal.ValidateRecipe(c, conf)
|
|
||||||
|
|
||||||
catalogue, err := recipePkg.ReadRecipeCatalogue(conf)
|
catl, err := recipePkg.ReadRecipeCatalogue(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
recipeMeta, ok := catalogue[recipe.Name]
|
recipeMeta, ok := catl[recipe.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
logrus.Fatalf("%s recipe doesn't exist?", recipe.Name)
|
logrus.Fatalf("%s is not published on the catalogue?", recipe.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableCol := []string{"Version", "Service", "Image", "Tag"}
|
if len(recipeMeta.Versions) == 0 {
|
||||||
table := formatter.CreateTable(tableCol)
|
logrus.Fatalf("%s has no catalogue published versions?", recipe.Name)
|
||||||
|
}
|
||||||
|
|
||||||
for i := len(recipeMeta.Versions) - 1; i >= 0; i-- {
|
for i := len(recipeMeta.Versions) - 1; i >= 0; i-- {
|
||||||
for tag, meta := range recipeMeta.Versions[i] {
|
tableCols := []string{"version", "service", "image", "tag"}
|
||||||
|
table := formatter.CreateTable(tableCols)
|
||||||
|
for version, meta := range recipeMeta.Versions[i] {
|
||||||
|
var versions [][]string
|
||||||
for service, serviceMeta := range meta {
|
for service, serviceMeta := range meta {
|
||||||
table.Append([]string{tag, service, serviceMeta.Image, serviceMeta.Tag})
|
versions = append(versions, []string{version, service, serviceMeta.Image, serviceMeta.Tag})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(versions, sortServiceByName(versions))
|
||||||
|
|
||||||
|
for _, version := range versions {
|
||||||
|
table.Append(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
if internal.MachineReadable {
|
||||||
|
table.JSONRender()
|
||||||
|
} else {
|
||||||
|
table.SetAutoMergeCellsByColumnIndex([]int{0})
|
||||||
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||||
|
table.Render()
|
||||||
|
fmt.Println()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.SetAutoMergeCells(true)
|
|
||||||
|
|
||||||
if table.NumLines() > 0 {
|
|
||||||
table.Render()
|
|
||||||
} else {
|
|
||||||
logrus.Fatalf("%s has no published versions?", recipe.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
package record
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
|
||||||
gandiPkg "coopcloud.tech/abra/pkg/dns/gandi"
|
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
|
||||||
"github.com/libdns/gandi"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RecordListCommand lists domains.
|
|
||||||
var RecordListCommand = cli.Command{
|
|
||||||
Name: "list",
|
|
||||||
Usage: "List domain name records",
|
|
||||||
Aliases: []string{"ls"},
|
|
||||||
ArgsUsage: "<zone>",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
internal.DebugFlag,
|
|
||||||
internal.DNSProviderFlag,
|
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
|
||||||
Before: internal.SubCommandBefore,
|
|
||||||
Description: `
|
|
||||||
List all domain name records managed by a 3rd party provider for a specific
|
|
||||||
zone.
|
|
||||||
|
|
||||||
You must specify a zone (e.g. example.com) under which your domain name records
|
|
||||||
are listed. This zone must already be created on your provider account.
|
|
||||||
`,
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
if err := internal.EnsureDNSProvider(); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
zone, err := internal.EnsureZoneArgument(c)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var provider gandi.Provider
|
|
||||||
switch internal.DNSProvider {
|
|
||||||
case "gandi":
|
|
||||||
provider, err = gandiPkg.New()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
logrus.Fatalf("%s is not a supported DNS provider", internal.DNSProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
records, err := provider.GetRecords(context.Background(), zone)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tableCol := []string{"type", "name", "value", "TTL", "priority"}
|
|
||||||
table := formatter.CreateTable(tableCol)
|
|
||||||
|
|
||||||
for _, record := range records {
|
|
||||||
value := record.Value
|
|
||||||
if len(record.Value) > 30 {
|
|
||||||
value = fmt.Sprintf("%s...", record.Value[:30])
|
|
||||||
}
|
|
||||||
|
|
||||||
table.Append([]string{
|
|
||||||
record.Type,
|
|
||||||
record.Name,
|
|
||||||
value,
|
|
||||||
record.TTL.String(),
|
|
||||||
strconv.Itoa(record.Priority),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
package record
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
|
||||||
"coopcloud.tech/abra/pkg/dns"
|
|
||||||
gandiPkg "coopcloud.tech/abra/pkg/dns/gandi"
|
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
|
||||||
"github.com/libdns/gandi"
|
|
||||||
"github.com/libdns/libdns"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RecordNewCommand creates a new domain name record.
|
|
||||||
var RecordNewCommand = cli.Command{
|
|
||||||
Name: "new",
|
|
||||||
Usage: "Create a new domain record",
|
|
||||||
Aliases: []string{"n"},
|
|
||||||
ArgsUsage: "<zone>",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
internal.DebugFlag,
|
|
||||||
internal.NoInputFlag,
|
|
||||||
internal.DNSProviderFlag,
|
|
||||||
internal.DNSTypeFlag,
|
|
||||||
internal.DNSNameFlag,
|
|
||||||
internal.DNSValueFlag,
|
|
||||||
internal.DNSTTLFlag,
|
|
||||||
internal.DNSPriorityFlag,
|
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
|
||||||
Before: internal.SubCommandBefore,
|
|
||||||
Description: `
|
|
||||||
Create a new domain name record for a specific zone.
|
|
||||||
|
|
||||||
You must specify a zone (e.g. example.com) under which your domain name records
|
|
||||||
are listed. This zone must already be created on your provider account.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
abra record new foo.com -p gandi -t A -n myapp -v 192.168.178.44
|
|
||||||
|
|
||||||
You may also invoke this command in "wizard" mode and be prompted for input:
|
|
||||||
|
|
||||||
abra record new
|
|
||||||
`,
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
zone, err := internal.EnsureZoneArgument(c)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := internal.EnsureDNSProvider(); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var provider gandi.Provider
|
|
||||||
switch internal.DNSProvider {
|
|
||||||
case "gandi":
|
|
||||||
provider, err = gandiPkg.New()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
logrus.Fatalf("%s is not a supported DNS provider", internal.DNSProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := internal.EnsureDNSTypeFlag(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := internal.EnsureDNSNameFlag(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := internal.EnsureDNSValueFlag(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ttl, err := dns.GetTTL(internal.DNSTTL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
record := libdns.Record{
|
|
||||||
Type: internal.DNSType,
|
|
||||||
Name: internal.DNSName,
|
|
||||||
Value: internal.DNSValue,
|
|
||||||
TTL: ttl,
|
|
||||||
}
|
|
||||||
|
|
||||||
if internal.DNSType == "MX" || internal.DNSType == "SRV" || internal.DNSType == "URI" {
|
|
||||||
record.Priority = internal.DNSPriority
|
|
||||||
}
|
|
||||||
|
|
||||||
records, err := provider.GetRecords(context.Background(), zone)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, existingRecord := range records {
|
|
||||||
if existingRecord.Type == record.Type &&
|
|
||||||
existingRecord.Name == record.Name &&
|
|
||||||
existingRecord.Value == record.Value {
|
|
||||||
logrus.Fatalf("%s record for %s already exists?", record.Type, zone)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createdRecords, err := provider.SetRecords(
|
|
||||||
context.Background(),
|
|
||||||
zone,
|
|
||||||
[]libdns.Record{record},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(createdRecords) == 0 {
|
|
||||||
logrus.Fatal("provider library reports that no record was created?")
|
|
||||||
}
|
|
||||||
|
|
||||||
createdRecord := createdRecords[0]
|
|
||||||
|
|
||||||
tableCol := []string{"type", "name", "value", "TTL", "priority"}
|
|
||||||
table := formatter.CreateTable(tableCol)
|
|
||||||
|
|
||||||
value := createdRecord.Value
|
|
||||||
if len(createdRecord.Value) > 30 {
|
|
||||||
value = fmt.Sprintf("%s...", createdRecord.Value[:30])
|
|
||||||
}
|
|
||||||
|
|
||||||
table.Append([]string{
|
|
||||||
createdRecord.Type,
|
|
||||||
createdRecord.Name,
|
|
||||||
value,
|
|
||||||
createdRecord.TTL.String(),
|
|
||||||
strconv.Itoa(createdRecord.Priority),
|
|
||||||
})
|
|
||||||
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
logrus.Info("record created")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package record
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RecordCommand supports managing DNS entries.
|
|
||||||
var RecordCommand = cli.Command{
|
|
||||||
Name: "record",
|
|
||||||
Usage: "Manage domain name records",
|
|
||||||
Aliases: []string{"rc"},
|
|
||||||
ArgsUsage: "<record>",
|
|
||||||
Description: `
|
|
||||||
Manage domain name records via 3rd party providers such as Gandi DNS. It
|
|
||||||
supports listing, creating and removing all types of records that you might
|
|
||||||
need for managing Co-op Cloud apps.
|
|
||||||
|
|
||||||
The following providers are supported:
|
|
||||||
|
|
||||||
Gandi DNS https://www.gandi.net
|
|
||||||
|
|
||||||
You need an account with such a provider already. Typically, you need to
|
|
||||||
provide an API token on the Abra command-line when using these commands so that
|
|
||||||
you can authenticate with your provider account.
|
|
||||||
|
|
||||||
New providers can be integrated, we welcome change sets. See the underlying DNS
|
|
||||||
library documentation for more. It supports many existing providers and allows
|
|
||||||
to implement new provider support easily.
|
|
||||||
|
|
||||||
https://pkg.go.dev/github.com/libdns/libdns
|
|
||||||
`,
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
RecordListCommand,
|
|
||||||
RecordNewCommand,
|
|
||||||
RecordRemoveCommand,
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
package record
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
|
||||||
gandiPkg "coopcloud.tech/abra/pkg/dns/gandi"
|
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
|
||||||
"github.com/libdns/gandi"
|
|
||||||
"github.com/libdns/libdns"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RecordRemoveCommand lists domains.
|
|
||||||
var RecordRemoveCommand = cli.Command{
|
|
||||||
Name: "remove",
|
|
||||||
Usage: "Remove a domain name record",
|
|
||||||
Aliases: []string{"rm"},
|
|
||||||
ArgsUsage: "<zone>",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
internal.DebugFlag,
|
|
||||||
internal.NoInputFlag,
|
|
||||||
internal.DNSProviderFlag,
|
|
||||||
internal.DNSTypeFlag,
|
|
||||||
internal.DNSNameFlag,
|
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
|
||||||
Before: internal.SubCommandBefore,
|
|
||||||
Description: `
|
|
||||||
Remove a domain name record for a specific zone.
|
|
||||||
|
|
||||||
It uses the type of record and name to match existing records and choose one
|
|
||||||
for deletion. You must specify a zone (e.g. example.com) under which your
|
|
||||||
domain name records are listed. This zone must already be created on your
|
|
||||||
provider account.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
abra record remove foo.com -p gandi -t A -n myapp
|
|
||||||
|
|
||||||
You may also invoke this command in "wizard" mode and be prompted for input:
|
|
||||||
|
|
||||||
abra record rm
|
|
||||||
`,
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
zone, err := internal.EnsureZoneArgument(c)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := internal.EnsureDNSProvider(); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var provider gandi.Provider
|
|
||||||
switch internal.DNSProvider {
|
|
||||||
case "gandi":
|
|
||||||
provider, err = gandiPkg.New()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
logrus.Fatalf("%s is not a supported DNS provider", internal.DNSProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := internal.EnsureDNSTypeFlag(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := internal.EnsureDNSNameFlag(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
records, err := provider.GetRecords(context.Background(), zone)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var toDelete libdns.Record
|
|
||||||
for _, record := range records {
|
|
||||||
if record.Type == internal.DNSType && record.Name == internal.DNSName {
|
|
||||||
toDelete = record
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (libdns.Record{}) == toDelete {
|
|
||||||
logrus.Fatal("provider library reports no matching record?")
|
|
||||||
}
|
|
||||||
|
|
||||||
tableCol := []string{"type", "name", "value", "TTL", "priority"}
|
|
||||||
table := formatter.CreateTable(tableCol)
|
|
||||||
|
|
||||||
value := toDelete.Value
|
|
||||||
if len(toDelete.Value) > 30 {
|
|
||||||
value = fmt.Sprintf("%s...", toDelete.Value[:30])
|
|
||||||
}
|
|
||||||
|
|
||||||
table.Append([]string{
|
|
||||||
toDelete.Type,
|
|
||||||
toDelete.Name,
|
|
||||||
value,
|
|
||||||
toDelete.TTL.String(),
|
|
||||||
strconv.Itoa(toDelete.Priority),
|
|
||||||
})
|
|
||||||
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
if !internal.NoInput {
|
|
||||||
response := false
|
|
||||||
prompt := &survey.Confirm{
|
|
||||||
Message: "continue with record deletion?",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &response); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !response {
|
|
||||||
logrus.Fatal("exiting as requested")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = provider.DeleteRecords(context.Background(), zone, []libdns.Record{toDelete})
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Info("record successfully deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
@ -118,7 +118,6 @@ developer machine.
|
|||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
localFlag,
|
localFlag,
|
||||||
internal.OfflineFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<domain>",
|
||||||
|
@ -60,6 +60,17 @@ var serverListCommand = cli.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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}
|
row = []string{serverName, sp.Host, sp.User, sp.Port}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,8 +84,11 @@ var serverListCommand = cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if problemsFilter {
|
if problemsFilter {
|
||||||
if row[1] == "unknown" {
|
for _, val := range row {
|
||||||
table.Append(row)
|
if val == "unknown" {
|
||||||
|
table.Append(row)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
table.Append(row)
|
table.Append(row)
|
||||||
|
@ -1,261 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
|
||||||
"coopcloud.tech/libcapsul"
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
|
||||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newHetznerCloudVPS(c *cli.Context) error {
|
|
||||||
if err := internal.EnsureNewHetznerCloudVPSFlags(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := hcloud.NewClient(hcloud.WithToken(internal.HetznerCloudAPIToken))
|
|
||||||
|
|
||||||
var sshKeysRaw []string
|
|
||||||
var sshKeys []*hcloud.SSHKey
|
|
||||||
for _, sshKey := range c.StringSlice("hetzner-ssh-keys") {
|
|
||||||
if sshKey == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
sshKey, _, err := client.SSHKey.GetByName(context.Background(), sshKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sshKeys = append(sshKeys, sshKey)
|
|
||||||
sshKeysRaw = append(sshKeysRaw, sshKey.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverOpts := hcloud.ServerCreateOpts{
|
|
||||||
Name: internal.HetznerCloudName,
|
|
||||||
ServerType: &hcloud.ServerType{Name: internal.HetznerCloudType},
|
|
||||||
Image: &hcloud.Image{Name: internal.HetznerCloudImage},
|
|
||||||
SSHKeys: sshKeys,
|
|
||||||
Location: &hcloud.Location{Name: internal.HetznerCloudLocation},
|
|
||||||
}
|
|
||||||
|
|
||||||
sshKeyIDs := strings.Join(sshKeysRaw, "\n")
|
|
||||||
if sshKeyIDs == "" {
|
|
||||||
sshKeyIDs = "N/A (password auth)"
|
|
||||||
}
|
|
||||||
|
|
||||||
tableColumns := []string{"name", "type", "image", "ssh-keys", "location"}
|
|
||||||
table := formatter.CreateTable(tableColumns)
|
|
||||||
table.Append([]string{
|
|
||||||
internal.HetznerCloudName,
|
|
||||||
internal.HetznerCloudType,
|
|
||||||
internal.HetznerCloudImage,
|
|
||||||
sshKeyIDs,
|
|
||||||
internal.HetznerCloudLocation,
|
|
||||||
})
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
response := false
|
|
||||||
prompt := &survey.Confirm{
|
|
||||||
Message: "continue with hetzner cloud VPS creation?",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &response); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !response {
|
|
||||||
logrus.Fatal("exiting as requested")
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _, err := client.Server.Create(context.Background(), serverOpts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var rootPassword string
|
|
||||||
if len(sshKeys) > 0 {
|
|
||||||
rootPassword = "N/A (using SSH keys)"
|
|
||||||
} else {
|
|
||||||
rootPassword = res.RootPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := res.Server.PublicNet.IPv4.IP.String()
|
|
||||||
|
|
||||||
fmt.Println(fmt.Sprintf(`
|
|
||||||
Your new Hetzner Cloud VPS has successfully been created! Here are the details:
|
|
||||||
|
|
||||||
name: %s
|
|
||||||
IP address: %s
|
|
||||||
root password: %s
|
|
||||||
|
|
||||||
You can access this new VPS via SSH using the following command:
|
|
||||||
|
|
||||||
ssh root@%s
|
|
||||||
|
|
||||||
Please note, this server is not managed by Abra yet (i.e. "abra server ls" will
|
|
||||||
not list this server)! You will need to assign a domain name record (manually
|
|
||||||
or by using "abra record new") and add the server to your Abra configuration
|
|
||||||
("abra server add") to have a working server that you can deploy Co-op Cloud
|
|
||||||
apps to.
|
|
||||||
|
|
||||||
When setting up domain name records, you probably want to set up the following
|
|
||||||
2 A records. This supports deploying apps to your root domain (e.g.
|
|
||||||
example.com) and other apps on sub-domains (e.g. foo.example.com,
|
|
||||||
bar.example.com).
|
|
||||||
|
|
||||||
@ 1800 IN A %s
|
|
||||||
* 1800 IN A %s
|
|
||||||
`,
|
|
||||||
internal.HetznerCloudName, ip, rootPassword,
|
|
||||||
ip, ip, ip,
|
|
||||||
))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCapsulVPS(c *cli.Context) error {
|
|
||||||
if err := internal.EnsureNewCapsulVPSFlags(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
capsulCreateURL := fmt.Sprintf("https://%s/api/capsul/create", internal.CapsulInstanceURL)
|
|
||||||
|
|
||||||
var sshKeys []string
|
|
||||||
for _, sshKey := range c.StringSlice("capsul-ssh-keys") {
|
|
||||||
if sshKey == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sshKeys = append(sshKeys, sshKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
tableColumns := []string{"instance", "name", "type", "image", "ssh-keys"}
|
|
||||||
table := formatter.CreateTable(tableColumns)
|
|
||||||
table.Append([]string{
|
|
||||||
internal.CapsulInstanceURL,
|
|
||||||
internal.CapsulName,
|
|
||||||
internal.CapsulType,
|
|
||||||
internal.CapsulImage,
|
|
||||||
strings.Join(sshKeys, "\n"),
|
|
||||||
})
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
response := false
|
|
||||||
prompt := &survey.Confirm{
|
|
||||||
Message: "continue with capsul creation?",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &response); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !response {
|
|
||||||
logrus.Fatal("exiting as requested")
|
|
||||||
}
|
|
||||||
|
|
||||||
capsulClient := libcapsul.New(capsulCreateURL, internal.CapsulAPIToken)
|
|
||||||
resp, err := capsulClient.Create(
|
|
||||||
internal.CapsulName,
|
|
||||||
internal.CapsulType,
|
|
||||||
internal.CapsulImage,
|
|
||||||
sshKeys,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(fmt.Sprintf(`
|
|
||||||
Your new Capsul has successfully been created! Here are the details:
|
|
||||||
|
|
||||||
Capsul name: %s
|
|
||||||
Capsul ID: %v
|
|
||||||
|
|
||||||
You will need to log into your Capsul instance web interface to retrieve the IP
|
|
||||||
address. You can learn all about how to get SSH access to your new Capsul on:
|
|
||||||
|
|
||||||
%s/about-ssh
|
|
||||||
|
|
||||||
Please note, this server is not managed by Abra yet (i.e. "abra server ls" will
|
|
||||||
not list this server)! You will need to assign a domain name record (manually
|
|
||||||
or by using "abra record new") and add the server to your Abra configuration
|
|
||||||
("abra server add") to have a working server that you can deploy Co-op Cloud
|
|
||||||
apps to.
|
|
||||||
|
|
||||||
When setting up domain name records, you probably want to set up the following
|
|
||||||
2 A records. This supports deploying apps to your root domain (e.g.
|
|
||||||
example.com) and other apps on sub-domains (e.g. foo.example.com,
|
|
||||||
bar.example.com).
|
|
||||||
|
|
||||||
@ 1800 IN A <your-capsul-ip>
|
|
||||||
* 1800 IN A <your-capsul-ip>
|
|
||||||
`, internal.CapsulName, resp.ID, internal.CapsulInstanceURL))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverNewCommand = cli.Command{
|
|
||||||
Name: "new",
|
|
||||||
Aliases: []string{"n"},
|
|
||||||
Usage: "Create a new server using a 3rd party provider",
|
|
||||||
Description: `
|
|
||||||
Create a new server via a 3rd party provider.
|
|
||||||
|
|
||||||
The following providers are supported:
|
|
||||||
|
|
||||||
Capsul https://git.cyberia.club/Cyberia/capsul-flask
|
|
||||||
Hetzner Cloud https://docs.hetzner.com/cloud
|
|
||||||
|
|
||||||
You may invoke this command in "wizard" mode and be prompted for input:
|
|
||||||
|
|
||||||
abra record new
|
|
||||||
|
|
||||||
API tokens are read from the environment if specified, e.g.
|
|
||||||
|
|
||||||
export HCLOUD_TOKEN=...
|
|
||||||
`,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
internal.DebugFlag,
|
|
||||||
internal.NoInputFlag,
|
|
||||||
internal.ServerProviderFlag,
|
|
||||||
internal.OfflineFlag,
|
|
||||||
|
|
||||||
// Capsul
|
|
||||||
internal.CapsulInstanceURLFlag,
|
|
||||||
internal.CapsulTypeFlag,
|
|
||||||
internal.CapsulImageFlag,
|
|
||||||
internal.CapsulSSHKeysFlag,
|
|
||||||
internal.CapsulAPITokenFlag,
|
|
||||||
|
|
||||||
// Hetzner
|
|
||||||
internal.HetznerCloudNameFlag,
|
|
||||||
internal.HetznerCloudTypeFlag,
|
|
||||||
internal.HetznerCloudImageFlag,
|
|
||||||
internal.HetznerCloudSSHKeysFlag,
|
|
||||||
internal.HetznerCloudLocationFlag,
|
|
||||||
internal.HetznerCloudAPITokenFlag,
|
|
||||||
},
|
|
||||||
Before: internal.SubCommandBefore,
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
if err := internal.EnsureServerProvider(); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch internal.ServerProvider {
|
|
||||||
case "capsul":
|
|
||||||
if err := newCapsulVPS(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
case "hetzner-cloud":
|
|
||||||
if err := newHetznerCloudVPS(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
@ -20,12 +20,12 @@ var allFilterFlag = &cli.BoolFlag{
|
|||||||
Destination: &allFilter,
|
Destination: &allFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
var volunesFilter bool
|
var volumesFilter bool
|
||||||
|
|
||||||
var volumesFilterFlag = &cli.BoolFlag{
|
var volumesFilterFlag = &cli.BoolFlag{
|
||||||
Name: "volumes, v",
|
Name: "volumes, v",
|
||||||
Usage: "Prune volumes. This will remove app data, Be Careful!",
|
Usage: "Prune volumes. This will remove app data, Be Careful!",
|
||||||
Destination: &volunesFilter,
|
Destination: &volumesFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverPruneCommand = cli.Command{
|
var serverPruneCommand = cli.Command{
|
||||||
@ -35,7 +35,7 @@ var serverPruneCommand = cli.Command{
|
|||||||
Description: `
|
Description: `
|
||||||
Prunes unused containers, networks, and dangling images.
|
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.
|
also be removed. This can result in unwanted data loss if not used carefully.
|
||||||
`,
|
`,
|
||||||
ArgsUsage: "[<server>]",
|
ArgsUsage: "[<server>]",
|
||||||
@ -44,12 +44,11 @@ also be removed. This can result in unwanted data loss if not used carefully.
|
|||||||
volumesFilterFlag,
|
volumesFilterFlag,
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
internal.NoInputFlag,
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.ServerNameComplete,
|
BashComplete: autocomplete.ServerNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
var args filters.Args
|
|
||||||
|
|
||||||
serverName := internal.ValidateServer(c)
|
serverName := internal.ValidateServer(c)
|
||||||
|
|
||||||
cl, err := client.New(serverName)
|
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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var args filters.Args
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
cr, err := cl.ContainersPrune(ctx, args)
|
cr, err := cl.ContainersPrune(ctx, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -75,6 +76,7 @@ also be removed. This can result in unwanted data loss if not used carefully.
|
|||||||
|
|
||||||
pruneFilters := filters.NewArgs()
|
pruneFilters := filters.NewArgs()
|
||||||
if allFilter {
|
if allFilter {
|
||||||
|
logrus.Debugf("removing all images, not only dangling ones")
|
||||||
pruneFilters.Add("dangling", "false")
|
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)
|
imgSpaceReclaimed := formatter.ByteCountSI(ir.SpaceReclaimed)
|
||||||
logrus.Infof("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed)
|
logrus.Infof("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed)
|
||||||
|
|
||||||
if volunesFilter {
|
if volumesFilter {
|
||||||
vr, err := cl.VolumesPrune(ctx, args)
|
vr, err := cl.VolumesPrune(ctx, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@ -10,168 +8,31 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
|
||||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rmServer bool
|
|
||||||
var rmServerFlag = &cli.BoolFlag{
|
|
||||||
Name: "server, s",
|
|
||||||
Usage: "remove the actual server also",
|
|
||||||
Destination: &rmServer,
|
|
||||||
}
|
|
||||||
|
|
||||||
func rmHetznerCloudVPS(c *cli.Context) error {
|
|
||||||
if internal.HetznerCloudName == "" && !internal.NoInput {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud VPS name",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &internal.HetznerCloudName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if internal.HetznerCloudAPIToken == "" && !internal.NoInput {
|
|
||||||
token, ok := os.LookupEnv("HCLOUD_TOKEN")
|
|
||||||
if !ok {
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: "specify hetzner cloud API token",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &internal.HetznerCloudAPIToken); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
internal.HetznerCloudAPIToken = token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client := hcloud.NewClient(hcloud.WithToken(internal.HetznerCloudAPIToken))
|
|
||||||
|
|
||||||
server, _, err := client.Server.Get(context.Background(), internal.HetznerCloudName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if server == nil {
|
|
||||||
logrus.Fatalf("library provider reports that %s doesn't exist?", internal.HetznerCloudName)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(fmt.Sprintf(`
|
|
||||||
You have requested that Abra delete the following server (%s). Please be
|
|
||||||
absolutely sure that this is indeed the server that you would like to have
|
|
||||||
removed. There will be no going back once you confirm, the server will be
|
|
||||||
destroyed.
|
|
||||||
`, server.Name))
|
|
||||||
|
|
||||||
tableColumns := []string{"name", "type", "image", "location"}
|
|
||||||
table := formatter.CreateTable(tableColumns)
|
|
||||||
table.Append([]string{
|
|
||||||
server.Name,
|
|
||||||
server.ServerType.Name,
|
|
||||||
server.Image.Name,
|
|
||||||
server.Datacenter.Name,
|
|
||||||
})
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
response := false
|
|
||||||
prompt := &survey.Confirm{
|
|
||||||
Message: "continue with hetzner cloud VPS removal?",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &response); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !response {
|
|
||||||
logrus.Fatal("exiting as requested")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.Server.Delete(context.Background(), server)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Infof("%s has been deleted from your hetzner cloud account", internal.HetznerCloudName)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverRemoveCommand = cli.Command{
|
var serverRemoveCommand = cli.Command{
|
||||||
Name: "remove",
|
Name: "remove",
|
||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
ArgsUsage: "[<server>]",
|
ArgsUsage: "<server>",
|
||||||
Usage: "Remove a managed server",
|
Usage: "Remove a managed server",
|
||||||
Description: `
|
Description: `Remove a managed server.
|
||||||
Remova a server from Abra management.
|
|
||||||
|
|
||||||
Depending on whether you used a 3rd party provider to create this server ("abra
|
Abra will remove the internal bookkeeping (~/.abra/servers/...) and underlying
|
||||||
server new"), you can also destroy the virtual server as well. Pass
|
client connection context. This server will then be lost in time, like tears in
|
||||||
"--server/-s" to tell Abra to try to delete this VPS.
|
rain.
|
||||||
|
|
||||||
Otherwise, Abra will remove the internal bookkeeping (~/.abra/servers/...) and
|
|
||||||
underlying client connection context. This server will then be lost in time,
|
|
||||||
like tears in rain.
|
|
||||||
`,
|
`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DebugFlag,
|
internal.DebugFlag,
|
||||||
internal.NoInputFlag,
|
internal.NoInputFlag,
|
||||||
rmServerFlag,
|
|
||||||
internal.ServerProviderFlag,
|
|
||||||
internal.OfflineFlag,
|
internal.OfflineFlag,
|
||||||
|
|
||||||
// Hetzner
|
|
||||||
internal.HetznerCloudNameFlag,
|
|
||||||
internal.HetznerCloudAPITokenFlag,
|
|
||||||
},
|
},
|
||||||
Before: internal.SubCommandBefore,
|
Before: internal.SubCommandBefore,
|
||||||
BashComplete: autocomplete.ServerNameComplete,
|
BashComplete: autocomplete.ServerNameComplete,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
serverName := internal.ValidateServer(c)
|
serverName := internal.ValidateServer(c)
|
||||||
|
|
||||||
warnMsg := `Did not pass -s/--server for actual server deletion, prompting!
|
|
||||||
|
|
||||||
Abra doesn't currently know if it helped you create this server with one of the
|
|
||||||
3rd party integrations (e.g. Capsul). You have a choice here to actually,
|
|
||||||
really and finally destroy this server using those integrations. If you want to
|
|
||||||
do this, choose Yes.
|
|
||||||
|
|
||||||
If you just want to remove the server config files & context, choose No.
|
|
||||||
`
|
|
||||||
|
|
||||||
if !rmServer {
|
|
||||||
logrus.Warn(fmt.Sprintf(warnMsg))
|
|
||||||
|
|
||||||
response := false
|
|
||||||
prompt := &survey.Confirm{
|
|
||||||
Message: "delete actual live server?",
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &response); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
if response {
|
|
||||||
logrus.Info("setting -s/--server and attempting to remove actual server")
|
|
||||||
rmServer = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rmServer {
|
|
||||||
if err := internal.EnsureServerProvider(); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch internal.ServerProvider {
|
|
||||||
case "capsul":
|
|
||||||
logrus.Warn("capsul provider does not support automatic removal yet, sorry!")
|
|
||||||
case "hetzner-cloud":
|
|
||||||
if err := rmHetznerCloudVPS(c); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.DeleteContext(serverName); err != nil {
|
if err := client.DeleteContext(serverName); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,7 @@ var ServerCommand = cli.Command{
|
|||||||
Name: "server",
|
Name: "server",
|
||||||
Aliases: []string{"s"},
|
Aliases: []string{"s"},
|
||||||
Usage: "Manage servers",
|
Usage: "Manage servers",
|
||||||
Description: `
|
|
||||||
Create, manage and remove servers using 3rd party integrations.
|
|
||||||
|
|
||||||
Servers can be created from scratch using the "abra server new" command. If you
|
|
||||||
already have a server, you can add it to your configuration using "abra server
|
|
||||||
add". Abra can provision servers so that they are ready to deploy Co-op Cloud
|
|
||||||
recipes, see available flags on "abra server add" for more.
|
|
||||||
`,
|
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
serverNewCommand,
|
|
||||||
serverAddCommand,
|
serverAddCommand,
|
||||||
serverListCommand,
|
serverListCommand,
|
||||||
serverRemoveCommand,
|
serverRemoveCommand,
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/upstream/convert"
|
"coopcloud.tech/abra/pkg/upstream/convert"
|
||||||
"coopcloud.tech/abra/pkg/upstream/stack"
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"coopcloud.tech/tagcmp"
|
"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.
|
printed. To include major versions use the --major flag.
|
||||||
`,
|
`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
|
||||||
|
|
||||||
cl, err := client.New("default")
|
cl, err := client.New("default")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -78,7 +75,7 @@ printed. To include major versions use the --major flag.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if recipeName != "" {
|
if recipeName != "" {
|
||||||
_, err = getLatestUpgrade(cl, stackName, recipeName, conf)
|
_, err = getLatestUpgrade(cl, stackName, recipeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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.
|
update chaos deployments use the "--chaos" flag. Use it with care.
|
||||||
`,
|
`,
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
conf := runtime.New(runtime.WithOffline(internal.Offline))
|
|
||||||
|
|
||||||
cl, err := client.New("default")
|
cl, err := client.New("default")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
@ -123,7 +118,7 @@ update chaos deployments use the "--chaos" flag. Use it with care.
|
|||||||
if !updateAll {
|
if !updateAll {
|
||||||
stackName := c.Args().Get(0)
|
stackName := c.Args().Get(0)
|
||||||
recipeName := c.Args().Get(1)
|
recipeName := c.Args().Get(1)
|
||||||
err = tryUpgrade(cl, stackName, recipeName, conf)
|
err = tryUpgrade(cl, stackName, recipeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -143,7 +138,7 @@ update chaos deployments use the "--chaos" flag. Use it with care.
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tryUpgrade(cl, stackName, recipeName, conf)
|
err = tryUpgrade(cl, stackName, recipeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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
|
// getLatestUpgrade returns the latest available version for an app respecting
|
||||||
// the "--major" flag if it is newer than the currently deployed version.
|
// the "--major" flag if it is newer than the currently deployed version.
|
||||||
func getLatestUpgrade(cl *dockerclient.Client, stackName string,
|
func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName string) (string, error) {
|
||||||
recipeName string, conf *runtime.Config) (string, error) {
|
|
||||||
deployedVersion, err := getDeployedVersion(cl, stackName, recipeName)
|
deployedVersion, err := getDeployedVersion(cl, stackName, recipeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
availableUpgrades, err := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion, conf)
|
availableUpgrades, err := getAvailableUpgrades(cl, stackName, recipeName, deployedVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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"
|
// than the deployed version. It only includes major upgrades if the "--major"
|
||||||
// flag is set.
|
// flag is set.
|
||||||
func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string,
|
func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName string,
|
||||||
deployedVersion string, conf *runtime.Config) ([]string, error) {
|
deployedVersion string) ([]string, error) {
|
||||||
catl, err := recipe.ReadRecipeCatalogue(conf)
|
catl, err := recipe.ReadRecipeCatalogue(internal.Offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// processRecipeRepoVersion clones, pulls, checks out the version and lints the
|
||||||
// recipe repository.
|
// recipe repository.
|
||||||
func processRecipeRepoVersion(recipeName, version string, conf *runtime.Config) error {
|
func processRecipeRepoVersion(recipeName, version string) error {
|
||||||
if err := recipe.EnsureExists(recipeName, conf); err != nil {
|
if err := recipe.EnsureExists(recipeName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := recipe.EnsureUpToDate(recipeName, conf); err != nil {
|
if err := recipe.EnsureUpToDate(recipeName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +329,7 @@ func processRecipeRepoVersion(recipeName, version string, conf *runtime.Config)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if r, err := recipe.Get(recipeName, conf); err != nil {
|
if r, err := recipe.Get(recipeName, internal.Offline); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := lint.LintForErrors(r); err != nil {
|
} else if err := lint.LintForErrors(r); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -370,7 +364,7 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv)
|
|||||||
ResolveImage: stack.ResolveImageAlways,
|
ResolveImage: stack.ResolveImageAlways,
|
||||||
}
|
}
|
||||||
|
|
||||||
composeFiles, err := config.GetAppComposeFiles(recipeName, env)
|
composeFiles, err := config.GetComposeFiles(recipeName, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, deployOpts, err
|
return nil, deployOpts, err
|
||||||
}
|
}
|
||||||
@ -392,7 +386,7 @@ func createDeployConfig(recipeName string, stackName string, env config.AppEnv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tryUpgrade performs the upgrade if all the requirements are fulfilled.
|
// 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 == "" {
|
if recipeName == "" {
|
||||||
logrus.Debugf("don't update %s due to missing recipe name", stackName)
|
logrus.Debugf("don't update %s due to missing recipe name", stackName)
|
||||||
return nil
|
return nil
|
||||||
@ -418,7 +412,7 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string, conf *run
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
upgradeVersion, err := getLatestUpgrade(cl, stackName, recipeName, conf)
|
upgradeVersion, err := getLatestUpgrade(cl, stackName, recipeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -428,14 +422,14 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string, conf *run
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = upgrade(cl, stackName, recipeName, upgradeVersion, conf)
|
err = upgrade(cl, stackName, recipeName, upgradeVersion)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// upgrade performs all necessary steps to upgrade an app.
|
// upgrade performs all necessary steps to upgrade an app.
|
||||||
func upgrade(cl *dockerclient.Client, stackName, recipeName,
|
func upgrade(cl *dockerclient.Client, stackName, recipeName,
|
||||||
upgradeVersion string, conf *runtime.Config) error {
|
upgradeVersion string) error {
|
||||||
env, err := getEnv(cl, stackName)
|
env, err := getEnv(cl, stackName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -448,7 +442,7 @@ func upgrade(cl *dockerclient.Client, stackName, recipeName,
|
|||||||
Env: env,
|
Env: env,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = processRecipeRepoVersion(recipeName, upgradeVersion, conf); err != nil {
|
if err = processRecipeRepoVersion(recipeName, upgradeVersion); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
89
go.mod
89
go.mod
@ -1,28 +1,97 @@
|
|||||||
module coopcloud.tech/abra
|
module coopcloud.tech/abra
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52
|
coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||||
github.com/Autonomic-Cooperative/godotenv v1.3.1-0.20210731094149-b031ea1211e7
|
github.com/Autonomic-Cooperative/godotenv v1.3.1-0.20210731094149-b031ea1211e7
|
||||||
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
|
github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4
|
||||||
github.com/docker/cli v24.0.5+incompatible
|
github.com/docker/cli v24.0.6+incompatible
|
||||||
github.com/docker/distribution v2.8.2+incompatible
|
github.com/docker/distribution v2.8.2+incompatible
|
||||||
github.com/docker/docker v24.0.5+incompatible
|
github.com/docker/docker v24.0.6+incompatible
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
github.com/go-git/go-git/v5 v5.8.1
|
github.com/go-git/go-git/v5 v5.9.0
|
||||||
github.com/moby/sys/signal v0.7.0
|
github.com/moby/sys/signal v0.7.0
|
||||||
github.com/moby/term v0.5.0
|
github.com/moby/term v0.5.0
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/schollz/progressbar/v3 v3.13.1
|
github.com/schollz/progressbar/v3 v3.13.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
gotest.tools/v3 v3.5.0
|
gotest.tools/v3 v3.5.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.0.0 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
|
github.com/Microsoft/hcsshim v0.9.2 // indirect
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||||
|
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/cloudflare/circl v1.3.3 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||||
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
|
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||||
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
|
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.14.2 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||||
|
github.com/miekg/pkcs11 v1.0.3 // indirect
|
||||||
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/runc v1.1.0 // indirect
|
||||||
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
|
github.com/prometheus/common v0.42.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.10.1 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/skeema/knownhosts v1.2.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
|
golang.org/x/crypto v0.13.0 // indirect
|
||||||
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
|
golang.org/x/net v0.15.0 // indirect
|
||||||
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
|
golang.org/x/term v0.12.0 // indirect
|
||||||
|
golang.org/x/text v0.13.0 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
|
||||||
|
golang.org/x/tools v0.13.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
coopcloud.tech/libcapsul v0.0.0-20230605070824-878af473f07b
|
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
||||||
github.com/buger/goterm v1.0.4
|
github.com/buger/goterm v1.0.4
|
||||||
github.com/containerd/containerd v1.5.9 // indirect
|
github.com/containerd/containerd v1.5.9 // indirect
|
||||||
@ -35,18 +104,16 @@ require (
|
|||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.4
|
github.com/hashicorp/go-retryablehttp v0.7.4
|
||||||
github.com/hetznercloud/hcloud-go v1.48.0
|
|
||||||
github.com/klauspost/pgzip v1.2.6
|
github.com/klauspost/pgzip v1.2.6
|
||||||
github.com/libdns/gandi v1.0.2
|
|
||||||
github.com/libdns/libdns v0.2.1
|
|
||||||
github.com/moby/patternmatcher v0.5.0 // indirect
|
github.com/moby/patternmatcher v0.5.0 // indirect
|
||||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84 // indirect
|
github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
github.com/prometheus/client_golang v1.16.0 // indirect
|
||||||
github.com/sergi/go-diff v1.2.0 // indirect
|
github.com/sergi/go-diff v1.2.0 // indirect
|
||||||
github.com/spf13/cobra v1.3.0 // indirect
|
github.com/spf13/cobra v1.3.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||||
github.com/urfave/cli v1.22.9
|
github.com/urfave/cli v1.22.9
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b // indirect
|
||||||
golang.org/x/sys v0.10.0
|
golang.org/x/sys v0.12.0
|
||||||
)
|
)
|
||||||
|
150
go.sum
150
go.sum
@ -34,7 +34,6 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
|
|||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
||||||
@ -47,8 +46,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
coopcloud.tech/libcapsul v0.0.0-20230605070824-878af473f07b h1:ORxAmzrd6SSlSGm/RdPM2TSTTe5+QWp7rqzjEY/pIKA=
|
|
||||||
coopcloud.tech/libcapsul v0.0.0-20230605070824-878af473f07b/go.mod h1:6u7ekg+v+yL07QtU7E+K+WqK9LKDDqTF4s+PrIXZ+QM=
|
|
||||||
coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52 h1:cyFFOl0tKe+dVHt8saejG8xoff33eQiHxFCVzRpPUjM=
|
coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52 h1:cyFFOl0tKe+dVHt8saejG8xoff33eQiHxFCVzRpPUjM=
|
||||||
coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||||
@ -110,8 +107,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
|
|||||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs=
|
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
@ -121,16 +118,12 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/O
|
|||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||||
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
|
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
|
||||||
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||||
github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
|
||||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
|
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
@ -139,7 +132,6 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb
|
|||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||||
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
@ -170,7 +162,6 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
|
|||||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
@ -322,10 +313,10 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
|
|||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
|
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||||
@ -343,16 +334,16 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
|||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||||
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
|
github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY=
|
||||||
github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
|
||||||
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||||
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
|
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
|
||||||
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
|
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
|
||||||
@ -377,9 +368,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
|
|||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||||
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
|
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||||
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
|
||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
@ -417,29 +406,21 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H
|
|||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||||
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
|
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||||
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY=
|
||||||
github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A=
|
github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0=
|
||||||
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
|
||||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
|
||||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
|
||||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
|
||||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||||
@ -526,7 +507,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
|
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
|
||||||
@ -621,8 +601,6 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
|
|||||||
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||||
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||||
github.com/hetznercloud/hcloud-go v1.48.0 h1:b6x0ABNYJr8zVX9sE1kCNMU/sndzI8vmZjxEWEr+Gn0=
|
|
||||||
github.com/hetznercloud/hcloud-go v1.48.0/go.mod h1:VzDWThl47lOnZXY0q5/LPFD+M62pfe/52TV+mOrpp9Q=
|
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
@ -640,7 +618,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
|||||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
|
||||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
|
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
|
||||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
|
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
|
||||||
@ -650,7 +627,6 @@ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht
|
|||||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
|
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
@ -662,7 +638,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
|||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
|
||||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
@ -688,18 +663,12 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/libdns/gandi v1.0.2 h1:1Ts8UpI1x5PVKpOjKC7Dn4+EObndz9gm6vdZnloHSKQ=
|
|
||||||
github.com/libdns/gandi v1.0.2/go.mod h1:hxpbQKcQFgQrTS5lV4tAgn6QoL6HcCnoBJaW5nOW4Sk=
|
|
||||||
github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
|
||||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
|
||||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
|
||||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
@ -762,7 +731,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
|||||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||||
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
|
||||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||||
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
|
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
|
||||||
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||||
@ -789,7 +757,6 @@ github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
@ -814,6 +781,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
|||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||||
|
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
@ -856,7 +824,6 @@ github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
|
|||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@ -876,9 +843,6 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
|
|||||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
|
||||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
|
||||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
|
||||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
@ -895,9 +859,6 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
|||||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
|
||||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
|
||||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
|
||||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
@ -911,8 +872,6 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
|||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
|
||||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
|
||||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
@ -920,12 +879,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
|
||||||
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
|
|
||||||
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
|
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
@ -940,7 +895,6 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24
|
|||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
|
||||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
@ -952,7 +906,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
|||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
|
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
|
||||||
@ -996,8 +949,6 @@ github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
@ -1005,8 +956,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
|||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
@ -1050,7 +999,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
|||||||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
|
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@ -1086,7 +1034,6 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
|||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
|
||||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@ -1109,13 +1056,10 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
|
|||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
|
||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -1153,10 +1097,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -1207,23 +1150,15 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
|
||||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -1241,8 +1176,6 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ
|
|||||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
|
||||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -1254,11 +1187,10 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -1318,7 +1250,6 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -1349,7 +1280,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -1366,34 +1296,24 @@ golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
|
||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -1406,9 +1326,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -1481,9 +1400,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
|
||||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -1642,8 +1561,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||||
@ -1656,7 +1573,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
@ -1688,12 +1604,11 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
@ -1732,7 +1647,6 @@ k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAG
|
|||||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -28,12 +27,7 @@ func AppNameComplete(c *cli.Context) {
|
|||||||
|
|
||||||
// RecipeNameComplete completes recipe names.
|
// RecipeNameComplete completes recipe names.
|
||||||
func RecipeNameComplete(c *cli.Context) {
|
func RecipeNameComplete(c *cli.Context) {
|
||||||
// defaults since we can't take arguments here... this means auto-completion
|
catl, err := recipe.ReadRecipeCatalogue(false)
|
||||||
// of recipe names always access the network if e.g. the catalogue needs
|
|
||||||
// cloning / updating
|
|
||||||
conf := runtime.New()
|
|
||||||
|
|
||||||
catl, err := recipe.ReadRecipeCatalogue(conf)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warn(err)
|
logrus.Warn(err)
|
||||||
}
|
}
|
||||||
@ -74,7 +68,6 @@ func SubcommandComplete(c *cli.Context) {
|
|||||||
"autocomplete",
|
"autocomplete",
|
||||||
"catalogue",
|
"catalogue",
|
||||||
"recipe",
|
"recipe",
|
||||||
"record",
|
|
||||||
"server",
|
"server",
|
||||||
"upgrade",
|
"upgrade",
|
||||||
}
|
}
|
||||||
|
@ -8,58 +8,54 @@ import (
|
|||||||
|
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CatalogueSkipList is all the repos that are not recipes.
|
// CatalogueSkipList is all the repos that are not recipes.
|
||||||
var CatalogueSkipList = map[string]bool{
|
var CatalogueSkipList = map[string]bool{
|
||||||
"abra": true,
|
"abra": true,
|
||||||
"abra-apps": true,
|
"abra-apps": true,
|
||||||
"abra-aur": true,
|
"abra-aur": true,
|
||||||
"abra-bash": true,
|
"abra-bash": true,
|
||||||
"abra-capsul": true,
|
"abra-capsul": true,
|
||||||
"abra-gandi": true,
|
"abra-gandi": true,
|
||||||
"abra-hetzner": true,
|
"abra-hetzner": true,
|
||||||
"apps": true,
|
"abra-integration-test-recipe": true,
|
||||||
"aur-abra-git": true,
|
"apps": true,
|
||||||
"auto-recipes-catalogue-json": true,
|
"aur-abra-git": true,
|
||||||
"auto-mirror": true,
|
"auto-mirror": true,
|
||||||
"backup-bot": true,
|
"auto-recipes-catalogue-json": true,
|
||||||
"backup-bot-two": true,
|
"backup-bot": true,
|
||||||
"beta.coopcloud.tech": true,
|
"backup-bot-two": true,
|
||||||
"comrade-renovate-bot": true,
|
"beta.coopcloud.tech": true,
|
||||||
"coopcloud.tech": true,
|
"comrade-renovate-bot": true,
|
||||||
"coturn": true,
|
"coopcloud.tech": true,
|
||||||
"docker-cp-deploy": true,
|
"coturn": true,
|
||||||
"docker-dind-bats-kcov": true,
|
"docker-cp-deploy": true,
|
||||||
"docs.coopcloud.tech": true,
|
"docker-dind-bats-kcov": true,
|
||||||
"drone-abra": true,
|
"docs.coopcloud.tech": true,
|
||||||
"example": true,
|
"drone-abra": true,
|
||||||
"gardening": true,
|
"example": true,
|
||||||
"go-abra": true,
|
"gardening": true,
|
||||||
"organising": true,
|
"go-abra": true,
|
||||||
"pyabra": true,
|
"organising": true,
|
||||||
"radicle-seed-node": true,
|
"pyabra": true,
|
||||||
"recipes-catalogue-json": true,
|
"radicle-seed-node": true,
|
||||||
"recipes-wishlist": true,
|
"recipes-catalogue-json": true,
|
||||||
"recipes.coopcloud.tech": true,
|
"recipes-wishlist": true,
|
||||||
"stack-ssh-deploy": true,
|
"recipes.coopcloud.tech": true,
|
||||||
"swarm-cronjob": true,
|
"stack-ssh-deploy": true,
|
||||||
"tagcmp": true,
|
"swarm-cronjob": true,
|
||||||
"traefik-cert-dumper": true,
|
"tagcmp": true,
|
||||||
"tyop": true,
|
"traefik-cert-dumper": true,
|
||||||
|
"tyop": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureCatalogue ensures that the catalogue is cloned locally & present.
|
// 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")
|
catalogueDir := path.Join(config.ABRA_DIR, "catalogue")
|
||||||
if _, err := os.Stat(catalogueDir); err != nil && os.IsNotExist(err) {
|
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)
|
url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME)
|
||||||
if err := gitPkg.Clone(catalogueDir, url); err != nil {
|
if err := gitPkg.Clone(catalogueDir, url); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -71,9 +67,8 @@ func EnsureCatalogue(conf *runtime.Config) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureUpToDate ensures that the local catalogue has no unstaged changes as
|
// EnsureIsClean makes sure that the catalogue has no unstaged changes.
|
||||||
// is up to date. This is useful to run before doing catalogue generation.
|
func EnsureIsClean() error {
|
||||||
func EnsureUpToDate(conf *runtime.Config) error {
|
|
||||||
isClean, err := gitPkg.IsClean(config.CATALOGUE_DIR)
|
isClean, err := gitPkg.IsClean(config.CATALOGUE_DIR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -84,11 +79,11 @@ func EnsureUpToDate(conf *runtime.Config) error {
|
|||||||
return fmt.Errorf(msg, config.CATALOGUE_DIR)
|
return fmt.Errorf(msg, config.CATALOGUE_DIR)
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.Offline {
|
return nil
|
||||||
logrus.Debug("attempting to use local catalogue without access network (\"--offline\")")
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// EnsureUpToDate ensures that the local catalogue is up to date.
|
||||||
|
func EnsureUpToDate() error {
|
||||||
repo, err := git.PlainOpen(config.CATALOGUE_DIR)
|
repo, err := git.PlainOpen(config.CATALOGUE_DIR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -122,7 +122,7 @@ func UpdateLabel(pattern, serviceName, label, recipeName string) error {
|
|||||||
|
|
||||||
discovered := false
|
discovered := false
|
||||||
for oldLabel, value := range service.Deploy.Labels {
|
for oldLabel, value := range service.Deploy.Labels {
|
||||||
if strings.HasPrefix(oldLabel, "coop-cloud") {
|
if strings.HasPrefix(oldLabel, "coop-cloud") && strings.Contains(oldLabel, "version") {
|
||||||
discovered = true
|
discovered = true
|
||||||
|
|
||||||
bytes, err := ioutil.ReadFile(composeFile)
|
bytes, err := ioutil.ReadFile(composeFile)
|
||||||
|
@ -76,7 +76,7 @@ func (a App) StackName() string {
|
|||||||
func (a App) Filters(appendServiceNames, exactMatch bool) (filters.Args, error) {
|
func (a App) Filters(appendServiceNames, exactMatch bool) (filters.Args, error) {
|
||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
|
|
||||||
composeFiles, err := GetAppComposeFiles(a.Recipe, a.Env)
|
composeFiles, err := GetComposeFiles(a.Recipe, a.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return filters, err
|
return filters, err
|
||||||
}
|
}
|
||||||
@ -277,7 +277,7 @@ func GetAppServiceNames(appName string) ([]string, error) {
|
|||||||
return serviceNames, err
|
return serviceNames, err
|
||||||
}
|
}
|
||||||
|
|
||||||
composeFiles, err := GetAppComposeFiles(app.Recipe, app.Env)
|
composeFiles, err := GetComposeFiles(app.Recipe, app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return serviceNames, err
|
return serviceNames, 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))
|
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)
|
return fmt.Errorf("%s already exists?", appEnvPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,9 +437,10 @@ func GetAppStatuses(apps []App, MachineReadable bool) (map[string]map[string]str
|
|||||||
return statuses, nil
|
return statuses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAppComposeFiles gets the list of compose files for an app which should be
|
// GetComposeFiles gets the list of compose files for an app (or recipe if you
|
||||||
// merged into a composetypes.Config while respecting the COMPOSE_FILE env var.
|
// don't already have an app) which should be merged into a composetypes.Config
|
||||||
func GetAppComposeFiles(recipe string, appEnv AppEnv) ([]string, error) {
|
// while respecting the COMPOSE_FILE env var.
|
||||||
|
func GetComposeFiles(recipe string, appEnv AppEnv) ([]string, error) {
|
||||||
var composeFiles []string
|
var composeFiles []string
|
||||||
|
|
||||||
if _, ok := appEnv["COMPOSE_FILE"]; !ok {
|
if _, ok := appEnv["COMPOSE_FILE"]; !ok {
|
||||||
|
@ -14,7 +14,16 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ABRA_DIR = os.ExpandEnv("$HOME/.abra")
|
// getBaseDir retrieves the Abra base directory.
|
||||||
|
func getBaseDir() string {
|
||||||
|
home := os.ExpandEnv("$HOME/.abra")
|
||||||
|
if customAbraDir, exists := os.LookupEnv("ABRA_DIR"); exists && customAbraDir != "" {
|
||||||
|
home = customAbraDir
|
||||||
|
}
|
||||||
|
return home
|
||||||
|
}
|
||||||
|
|
||||||
|
var ABRA_DIR = getBaseDir()
|
||||||
var SERVERS_DIR = path.Join(ABRA_DIR, "servers")
|
var SERVERS_DIR = path.Join(ABRA_DIR, "servers")
|
||||||
var RECIPES_DIR = path.Join(ABRA_DIR, "recipes")
|
var RECIPES_DIR = path.Join(ABRA_DIR, "recipes")
|
||||||
var VENDOR_DIR = path.Join(ABRA_DIR, "vendor")
|
var VENDOR_DIR = path.Join(ABRA_DIR, "vendor")
|
||||||
|
@ -3,32 +3,8 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewToken constructs a new DNS provider token.
|
|
||||||
func NewToken(provider, providerTokenEnvVar string) (string, error) {
|
|
||||||
if token, present := os.LookupEnv(providerTokenEnvVar); present {
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("no %s in environment, asking via stdin", providerTokenEnvVar)
|
|
||||||
|
|
||||||
var token string
|
|
||||||
prompt := &survey.Input{
|
|
||||||
Message: fmt.Sprintf("%s API token?", provider),
|
|
||||||
}
|
|
||||||
if err := survey.AskOne(prompt, &token); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureIPv4 ensures that an ipv4 address is set for a domain name
|
// EnsureIPv4 ensures that an ipv4 address is set for a domain name
|
||||||
func EnsureIPv4(domainName string) (string, error) {
|
func EnsureIPv4(domainName string) (string, error) {
|
||||||
ipv4, err := net.ResolveIPAddr("ip", domainName)
|
ipv4, err := net.ResolveIPAddr("ip", domainName)
|
||||||
@ -72,12 +48,3 @@ func EnsureDomainsResolveSameIPv4(domainName, server string) (string, error) {
|
|||||||
|
|
||||||
return ipv4, nil
|
return ipv4, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTTL parses a ttl string into a duration
|
|
||||||
func GetTTL(ttl string) (time.Duration, error) {
|
|
||||||
val, err := time.ParseDuration(ttl)
|
|
||||||
if err != nil {
|
|
||||||
return val, err
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package gandi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"coopcloud.tech/abra/pkg/dns"
|
|
||||||
"github.com/libdns/gandi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New constructs a new DNs provider.
|
|
||||||
func New() (gandi.Provider, error) {
|
|
||||||
token, err := dns.NewToken("Gandi", "GANDI_TOKEN")
|
|
||||||
if err != nil {
|
|
||||||
return gandi.Provider{}, err
|
|
||||||
}
|
|
||||||
return gandi.Provider{APIToken: token}, nil
|
|
||||||
}
|
|
@ -191,6 +191,12 @@ func (t *JSONTable) SetAutoMergeCellsByColumnIndex(cols []int) {
|
|||||||
t.tbl.SetAutoMergeCellsByColumnIndex(cols)
|
t.tbl.SetAutoMergeCellsByColumnIndex(cols)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stuff we should implement but we just proxy for now.
|
||||||
|
func (t *JSONTable) SetAlignment(align int) {
|
||||||
|
// FIXME
|
||||||
|
t.tbl.SetAlignment(align)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *JSONTable) SetAutoMergeCells(auto bool) {
|
func (t *JSONTable) SetAutoMergeCells(auto bool) {
|
||||||
// FIXME
|
// FIXME
|
||||||
t.tbl.SetAutoMergeCells(auto)
|
t.tbl.SetAutoMergeCells(auto)
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/tagcmp"
|
"coopcloud.tech/tagcmp"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/go-git/go-git/v5"
|
"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) {
|
func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) {
|
||||||
// defaults since we can't take arguments here... this means this lint rule
|
catl, err := recipePkg.ReadRecipeCatalogue(false)
|
||||||
// always access the network if e.g. the catalogue needs cloning / updating
|
|
||||||
conf := runtime.New()
|
|
||||||
|
|
||||||
catl, err := recipePkg.ReadRecipeCatalogue(conf)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||||
"coopcloud.tech/abra/pkg/limit"
|
"coopcloud.tech/abra/pkg/limit"
|
||||||
"coopcloud.tech/abra/pkg/runtime"
|
|
||||||
"coopcloud.tech/abra/pkg/upstream/stack"
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
loader "coopcloud.tech/abra/pkg/upstream/stack"
|
loader "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"coopcloud.tech/abra/pkg/web"
|
"coopcloud.tech/abra/pkg/web"
|
||||||
@ -25,7 +24,6 @@ import (
|
|||||||
composetypes "github.com/docker/cli/cli/compose/types"
|
composetypes "github.com/docker/cli/cli/compose/types"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
gitConfig "github.com/go-git/go-git/v5/config"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -207,8 +205,8 @@ func (r Recipe) Tags() ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get retrieves a recipe.
|
// Get retrieves a recipe.
|
||||||
func Get(recipeName string, conf *runtime.Config) (Recipe, error) {
|
func Get(recipeName string, offline bool) (Recipe, error) {
|
||||||
if err := EnsureExists(recipeName, conf); err != nil {
|
if err := EnsureExists(recipeName); err != nil {
|
||||||
return Recipe{}, err
|
return Recipe{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,11 +232,12 @@ func Get(recipeName string, conf *runtime.Config) (Recipe, error) {
|
|||||||
return Recipe{}, err
|
return Recipe{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
meta, err := GetRecipeMeta(recipeName, conf)
|
meta, err := GetRecipeMeta(recipeName, offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "does not exist") {
|
switch err.(type) {
|
||||||
|
case RecipeMissingFromCatalogue:
|
||||||
meta = RecipeMeta{}
|
meta = RecipeMeta{}
|
||||||
} else {
|
default:
|
||||||
return Recipe{}, err
|
return Recipe{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,15 +249,20 @@ func Get(recipeName string, conf *runtime.Config) (Recipe, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r Recipe) SampleEnv() (map[string]string, error) {
|
||||||
|
envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
|
||||||
|
sampleEnv, err := config.ReadEnv(envSamplePath)
|
||||||
|
if err != nil {
|
||||||
|
return sampleEnv, fmt.Errorf("unable to discover .env.sample for %s", r.Name)
|
||||||
|
}
|
||||||
|
return sampleEnv, nil
|
||||||
|
}
|
||||||
|
|
||||||
// EnsureExists ensures that a recipe is locally cloned
|
// EnsureExists ensures that a recipe is locally cloned
|
||||||
func EnsureExists(recipeName string, conf *runtime.Config) error {
|
func EnsureExists(recipeName string) error {
|
||||||
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
||||||
|
|
||||||
if _, err := os.Stat(recipeDir); os.IsNotExist(err) {
|
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)
|
logrus.Debugf("%s does not exist, attemmpting to clone", recipeDir)
|
||||||
url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipeName)
|
url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipeName)
|
||||||
if err := gitPkg.Clone(recipeDir, url); err != nil {
|
if err := gitPkg.Clone(recipeDir, url); err != nil {
|
||||||
@ -277,15 +281,6 @@ func EnsureExists(recipeName string, conf *runtime.Config) error {
|
|||||||
func EnsureVersion(recipeName, version string) error {
|
func EnsureVersion(recipeName, version string) error {
|
||||||
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
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 {
|
if err := gitPkg.EnsureGitRepo(recipeDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -312,7 +307,10 @@ func EnsureVersion(recipeName, version string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("read %s as tags for recipe %s", strings.Join(parsedTags, ", "), recipeName)
|
joinedTags := strings.Join(parsedTags, ", ")
|
||||||
|
if joinedTags != "" {
|
||||||
|
logrus.Debugf("read %s as tags for recipe %s", joinedTags, recipeName)
|
||||||
|
}
|
||||||
|
|
||||||
if tagRef.String() == "" {
|
if tagRef.String() == "" {
|
||||||
return fmt.Errorf("the local copy of %s doesn't seem to have version %s available?", recipeName, version)
|
return fmt.Errorf("the local copy of %s doesn't seem to have version %s available?", recipeName, version)
|
||||||
@ -337,25 +335,31 @@ func EnsureVersion(recipeName, version string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureLatest makes sure the latest commit is checked out for a local recipe repository
|
// EnsureIsClean makes sure that the recipe repository has no unstaged changes.
|
||||||
func EnsureLatest(recipeName string, conf *runtime.Config) error {
|
func EnsureIsClean(recipeName string) error {
|
||||||
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
||||||
|
|
||||||
isClean, err := gitPkg.IsClean(recipeDir)
|
isClean, err := gitPkg.IsClean(recipeDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("unable to check git clean status in %s: %s", recipeDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isClean {
|
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 {
|
if err := gitPkg.EnsureGitRepo(recipeDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("attempting to open git repository in %s", recipeDir)
|
|
||||||
|
|
||||||
repo, err := git.PlainOpen(recipeDir)
|
repo, err := git.PlainOpen(recipeDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -366,21 +370,11 @@ func EnsureLatest(recipeName string, conf *runtime.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
meta, err := GetRecipeMeta(recipeName, conf)
|
branch, err := gitPkg.GetDefaultBranch(repo, recipeDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkOutOpts := &git.CheckoutOptions{
|
checkOutOpts := &git.CheckoutOptions{
|
||||||
Create: false,
|
Create: false,
|
||||||
Force: true,
|
Force: true,
|
||||||
@ -437,7 +431,7 @@ func GetVersionLabelLocal(recipe Recipe) (string, error) {
|
|||||||
|
|
||||||
for _, service := range recipe.Config.Services {
|
for _, service := range recipe.Config.Services {
|
||||||
for label, value := range service.Deploy.Labels {
|
for label, value := range service.Deploy.Labels {
|
||||||
if strings.HasPrefix(label, "coop-cloud") {
|
if strings.HasPrefix(label, "coop-cloud") && strings.Contains(label, "version") {
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -583,24 +577,9 @@ func GetStringInBetween(recipeName, str, start, end string) (result string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EnsureUpToDate ensures that the local repo is synced to the remote
|
// EnsureUpToDate ensures that the local repo is synced to the remote
|
||||||
func EnsureUpToDate(recipeName string, conf *runtime.Config) error {
|
func EnsureUpToDate(recipeName string) error {
|
||||||
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
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)
|
repo, err := git.PlainOpen(recipeDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to open %s: %s", recipeDir, err)
|
return fmt.Errorf("unable to open %s: %s", recipeDir, err)
|
||||||
@ -626,12 +605,7 @@ func EnsureUpToDate(recipeName string, conf *runtime.Config) error {
|
|||||||
return fmt.Errorf("unable to check out default branch in %s: %s", recipeDir, err)
|
return fmt.Errorf("unable to check out default branch in %s: %s", recipeDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchOpts := &git.FetchOptions{
|
fetchOpts := &git.FetchOptions{Tags: git.AllTags}
|
||||||
Tags: git.AllTags,
|
|
||||||
RefSpecs: []gitConfig.RefSpec{
|
|
||||||
gitConfig.RefSpec(fmt.Sprintf("%s:%s", branch, branch)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := repo.Fetch(fetchOpts); err != nil {
|
if err := repo.Fetch(fetchOpts); err != nil {
|
||||||
if !strings.Contains(err.Error(), "already up-to-date") {
|
if !strings.Contains(err.Error(), "already up-to-date") {
|
||||||
return fmt.Errorf("unable to fetch tags in %s: %s", recipeDir, err)
|
return fmt.Errorf("unable to fetch tags in %s: %s", recipeDir, err)
|
||||||
@ -656,15 +630,17 @@ func EnsureUpToDate(recipeName string, conf *runtime.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadRecipeCatalogue reads the recipe catalogue.
|
// ReadRecipeCatalogue reads the recipe catalogue.
|
||||||
func ReadRecipeCatalogue(conf *runtime.Config) (RecipeCatalogue, error) {
|
func ReadRecipeCatalogue(offline bool) (RecipeCatalogue, error) {
|
||||||
recipes := make(RecipeCatalogue)
|
recipes := make(RecipeCatalogue)
|
||||||
|
|
||||||
if err := catalogue.EnsureCatalogue(conf); err != nil {
|
if err := catalogue.EnsureCatalogue(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := catalogue.EnsureUpToDate(conf); err != nil {
|
if !offline {
|
||||||
return nil, err
|
if err := catalogue.EnsureUpToDate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := readRecipeCatalogueFS(&recipes); err != nil {
|
if err := readRecipeCatalogueFS(&recipes); err != nil {
|
||||||
@ -691,10 +667,10 @@ func readRecipeCatalogueFS(target interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VersionsOfService lists the version of a service.
|
// 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
|
var versions []string
|
||||||
|
|
||||||
catalogue, err := ReadRecipeCatalogue(conf)
|
catalogue, err := ReadRecipeCatalogue(offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -719,20 +695,26 @@ func VersionsOfService(recipe, serviceName string, conf *runtime.Config) ([]stri
|
|||||||
return versions, nil
|
return versions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecipeMissingFromCatalogue signifies a recipe is not present in the catalogue.
|
||||||
|
type RecipeMissingFromCatalogue struct{ err string }
|
||||||
|
|
||||||
|
// Error outputs the error message.
|
||||||
|
func (r RecipeMissingFromCatalogue) Error() string {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
||||||
// GetRecipeMeta retrieves the recipe metadata from the recipe catalogue.
|
// GetRecipeMeta retrieves the recipe metadata from the recipe catalogue.
|
||||||
func GetRecipeMeta(recipeName string, conf *runtime.Config) (RecipeMeta, error) {
|
func GetRecipeMeta(recipeName string, offline bool) (RecipeMeta, error) {
|
||||||
catl, err := ReadRecipeCatalogue(conf)
|
catl, err := ReadRecipeCatalogue(offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return RecipeMeta{}, err
|
return RecipeMeta{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
recipeMeta, ok := catl[recipeName]
|
recipeMeta, ok := catl[recipeName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return RecipeMeta{}, fmt.Errorf("recipe %s does not exist?", recipeName)
|
return RecipeMeta{}, RecipeMissingFromCatalogue{
|
||||||
}
|
err: fmt.Sprintf("recipe %s does not exist?", recipeName),
|
||||||
|
}
|
||||||
if err := EnsureExists(recipeName, conf); err != nil {
|
|
||||||
return RecipeMeta{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("recipe metadata retrieved for %s", recipeName)
|
logrus.Debugf("recipe metadata retrieved for %s", recipeName)
|
||||||
@ -818,11 +800,7 @@ type InternalTracker struct {
|
|||||||
type RepoCatalogue map[string]RepoMeta
|
type RepoCatalogue map[string]RepoMeta
|
||||||
|
|
||||||
// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
|
// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
|
||||||
func ReadReposMetadata(conf *runtime.Config) (RepoCatalogue, error) {
|
func ReadReposMetadata() (RepoCatalogue, error) {
|
||||||
if conf.Offline {
|
|
||||||
return nil, fmt.Errorf("network access required to query recipes metadata")
|
|
||||||
}
|
|
||||||
|
|
||||||
reposMeta := make(RepoCatalogue)
|
reposMeta := make(RepoCatalogue)
|
||||||
|
|
||||||
pageIdx := 1
|
pageIdx := 1
|
||||||
@ -857,7 +835,7 @@ func ReadReposMetadata(conf *runtime.Config) (RepoCatalogue, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRecipeVersions retrieves all recipe versions.
|
// GetRecipeVersions retrieves all recipe versions.
|
||||||
func GetRecipeVersions(recipeName string, conf *runtime.Config) (RecipeVersions, error) {
|
func GetRecipeVersions(recipeName string, offline bool) (RecipeVersions, error) {
|
||||||
versions := RecipeVersions{}
|
versions := RecipeVersions{}
|
||||||
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
|
||||||
|
|
||||||
@ -895,7 +873,7 @@ func GetRecipeVersions(recipeName string, conf *runtime.Config) (RecipeVersions,
|
|||||||
|
|
||||||
logrus.Debugf("successfully checked out %s in %s", ref.Name(), recipeDir)
|
logrus.Debugf("successfully checked out %s in %s", ref.Name(), recipeDir)
|
||||||
|
|
||||||
recipe, err := Get(recipeName, conf)
|
recipe, err := Get(recipeName, offline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1002,11 +980,7 @@ func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRepositories clones and updates all recipe repositories locally.
|
// UpdateRepositories clones and updates all recipe repositories locally.
|
||||||
func UpdateRepositories(repos RepoCatalogue, recipeName string, conf *runtime.Config) error {
|
func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
|
||||||
if conf.Offline {
|
|
||||||
return fmt.Errorf("network access required to update recipes")
|
|
||||||
}
|
|
||||||
|
|
||||||
var barLength int
|
var barLength int
|
||||||
if recipeName != "" {
|
if recipeName != "" {
|
||||||
barLength = 1
|
barLength = 1
|
||||||
@ -1040,10 +1014,6 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, conf *runtime.Co
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := EnsureUpToDate(rm.Name, conf); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ch <- rm.Name
|
ch <- rm.Name
|
||||||
retrieveBar.Add(1)
|
retrieveBar.Add(1)
|
||||||
}(repoMeta)
|
}(repoMeta)
|
||||||
|
31
pkg/recipe/recipe_test.go
Normal file
31
pkg/recipe/recipe_test.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package recipe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) {
|
||||||
|
offline := true
|
||||||
|
recipe, err := Get("traefik", offline)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < 1000; i++ {
|
||||||
|
label, err := GetVersionLabelLocal(recipe)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(d1): this is potentially quite a brittle unit test as it needs to
|
||||||
|
// hardcode the default timeout label to ensure that the label parser never
|
||||||
|
// returns it. hopefully this won't fail too often! if you're here because
|
||||||
|
// of a failure, just update the `defaultTimeoutLabel` value & permalink
|
||||||
|
// below
|
||||||
|
// https://git.coopcloud.tech/coop-cloud/traefik/src/commit/ac3a47fe8ca3ef92db84f64cfedfbb348000faee/.env.sample#L2
|
||||||
|
defaultTimeoutLabel := "300"
|
||||||
|
assert.NotEqual(t, label, defaultTimeoutLabel)
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +0,0 @@
|
|||||||
package runtime
|
|
||||||
|
|
||||||
import "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Offline bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Option func(c *Config)
|
|
||||||
|
|
||||||
func New(opts ...Option) *Config {
|
|
||||||
conf := &Config{Offline: false}
|
|
||||||
|
|
||||||
for _, optFunc := range opts {
|
|
||||||
optFunc(conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
return conf
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithOffline(offline bool) Option {
|
|
||||||
return func(c *Config) {
|
|
||||||
if offline {
|
|
||||||
logrus.Debugf("runtime config: attempting to run in offline mode")
|
|
||||||
}
|
|
||||||
c.Offline = offline
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,13 +5,14 @@ package secret
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
|
loader "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/decentral1se/passgen"
|
"github.com/decentral1se/passgen"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -60,40 +61,51 @@ func GeneratePassphrases(count uint) ([]string, error) {
|
|||||||
return passphrases, nil
|
return passphrases, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadSecretEnvVars reads secret env vars from an app env var config.
|
// ReadSecretsConfig reads secret names/versions from the recipe config. The
|
||||||
func ReadSecretEnvVars(appEnv config.AppEnv) map[string]string {
|
// function generalises appEnv/composeFiles because some times you have an app
|
||||||
secretEnvVars := make(map[string]string)
|
// and some times you don't (as the caller). We need to be able to handle the
|
||||||
|
// "app new" case where we pass in the .env.sample and the "secret generate"
|
||||||
|
// case where the app is created.
|
||||||
|
func ReadSecretsConfig(appEnv map[string]string, composeFiles []string, recipeName string) (map[string]string, error) {
|
||||||
|
secretConfigs := make(map[string]string)
|
||||||
|
|
||||||
for envVar := range appEnv {
|
opts := stack.Deploy{Composefiles: composeFiles}
|
||||||
regex := regexp.MustCompile(`^SECRET.*VERSION.*`)
|
config, err := loader.LoadComposefile(opts, appEnv)
|
||||||
if string(regex.Find([]byte(envVar))) != "" {
|
if err != nil {
|
||||||
secretEnvVars[envVar] = appEnv[envVar]
|
return secretConfigs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var enabledSecrets []string
|
||||||
|
for _, service := range config.Services {
|
||||||
|
for _, secret := range service.Secrets {
|
||||||
|
enabledSecrets = append(enabledSecrets, secret.Source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("read %s as secrets from %s", secretEnvVars, appEnv)
|
if len(enabledSecrets) == 0 {
|
||||||
|
logrus.Debugf("not generating app secrets, none enabled in recipe config")
|
||||||
|
return secretConfigs, nil
|
||||||
|
}
|
||||||
|
|
||||||
return secretEnvVars
|
for secretId, secretConfig := range config.Secrets {
|
||||||
|
if string(secretConfig.Name[len(secretConfig.Name)-1]) == "_" {
|
||||||
|
return secretConfigs, fmt.Errorf("missing version for secret? (%s)", secretId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(slices.Contains(enabledSecrets, secretId)) {
|
||||||
|
logrus.Warnf("%s not enabled in recipe config, not generating", secretId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIdx := strings.LastIndex(secretConfig.Name, "_")
|
||||||
|
secretVersion := secretConfig.Name[lastIdx+1:]
|
||||||
|
secretConfigs[secretId] = secretVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return secretConfigs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseSecretEnvVarName(secretEnvVar string) string {
|
func ParseSecretValue(secret string) (secretValue, error) {
|
||||||
withoutPrefix := strings.TrimPrefix(secretEnvVar, "SECRET_")
|
|
||||||
withoutSuffix := strings.TrimSuffix(withoutPrefix, "_VERSION")
|
|
||||||
name := strings.ToLower(withoutSuffix)
|
|
||||||
logrus.Debugf("parsed %s as name from %s", name, secretEnvVar)
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseGeneratedSecretName(secret string, appEnv config.App) string {
|
|
||||||
name := fmt.Sprintf("%s_", appEnv.StackName())
|
|
||||||
withoutAppName := strings.TrimPrefix(secret, name)
|
|
||||||
idx := strings.LastIndex(withoutAppName, "_")
|
|
||||||
parsed := withoutAppName[:idx]
|
|
||||||
logrus.Debugf("parsed %s as name from %s", parsed, secret)
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseSecretEnvVarValue(secret string) (secretValue, error) {
|
|
||||||
values := strings.Split(secret, "#")
|
values := strings.Split(secret, "#")
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
return secretValue{}, fmt.Errorf("unable to parse %s", secret)
|
return secretValue{}, fmt.Errorf("unable to parse %s", secret)
|
||||||
@ -118,30 +130,29 @@ func ParseSecretEnvVarValue(secret string) (secretValue, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateSecrets generates secrets locally and sends them to a remote server for storage.
|
// GenerateSecrets generates secrets locally and sends them to a remote server for storage.
|
||||||
func GenerateSecrets(cl *dockerClient.Client, secretEnvVars map[string]string, appName, server string) (map[string]string, error) {
|
func GenerateSecrets(cl *dockerClient.Client, secretsFromConfig map[string]string, appName, server string) (map[string]string, error) {
|
||||||
secrets := make(map[string]string)
|
secrets := make(map[string]string)
|
||||||
|
|
||||||
var mutex sync.Mutex
|
var mutex sync.Mutex
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
ch := make(chan error, len(secretEnvVars))
|
ch := make(chan error, len(secretsFromConfig))
|
||||||
for secretEnvVar := range secretEnvVars {
|
for n, v := range secretsFromConfig {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
go func(s string) {
|
go func(secretName, secretValue string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
secretName := ParseSecretEnvVarName(s)
|
parsedSecretValue, err := ParseSecretValue(secretValue)
|
||||||
secretValue, err := ParseSecretEnvVarValue(secretEnvVars[s])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ch <- err
|
ch <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
secretRemoteName := fmt.Sprintf("%s_%s_%s", appName, secretName, secretValue.Version)
|
secretRemoteName := fmt.Sprintf("%s_%s_%s", appName, secretName, parsedSecretValue.Version)
|
||||||
logrus.Debugf("attempting to generate and store %s on %s", secretRemoteName, server)
|
logrus.Debugf("attempting to generate and store %s on %s", secretRemoteName, server)
|
||||||
|
|
||||||
if secretValue.Length > 0 {
|
if parsedSecretValue.Length > 0 {
|
||||||
passwords, err := GeneratePasswords(1, uint(secretValue.Length))
|
passwords, err := GeneratePasswords(1, uint(parsedSecretValue.Length))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ch <- err
|
ch <- err
|
||||||
return
|
return
|
||||||
@ -182,12 +193,12 @@ func GenerateSecrets(cl *dockerClient.Client, secretEnvVars map[string]string, a
|
|||||||
secrets[secretName] = passphrases[0]
|
secrets[secretName] = passphrases[0]
|
||||||
}
|
}
|
||||||
ch <- nil
|
ch <- nil
|
||||||
}(secretEnvVar)
|
}(n, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
for range secretEnvVars {
|
for range secretsFromConfig {
|
||||||
err := <-ch
|
err := <-ch
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
41
pkg/secret/secret_test.go
Normal file
41
pkg/secret/secret_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
|
loader "coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadSecretsConfig(t *testing.T) {
|
||||||
|
offline := true
|
||||||
|
recipe, err := recipe.Get("matrix-synapse", offline)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleEnv, err := recipe.SampleEnv()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
composeFiles := []string{path.Join(config.RECIPES_DIR, recipe.Name, "compose.yml")}
|
||||||
|
secretsFromConfig, err := ReadSecretsConfig(sampleEnv, composeFiles, recipe.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := stack.Deploy{Composefiles: composeFiles}
|
||||||
|
config, err := loader.LoadComposefile(opts, sampleEnv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for secretId := range config.Secrets {
|
||||||
|
assert.Contains(t, secretsFromConfig, secretId)
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/upstream/convert"
|
"coopcloud.tech/abra/pkg/upstream/convert"
|
||||||
@ -414,7 +415,7 @@ func deployServices(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof("waiting for services to converge: %s", strings.Join(serviceNames, ", "))
|
logrus.Infof("Waiting for %s to deploy... please hold 🤚", appName)
|
||||||
ch := make(chan error, len(serviceIDs))
|
ch := make(chan error, len(serviceIDs))
|
||||||
for serviceID, serviceName := range serviceIDs {
|
for serviceID, serviceName := range serviceIDs {
|
||||||
logrus.Debugf("waiting on %s to converge", serviceName)
|
logrus.Debugf("waiting on %s to converge", serviceName)
|
||||||
@ -431,7 +432,7 @@ func deployServices(
|
|||||||
logrus.Debugf("assuming %s converged successfully", serviceID)
|
logrus.Debugf("assuming %s converged successfully", serviceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Info("services converged 👌")
|
logrus.Infof("Successfully deployed %s", appName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -454,6 +455,10 @@ func WaitOnService(ctx context.Context, cl *dockerClient.Client, serviceID, appN
|
|||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
pipeReader, pipeWriter := io.Pipe()
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
|
||||||
|
sigintChannel := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigintChannel, os.Interrupt)
|
||||||
|
defer signal.Stop(sigintChannel)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
errChan <- progress.ServiceProgress(ctx, cl, serviceID, pipeWriter)
|
errChan <- progress.ServiceProgress(ctx, cl, serviceID, pipeWriter)
|
||||||
}()
|
}()
|
||||||
@ -465,6 +470,12 @@ func WaitOnService(ctx context.Context, cl *dockerClient.Client, serviceID, appN
|
|||||||
select {
|
select {
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
return err
|
return err
|
||||||
|
case <-sigintChannel:
|
||||||
|
return fmt.Errorf(fmt.Sprintf(`
|
||||||
|
Not waiting for %s to deploy. The deployment is ongoing...
|
||||||
|
|
||||||
|
If you want to stop the deployment, try:
|
||||||
|
abra app undeploy %s`, appName, appName))
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
return fmt.Errorf(fmt.Sprintf(`
|
return fmt.Errorf(fmt.Sprintf(`
|
||||||
%s has not converged (%s second timeout reached).
|
%s has not converged (%s second timeout reached).
|
||||||
@ -481,7 +492,7 @@ And inspect the logs with:
|
|||||||
|
|
||||||
abra app logs %s
|
abra app logs %s
|
||||||
|
|
||||||
If a service is failing to even start, try smoke out the error with:
|
If a service is failing to even start, try to smoke out the error with:
|
||||||
|
|
||||||
abra app errors --watch %s
|
abra app errors --watch %s
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"gomodTidy"
|
"gomodTidy"
|
||||||
],
|
],
|
||||||
"ignoreDeps": [
|
"ignoreDeps": [
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli",
|
||||||
|
"goreleaser/goreleaser"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
ABRA_VERSION="0.7.0-beta"
|
ABRA_VERSION="0.8.0-beta"
|
||||||
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
|
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
|
||||||
RC_VERSION="0.8.0-rc2-beta"
|
RC_VERSION="0.8.0-beta"
|
||||||
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$RC_VERSION"
|
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$RC_VERSION"
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
|
11
scripts/release/test.sh
Executable file
11
scripts/release/test.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
for f in $(find ./tests/integration -name "*.bats"); do
|
||||||
|
bats -Tp "$f"
|
||||||
|
res=$?
|
||||||
|
if [[ "$res" -ne 0 ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
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.
|
@ -1,4 +0,0 @@
|
|||||||
GANDI_TOKEN=...
|
|
||||||
HCLOUD_TOKEN=...
|
|
||||||
REGISTRY_PASSWORD=...
|
|
||||||
REGISTRY_USERNAME=...
|
|
1
tests/integration/.gitignore
vendored
1
tests/integration/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
logs
|
|
@ -1,28 +0,0 @@
|
|||||||
# integration tests
|
|
||||||
|
|
||||||
> You need to be a member of Autonomic Co-op to run these tests, sorry!
|
|
||||||
|
|
||||||
`testfunctions.sh` contains the functions necessary to save and manipulate
|
|
||||||
logs. Run `test_all.sh logdir` to run tests specified in that file and save the
|
|
||||||
logs to `logdir`.
|
|
||||||
|
|
||||||
When creating new tests, make sure the test command is a one-liner (you can use
|
|
||||||
`;` to separate commands). Include `testfunctions.sh` and then write your tests
|
|
||||||
like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
run_test '$ABRA other stuff here'
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, the testing script will ask after every command if the execution
|
|
||||||
succeeded. If you reply `n`, it will log the test in the `logdir`. If you want
|
|
||||||
all tests to run without questions, run `export logall=yes` before executing
|
|
||||||
the test script.
|
|
||||||
|
|
||||||
To run tests, you'll need to prepare your environment:
|
|
||||||
|
|
||||||
```
|
|
||||||
cp .envrc.sample .envrc # fill out values...
|
|
||||||
direnv allow
|
|
||||||
./test_all.sh logs
|
|
||||||
```
|
|
@ -1,14 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source ./testfunctions.sh
|
|
||||||
source ./common.sh
|
|
||||||
|
|
||||||
run_test '$ABRA app ls'
|
|
||||||
|
|
||||||
run_test '$ABRA app ls --status'
|
|
||||||
|
|
||||||
run_test '$ABRA app ls --type wordpress'
|
|
||||||
|
|
||||||
run_test '$ABRA app ls --type wordpress --server swarm.autonomic.zone'
|
|
||||||
|
|
||||||
run_test '$ABRA app ls --type wordpress --server swarm.autonomic.zone --status'
|
|
173
tests/integration/app_backup.bats
Normal file
173
tests/integration/app_backup.bats
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file() {
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file() {
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 "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 backup "$TEST_APP_DOMAIN" app
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app backup "$TEST_APP_DOMAIN" app --chaos
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'running backup for the app service'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 backup "$TEST_APP_DOMAIN" --debug
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'no containers matching'
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
refute_output --partial 'behind 3'
|
||||||
|
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 backup "$TEST_APP_DOMAIN" --debug --offline
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'no containers matching'
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
_reset_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 --chaos
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'no backup config for app'
|
||||||
|
|
||||||
|
_checkout_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 --chaos
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'backup paths are empty for app?'
|
||||||
|
|
||||||
|
_checkout_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
|
||||||
|
}
|
122
tests/integration/app_check.bats
Normal file
122
tests/integration/app_check.bats
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "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 $ABRA app check "$TEST_APP_DOMAIN"
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 $ABRA app check "$TEST_APP_DOMAIN" --chaos
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 check "$TEST_APP_DOMAIN"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
refute_output --partial 'behind 3'
|
||||||
|
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ensure recipe not up to date if --offline" {
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~1
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
assert_output --partial 'behind 1'
|
||||||
|
|
||||||
|
# NOTE(d1): we can't quite tell if this will fail or not in the future, so,
|
||||||
|
# since it isn't an important part of what we're testing here, we don't check
|
||||||
|
# it
|
||||||
|
run $ABRA app check "$TEST_APP_DOMAIN" --offline
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
assert_output --partial 'behind 1'
|
||||||
|
|
||||||
|
_reset_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" --chaos
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial '.env.sample does not exist?'
|
||||||
|
|
||||||
|
_checkout_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" --chaos
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial "NEW_VAR"
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
}
|
183
tests/integration/app_cmd.bats
Normal file
183
tests/integration/app_cmd.bats
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 "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 $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd --local
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "do not bail if unstaged changes and --chaos" {
|
||||||
|
run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
|
||||||
|
run $ABRA app cmd "$TEST_APP_DOMAIN" test_cmd --local --chaos
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'baz'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 cmd "$TEST_APP_DOMAIN" test_cmd --local
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'baz'
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
refute_output --partial 'behind 3'
|
||||||
|
|
||||||
|
_reset_recipe "$TEST_RECIPE"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ensure recipe not up to date if --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 cmd "$TEST_APP_DOMAIN" test_cmd --local --offline
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'baz'
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
assert_output --partial 'behind 3'
|
||||||
|
|
||||||
|
_reset_recipe "$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 --chaos
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial "$ABRA_DIR/recipes/$TEST_RECIPE/abra.sh does not exist"
|
||||||
|
|
||||||
|
_checkout_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'
|
||||||
|
}
|
130
tests/integration/app_cp.bats
Normal file
130
tests/integration/app_cp.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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
298
tests/integration/app_deploy.bats
Normal file
298
tests/integration/app_deploy.bats
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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"
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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'
|
||||||
|
|
||||||
|
_reset_recipe
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "ensure recipe not up to date if --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'
|
||||||
|
|
||||||
|
# NOTE(d1): need to use --chaos to force same commit
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
|
--no-input --no-converge-checks --chaos --offline
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
assert_output --partial 'behind 3'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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)"
|
||||||
|
|
||||||
|
_remove_tags
|
||||||
|
|
||||||
|
# NOTE(d1): need to pass --offline to stop tags being pulled again
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
|
--no-input --no-converge-checks --offline
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestCommit"
|
||||||
|
assert_output --partial 'using latest commit'
|
||||||
|
refute_output --partial 'chaos'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
_reset_tags
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "error if specific version does not exist" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" DOESNTEXIST --no-input --no-converge-checks
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial "doesn't seem to have version DOESNTEXIST available"
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "deploy specific version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "0.2.0+1.21.0"
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
44
tests/integration/app_errors.bats
Normal file
44
tests/integration/app_errors.bats
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#!/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 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
|
||||||
|
}
|
137
tests/integration/app_list.bats
Normal file
137
tests/integration/app_list.bats
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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"
|
||||||
|
}
|
34
tests/integration/app_logs.bats
Normal file
34
tests/integration/app_logs.bats
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/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 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'
|
||||||
|
}
|
145
tests/integration/app_new.bats
Normal file
145
tests/integration/app_new.bats
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_fetch_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
_rm_app
|
||||||
|
}
|
||||||
|
|
||||||
|
@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"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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'
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 new "$TEST_RECIPE" \
|
||||||
|
--no-input \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$TEST_APP_DOMAIN"
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 new "$TEST_RECIPE" \
|
||||||
|
--no-input \
|
||||||
|
--chaos \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$TEST_APP_DOMAIN" \
|
||||||
|
--secrets
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 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 git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
refute_output --partial 'behind 3'
|
||||||
|
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ensure recipe not up to date if --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'
|
||||||
|
|
||||||
|
# NOTE(d1): need to use --chaos to force same commit
|
||||||
|
run $ABRA app new "$TEST_RECIPE" \
|
||||||
|
--no-input \
|
||||||
|
--offline \
|
||||||
|
--chaos \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$TEST_APP_DOMAIN" \
|
||||||
|
--secrets
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
|
||||||
|
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status
|
||||||
|
assert_output --partial 'behind 3'
|
||||||
|
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate secrets" {
|
||||||
|
run $ABRA app new "$TEST_RECIPE" \
|
||||||
|
--no-input \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$TEST_APP_DOMAIN" \
|
||||||
|
--secrets
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'generated secrets'
|
||||||
|
assert_output --partial 'test_pass_one'
|
||||||
|
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_pass_one'
|
||||||
|
}
|
53
tests/integration/app_ps.bats
Normal file
53
tests/integration/app_ps.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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
155
tests/integration/app_remove.bats
Normal file
155
tests/integration/app_remove.bats
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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_pass_one'
|
||||||
|
|
||||||
|
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_pass_one'
|
||||||
|
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_pass_one'
|
||||||
|
|
||||||
|
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_pass_one_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
|
||||||
|
}
|
60
tests/integration/app_restart.bats
Normal file
60
tests/integration/app_restart.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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
251
tests/integration/app_restore.bats
Normal file
251
tests/integration/app_restore.bats
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 "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"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 restore "$TEST_APP_DOMAIN" app DOESNTEXIST
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 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" --chaos --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app restore "$TEST_APP_DOMAIN" app "$BATS_TMPDIR/foo.tar.gz" \
|
||||||
|
--debug --chaos
|
||||||
|
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
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
|
||||||
|
_checkout_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 restore "$TEST_APP_DOMAIN" app DOESNTEXIST
|
||||||
|
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 restore "$TEST_APP_DOMAIN" app DOESNTEXIST --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 "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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 --chaos
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
}
|
221
tests/integration/app_rollback.bats
Normal file
221
tests/integration/app_rollback.bats
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
_reset_recipe "$TEST_RECIPE"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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" {
|
||||||
|
latestCommit="$(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
|
||||||
|
assert_output --partial "$latestCommit"
|
||||||
|
assert_output --partial 'chaos'
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" \
|
||||||
|
--chaos --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestCommit"
|
||||||
|
assert_output --partial 'chaos'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 --chaos --no-converge-checks
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'failed lint checks'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "error if not already deployed" {
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'not deployed'
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "no rollback if on oldest version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "you're on oldest"
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "error if specific version is not downgrade" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'is not a downgrade'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "rollback to previous version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.1+1.20.2'
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "0.1.0+1.20.0"
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "rollback to a version 2 tags behind" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "0.2.0+1.21.0"
|
||||||
|
|
||||||
|
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "0.1.0+1.20.0"
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
67
tests/integration/app_run.bats
Normal file
67
tests/integration/app_run.bats
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
352
tests/integration/app_secret.bats
Normal file
352
tests/integration/app_secret.bats
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_reset_app(){
|
||||||
|
run rm -rf "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
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: create secrets" {
|
||||||
|
run $ABRA app secret ls "$TEST_APP_DOMAIN"
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'test_pass_one'
|
||||||
|
assert_output --partial 'test_pass_two'
|
||||||
|
refute_output --partial 'extra_pass'
|
||||||
|
assert_output --partial 'false'
|
||||||
|
refute_output --partial 'true'
|
||||||
|
|
||||||
|
run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'test_pass_one'
|
||||||
|
assert_output --partial 'test_pass_two'
|
||||||
|
refute_output --partial 'extra_pass'
|
||||||
|
|
||||||
|
run $ABRA app secret ls "$TEST_APP_DOMAIN"
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'test_pass_one'
|
||||||
|
assert_output --partial 'test_pass_two'
|
||||||
|
refute_output --partial 'extra_pass'
|
||||||
|
refute_output --partial 'false'
|
||||||
|
assert_output --partial 'true'
|
||||||
|
|
||||||
|
run docker -c "$TEST_SERVER" secret ls
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'test_pass_one'
|
||||||
|
assert_output --partial 'test_pass_two'
|
||||||
|
refute_output --partial 'extra_pass'
|
||||||
|
|
||||||
|
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate: broken if missing version" {
|
||||||
|
run sed -i '/SECRET_TEST_PASS_ONE_VERSION=.*/d' \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'missing version'
|
||||||
|
|
||||||
|
_reset_app
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate: use version from app env" {
|
||||||
|
run sed -i 's/SECRET_TEST_PASS_ONE_VERSION=v1/SECRET_TEST_PASS_ONE_VERSION=v2/g' \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'test_pass_one'
|
||||||
|
|
||||||
|
run docker -c "$TEST_SERVER" secret ls
|
||||||
|
assert_success
|
||||||
|
assert_output --regexp ".*_test_pass_one_v2"
|
||||||
|
refute_output --regexp ".*_test_pass_one_v1"
|
||||||
|
|
||||||
|
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
_reset_app
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate: generate extra secret based on COMPOSE_FILE" {
|
||||||
|
run sed -i 's/COMPOSE_FILE="compose.yml"/COMPOSE_FILE="compose.yml:compose.extra_secret.yml"/g' \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run sed -i 's/#SECRET_EXTRA_PASS_VERSION=v1/SECRET_EXTRA_PASS_VERSION=v1/g' \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app secret generate "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'extra_pass'
|
||||||
|
|
||||||
|
run docker -c "$TEST_SERVER" secret ls
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$TEST_APP_DOMAIN_extra_pass_v1"
|
||||||
|
|
||||||
|
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
_reset_app
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate: 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 $ABRA app secret generate "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate: do not generate if not enabled" {
|
||||||
|
run sed -i '/- test_pass_one/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app secret generate "$TEST_APP_DOMAIN" --all --chaos
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'test_pass_one not enabled in recipe config'
|
||||||
|
assert_output --partial 'test_pass_two'
|
||||||
|
|
||||||
|
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all --chaos
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate: ensure secret name uses trimmed stack name" {
|
||||||
|
# NOTE(d1): 45 chars, to ensure that the app name must be trimmed
|
||||||
|
testAppDomain="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
|
|
||||||
|
run $ABRA app new "$TEST_RECIPE" \
|
||||||
|
--no-input \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$testAppDomain.$TEST_SERVER" \
|
||||||
|
--secrets \
|
||||||
|
--debug
|
||||||
|
assert_success
|
||||||
|
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$testAppDomain.$TEST_SERVER.env"
|
||||||
|
assert_output --partial "avoid runtime limits"
|
||||||
|
|
||||||
|
run $ABRA app secret rm "$testAppDomain.$TEST_SERVER" --all
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/servers/$TEST_SERVER/$testAppDomain.$TEST_SERVER.env"
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/$testAppDomain.$TEST_SERVER.env"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generate: secret length honoured" {
|
||||||
|
run bash -c '$ABRA app secret generate "$TEST_APP_DOMAIN" --all --machine \
|
||||||
|
| jq -r ".[] | select(.name==\"test_pass_two\") | .value" | awk "{print length}"'
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '10' # NOTE(d1): hardcoded # length=10 in recipe config
|
||||||
|
|
||||||
|
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_pass_one 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_pass_one
|
||||||
|
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_pass_one --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 "rm: 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 $ABRA app secret rm "$TEST_APP_DOMAIN" --all
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ls: 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 $ABRA app secret ls "$TEST_APP_DOMAIN"
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'locally unstaged changes'
|
||||||
|
|
||||||
|
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||||
|
}
|
56
tests/integration/app_services.bats
Normal file
56
tests/integration/app_services.bats
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
55
tests/integration/app_undeploy.bats
Normal file
55
tests/integration/app_undeploy.bats
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
205
tests/integration/app_upgrade.bats
Normal file
205
tests/integration/app_upgrade.bats
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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'
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "error if specific version is not an upgrade" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'is not an upgrade'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "no upgrade if lint error" {
|
||||||
|
_deploy_app
|
||||||
|
|
||||||
|
run sed -i '/traefik.enable=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" \
|
||||||
|
--no-input --no-converge-checks --chaos
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'failed lint checks'
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "no upgrade if on latest version" {
|
||||||
|
_deploy_app
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "you're on latest"
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "upgrade to latest catalogue published version" {
|
||||||
|
appDomain="custom-html.$TEST_SERVER"
|
||||||
|
|
||||||
|
run $ABRA app new custom-html \
|
||||||
|
--no-input \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$appDomain"
|
||||||
|
|
||||||
|
oneVersionBack=$(jq -r '."custom-html".versions[-2] | keys[0]' < "$ABRA_DIR/catalogue/recipes.json")
|
||||||
|
refute [ -z "$oneVersionBack" ];
|
||||||
|
|
||||||
|
run $ABRA app deploy "$appDomain" "$oneVersionBack" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$oneVersionsBack"
|
||||||
|
|
||||||
|
latestVersion=$(jq -r '."custom-html".versions[-1] | keys[0]' < "$ABRA_DIR/catalogue/recipes.json")
|
||||||
|
refute [ -z "$latestVersion" ];
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$appDomain" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestVersion"
|
||||||
|
|
||||||
|
run $ABRA app undeploy "$appDomain" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# NOTE(d1): to let the stack come down before nuking volumes
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
run $ABRA app volume remove "$appDomain" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app remove "$appDomain" --no-input
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/$appDomain.env"
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "upgrade specific version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.2.0+1.21.0'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "upgrade to latest" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
latestRelease=$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag -l | tail -n 1)
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestRelease"
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "show single release note" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.1+1.20.2'
|
||||||
|
|
||||||
|
latestRelease=$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag -l | tail -n 1)
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestRelease"
|
||||||
|
assert_output --partial 'release notes baz' # 0.2.0+1.21.0
|
||||||
|
refute_output --partial 'release notes bar' # 0.1.1+1.20.2
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "show single release note for specific version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.1+1.20.2'
|
||||||
|
assert_output --partial 'release notes bar' # 0.1.1+1.20.2
|
||||||
|
refute_output --partial 'release notes baz' # 0.2.0+1.21.0
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "show multiple release notes" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
latestRelease=$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag -l | tail -n 1)
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestRelease"
|
||||||
|
assert_output --partial 'release notes bar' # 0.1.1+1.20.2
|
||||||
|
assert_output --partial 'release notes baz' # 0.2.0+1.21.0
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "show multiple release notes for specific version" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.1.0+1.20.0'
|
||||||
|
|
||||||
|
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial '0.2.0+1.21.0'
|
||||||
|
assert_output --partial 'release notes bar' # 0.1.1+1.20.2
|
||||||
|
assert_output --partial 'release notes baz' # 0.2.0+1.21.0
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
103
tests/integration/app_version.bats
Normal file
103
tests/integration/app_version.bats
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "error if not in catalogue" {
|
||||||
|
_deploy_app
|
||||||
|
|
||||||
|
run $ABRA app version "$TEST_APP_DOMAIN"
|
||||||
|
assert_failure
|
||||||
|
assert_output --partial 'does not exist'
|
||||||
|
|
||||||
|
_undeploy_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "list version" {
|
||||||
|
appDomain="custom-html.$TEST_SERVER"
|
||||||
|
|
||||||
|
run $ABRA app new custom-html \
|
||||||
|
--no-input \
|
||||||
|
--server "$TEST_SERVER" \
|
||||||
|
--domain "$appDomain"
|
||||||
|
|
||||||
|
latestVersion=$(git -C "$ABRA_DIR/recipes/custom-html" tag | tail -n 1)
|
||||||
|
refute [ -z "$latestVersion" ];
|
||||||
|
|
||||||
|
run $ABRA app deploy "$appDomain" --no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestVersion"
|
||||||
|
|
||||||
|
run $ABRA app version "$appDomain"
|
||||||
|
assert_success
|
||||||
|
assert_output --partial "$latestVersion"
|
||||||
|
|
||||||
|
run $ABRA app undeploy "$appDomain" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# NOTE(d1): to let the stack come down before nuking volumes
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
run $ABRA app volume remove "$appDomain" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app remove "$appDomain" --no-input
|
||||||
|
assert_success
|
||||||
|
assert_not_exists "$ABRA_DIR/servers/$TEST_SERVER/$appDomain.env"
|
||||||
|
}
|
105
tests/integration/app_volume.bats
Normal file
105
tests/integration/app_volume.bats
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||||
|
_undeploy_app
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@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'
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user