Compare commits
1 Commits
main
...
deploy-uns
Author | SHA1 | Date | |
---|---|---|---|
124a91c67f |
12
.drone.yml
12
.drone.yml
@ -10,7 +10,7 @@ steps:
|
||||
- name: make test
|
||||
image: golang:1.22
|
||||
environment:
|
||||
CATL_URL: https://git.coopcloud.tech/toolshed/recipes-catalogue-json.git
|
||||
CATL_URL: https://git.coopcloud.tech/coop-cloud/recipes-catalogue-json.git
|
||||
commands:
|
||||
- mkdir -p $HOME/.abra
|
||||
- git clone $CATL_URL $HOME/.abra/catalogue
|
||||
@ -29,7 +29,7 @@ steps:
|
||||
event: tag
|
||||
|
||||
- name: release
|
||||
image: goreleaser/goreleaser:v2.5.1
|
||||
image: goreleaser/goreleaser:v1.24.0
|
||||
environment:
|
||||
GITEA_TOKEN:
|
||||
from_secret: goreleaser_gitea_token
|
||||
@ -47,10 +47,10 @@ steps:
|
||||
image: plugins/docker
|
||||
settings:
|
||||
auto_tag: true
|
||||
username: abra-bot
|
||||
username: 3wordchant
|
||||
password:
|
||||
from_secret: git_coopcloud_tech_token_abra_bot
|
||||
repo: git.coopcloud.tech/toolshed/abra
|
||||
from_secret: git_coopcloud_tech_token_3wc
|
||||
repo: git.coopcloud.tech/coop-cloud/abra
|
||||
tags: dev
|
||||
registry: git.coopcloud.tech
|
||||
when:
|
||||
@ -74,7 +74,7 @@ steps:
|
||||
request_pty: true
|
||||
script:
|
||||
- |
|
||||
wget https://git.coopcloud.tech/toolshed/abra/raw/branch/main/scripts/tests/run-ci-int -O run-ci-int
|
||||
wget https://git.coopcloud.tech/coop-cloud/abra/raw/branch/main/scripts/tests/run-ci-int -O run-ci-int
|
||||
chmod +x run-ci-int
|
||||
sh run-ci-int
|
||||
when:
|
||||
|
8
.gitea/ISSUE_TEMPLATE.md
Normal file
8
.gitea/ISSUE_TEMPLATE.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
name: "Do not use this issue tracker"
|
||||
about: "Do not use this issue tracker"
|
||||
title: "Do not use this issue tracker"
|
||||
labels: []
|
||||
---
|
||||
|
||||
Please report your issue on [`coop-cloud/organising`](https://git.coopcloud.tech/coop-cloud/organising)
|
@ -4,7 +4,6 @@
|
||||
> please do add yourself! This is a community project, let's show some 💞
|
||||
|
||||
- 3wordchant
|
||||
- ammaratef45
|
||||
- cassowary
|
||||
- codegod100
|
||||
- decentral1se
|
||||
@ -18,5 +17,3 @@
|
||||
- roxxers
|
||||
- vera
|
||||
- yksflip
|
||||
- basebuilder
|
||||
- mayel
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Build image
|
||||
FROM golang:1.22-alpine AS build
|
||||
|
||||
ENV GOPRIVATE=coopcloud.tech
|
||||
ENV GOPRIVATE coopcloud.tech
|
||||
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
|
@ -1,7 +1,7 @@
|
||||
# `abra`
|
||||
|
||||
[](https://build.coopcloud.tech/toolshed/abra)
|
||||
[](https://goreportcard.com/report/git.coopcloud.tech/toolshed/abra)
|
||||
[](https://build.coopcloud.tech/coop-cloud/abra)
|
||||
[](https://goreportcard.com/report/git.coopcloud.tech/coop-cloud/abra)
|
||||
[](https://pkg.go.dev/coopcloud.tech/abra)
|
||||
|
||||
The Co-op Cloud utility belt 🎩🐇
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var AppBackupListCommand = &cobra.Command{
|
||||
Use: "list <domain> [flags]",
|
||||
Use: "list <app> [flags]",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List the contents of a snapshot",
|
||||
Args: cobra.ExactArgs(1),
|
||||
@ -61,7 +61,7 @@ var AppBackupListCommand = &cobra.Command{
|
||||
}
|
||||
|
||||
var AppBackupDownloadCommand = &cobra.Command{
|
||||
Use: "download <domain> [flags]",
|
||||
Use: "download <app> [flags]",
|
||||
Aliases: []string{"d"},
|
||||
Short: "Download a snapshot",
|
||||
Long: `Downloads a backup.tar.gz to the current working directory.
|
||||
@ -78,7 +78,7 @@ var AppBackupDownloadCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ var AppBackupDownloadCommand = &cobra.Command{
|
||||
}
|
||||
|
||||
var AppBackupCreateCommand = &cobra.Command{
|
||||
Use: "create <domain> [flags]",
|
||||
Use: "create <app> [flags]",
|
||||
Aliases: []string{"c"},
|
||||
Short: "Create a new snapshot",
|
||||
Args: cobra.ExactArgs(1),
|
||||
@ -143,7 +143,7 @@ var AppBackupCreateCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ var AppBackupCreateCommand = &cobra.Command{
|
||||
}
|
||||
|
||||
var AppBackupSnapshotsCommand = &cobra.Command{
|
||||
Use: "snapshots <domain> [flags]",
|
||||
Use: "snapshots <app> [flags]",
|
||||
Aliases: []string{"s"},
|
||||
Short: "List all snapshots",
|
||||
Args: cobra.ExactArgs(1),
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
var AppCheckCommand = &cobra.Command{
|
||||
Use: "check <domain> [flags]",
|
||||
Use: "check <app> [flags]",
|
||||
Aliases: []string{"chk"},
|
||||
Short: "Ensure an app is well configured",
|
||||
Long: `Compare env vars in both the app ".env" and recipe ".env.sample" file.
|
||||
@ -36,7 +36,7 @@ ${FOO:<default>} syntax). "check" does not confirm or deny this for you.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -46,10 +46,7 @@ ${FOO:<default>} syntax). "check" does not confirm or deny this for you.`,
|
||||
}
|
||||
|
||||
table.
|
||||
Headers(
|
||||
fmt.Sprintf("%s .env.sample", app.Recipe.Name),
|
||||
fmt.Sprintf("%s.env", app.Name),
|
||||
).
|
||||
Headers("RECIPE ENV SAMPLE", "APP ENV").
|
||||
StyleFunc(func(row, col int) lipgloss.Style {
|
||||
switch {
|
||||
case col == 1:
|
||||
@ -74,9 +71,7 @@ ${FOO:<default>} syntax). "check" does not confirm or deny this for you.`,
|
||||
}
|
||||
}
|
||||
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var AppCmdCommand = &cobra.Command{
|
||||
Use: "command <domain> [service | --local] <cmd> [[args] [flags] | [flags] -- [args]]",
|
||||
Use: "command <app> [service | --local] <cmd> [[args] [flags] | [flags] -- [args]]",
|
||||
Aliases: []string{"cmd"},
|
||||
Short: "Run app commands",
|
||||
Long: `Run an app specific command.
|
||||
@ -92,7 +92,7 @@ does not).`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -192,14 +192,14 @@ does not).`,
|
||||
}
|
||||
|
||||
var AppCmdListCommand = &cobra.Command{
|
||||
Use: "list <domain> [flags]",
|
||||
Use: "list <app> [flags]",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List all available commands",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ func TestParseCmdArgs(t *testing.T) {
|
||||
}{
|
||||
// `--` is not parsed when passed in from the command-line e.g. -- foo bar baz
|
||||
// so we need to eumlate that as missing when testing if bash args are passed in
|
||||
// see https://git.coopcloud.tech/toolshed/organising/issues/336 for more
|
||||
// see https://git.coopcloud.tech/coop-cloud/organising/issues/336 for more
|
||||
{[]string{"foo.com", "app", "test"}, false, ""},
|
||||
{[]string{"foo.com", "app", "test", "foo"}, true, "foo "},
|
||||
{[]string{"foo.com", "app", "test", "foo", "bar", "baz"}, true, "foo bar baz "},
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
var AppConfigCommand = &cobra.Command{
|
||||
Use: "config <domain> [flags]",
|
||||
Use: "config <app> [flags]",
|
||||
Aliases: []string{"cfg"},
|
||||
Short: "Edit app config",
|
||||
Example: " abra config 1312.net",
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
)
|
||||
|
||||
var AppCpCommand = &cobra.Command{
|
||||
Use: "cp <domain> <src> <dst> [flags]",
|
||||
Use: "cp <app> <src> <dst> [flags]",
|
||||
Aliases: []string{"c"},
|
||||
Short: "Copy files to/from a deployed app service",
|
||||
Example: ` # copy myfile.txt to the root of the app service
|
||||
@ -49,7 +49,7 @@ var AppCpCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,10 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/app"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/envfile"
|
||||
@ -18,19 +18,18 @@ import (
|
||||
"coopcloud.tech/abra/pkg/lint"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var AppDeployCommand = &cobra.Command{
|
||||
Use: "deploy <domain> [version] [flags]",
|
||||
Use: "deploy <app> [version] [flags]",
|
||||
Aliases: []string{"d"},
|
||||
Short: "Deploy an app",
|
||||
Long: `Deploy an app.
|
||||
|
||||
This command supports chaos operations. Use "--chaos/-C" to deploy your recipe
|
||||
checkout as-is. Recipe commit hashes are also supported as values for
|
||||
"[version]". Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
This command supports chaos operations. Use "--chaos/-c" to deploy your recipe
|
||||
checkout as-is. Recipe commit hashes are also supported values for "[version]".
|
||||
Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
||||
Example: ` # standard deployment
|
||||
abra app deploy 1312.net
|
||||
|
||||
@ -53,8 +52,8 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
case 1:
|
||||
app, err := appPkg.Get(args[0])
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
||||
default:
|
||||
@ -62,20 +61,29 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
deployWarnMessages []string
|
||||
toDeployVersion string
|
||||
isChaosCommit bool
|
||||
toDeployChaosVersion = config.CHAOS_DEFAULT
|
||||
)
|
||||
var warnMessages []string
|
||||
|
||||
app := internal.ValidateApp(args)
|
||||
stackName := app.StackName()
|
||||
|
||||
if err := validateArgsAndFlags(args); err != nil {
|
||||
log.Fatal(err)
|
||||
ok, err := validateChaosXORVersion(args)
|
||||
if !ok {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
specificVersion := getSpecifiedVersion(args)
|
||||
|
||||
if specificVersion != "" {
|
||||
log.Debugf("overriding env file version (%s) with %s", app.Recipe.Version, specificVersion)
|
||||
app.Recipe.Version = specificVersion
|
||||
}
|
||||
|
||||
if specificVersion == "" && app.Recipe.Version != "" && !internal.Chaos {
|
||||
log.Debugf("retrieved %s as version from env file", app.Recipe.Version)
|
||||
specificVersion = app.Recipe.Version
|
||||
}
|
||||
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -83,68 +91,103 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("checking whether %s is already deployed", stackName)
|
||||
|
||||
cl, err := client.New(app.Server)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("checking whether %s is already deployed", app.StackName())
|
||||
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName())
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if deployMeta.IsDeployed && !(internal.Force || internal.Chaos) {
|
||||
log.Fatalf("%s is already deployed", app.Name)
|
||||
}
|
||||
// NOTE(d1): handles "<version> as git hash" use case
|
||||
var isChaosCommit bool
|
||||
|
||||
if len(args) == 2 && args[1] != "" {
|
||||
toDeployVersion = args[1]
|
||||
}
|
||||
// NOTE(d1): check out specific version before dealing with secrets. This
|
||||
// is because we need to deal with GetComposeFiles under the hood and these
|
||||
// files change from version to version which therefore affects which
|
||||
// secrets might be generated
|
||||
version := deployMeta.Version
|
||||
if specificVersion != "" {
|
||||
version = specificVersion
|
||||
log.Debugf("choosing %s as version to deploy", version)
|
||||
|
||||
if !deployMeta.IsDeployed &&
|
||||
toDeployVersion == "" &&
|
||||
app.Recipe.EnvVersion != "" && !internal.IgnoreEnvVersion {
|
||||
log.Debugf("new deployment, choosing .env version: %s", app.Recipe.EnvVersion)
|
||||
toDeployVersion = app.Recipe.EnvVersion
|
||||
}
|
||||
|
||||
if !internal.Chaos && toDeployVersion == "" {
|
||||
if err := getLatestVersionOrCommit(app, &toDeployVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Chaos {
|
||||
if err := getChaosVersion(app, &toDeployVersion, &toDeployChaosVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if !internal.Chaos {
|
||||
isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion)
|
||||
var err error
|
||||
isChaosCommit, err = app.Recipe.EnsureVersion(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if isChaosCommit {
|
||||
log.Debugf("assuming chaos commit: %s", toDeployVersion)
|
||||
|
||||
log.Debugf("assuming '%s' is a chaos commit", version)
|
||||
internal.Chaos = true
|
||||
toDeployChaosVersion = toDeployVersion
|
||||
}
|
||||
}
|
||||
|
||||
toDeployVersion, err = app.Recipe.GetVersionLabelLocal()
|
||||
secStats, err := secret.PollSecretsStatus(cl, app)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, secStat := range secStats {
|
||||
if !secStat.CreatedOnRemote {
|
||||
log.Fatalf("unable to deploy, secrets not generated (%s)?", secStat.LocalName)
|
||||
}
|
||||
}
|
||||
|
||||
if deployMeta.IsDeployed {
|
||||
if internal.Force || internal.Chaos {
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("%s is already deployed", app.Name))
|
||||
} else {
|
||||
log.Fatalf("%s is already deployed", app.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if !internal.Chaos && specificVersion == "" {
|
||||
versions, err := app.Recipe.Tags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(versions) > 0 && !internal.Chaos {
|
||||
version = versions[len(versions)-1]
|
||||
log.Debugf("choosing %s as version to deploy", version)
|
||||
if _, err := app.Recipe.EnsureVersion(version); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
head, err := app.Recipe.Head()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
version = formatter.SmallSHA(head.String())
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("no versions detected, using latest commit"))
|
||||
}
|
||||
}
|
||||
|
||||
chaosVersion := config.CHAOS_DEFAULT
|
||||
if internal.Chaos {
|
||||
warnMessages = append(warnMessages, "chaos mode engaged")
|
||||
|
||||
if isChaosCommit {
|
||||
chaosVersion = specificVersion
|
||||
versionLabelLocal, err := app.Recipe.GetVersionLabelLocal()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
version = versionLabelLocal
|
||||
} else {
|
||||
var err error
|
||||
chaosVersion, err = app.Recipe.ChaosVersion()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateSecrets(cl, app); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -158,7 +201,6 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
@ -171,17 +213,10 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
toDeployChaosVersionLabel := toDeployChaosVersion
|
||||
if app.Recipe.Dirty {
|
||||
toDeployChaosVersionLabel = formatter.AddDirtyMarker(toDeployChaosVersionLabel)
|
||||
}
|
||||
|
||||
appPkg.ExposeAllEnv(stackName, compose, app.Env)
|
||||
appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name)
|
||||
appPkg.SetChaosLabel(compose, stackName, internal.Chaos)
|
||||
if internal.Chaos {
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, toDeployChaosVersionLabel)
|
||||
}
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, chaosVersion)
|
||||
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
||||
|
||||
envVars, err := appPkg.CheckEnv(app)
|
||||
@ -191,43 +226,26 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
|
||||
for _, envVar := range envVars {
|
||||
if !envVar.Present {
|
||||
deployWarnMessages = append(deployWarnMessages,
|
||||
fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain),
|
||||
warnMessages = append(warnMessages,
|
||||
fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !internal.NoDomainChecks {
|
||||
if domainName, ok := app.Env["DOMAIN"]; ok {
|
||||
domainName, ok := app.Env["DOMAIN"]
|
||||
if ok {
|
||||
if _, err = dns.EnsureDomainsResolveSameIPv4(domainName, app.Server); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
log.Debug("skipping domain checks, no DOMAIN=... configured")
|
||||
warnMessages = append(warnMessages, "skipping domain checks as no DOMAIN=... configured for app")
|
||||
}
|
||||
} else {
|
||||
log.Debug("skipping domain checks")
|
||||
warnMessages = append(warnMessages, "skipping domain checks as requested")
|
||||
}
|
||||
|
||||
deployedVersion := config.NO_VERSION_DEFAULT
|
||||
if deployMeta.IsDeployed {
|
||||
deployedVersion = deployMeta.Version
|
||||
}
|
||||
|
||||
toWriteVersion := toDeployVersion
|
||||
if internal.Chaos || isChaosCommit {
|
||||
toWriteVersion = toDeployChaosVersion
|
||||
}
|
||||
|
||||
if err := internal.DeployOverview(
|
||||
app,
|
||||
deployWarnMessages,
|
||||
deployedVersion,
|
||||
deployMeta.ChaosVersion,
|
||||
toDeployVersion,
|
||||
toDeployChaosVersion,
|
||||
toWriteVersion,
|
||||
); err != nil {
|
||||
if err := internal.DeployOverview(app, warnMessages, version, chaosVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -235,8 +253,7 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout)
|
||||
log.Debugf("set waiting timeout to %d s", stack.WaitTimeout)
|
||||
|
||||
if err := stack.RunDeploy(cl, deployOpts, compose, app.Name, internal.DontWaitConverge); err != nil {
|
||||
log.Fatal(err)
|
||||
@ -250,77 +267,31 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.WriteRecipeVersion(toWriteVersion, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
app.Recipe.Version = version
|
||||
if chaosVersion != config.CHAOS_DEFAULT {
|
||||
app.Recipe.Version = chaosVersion
|
||||
}
|
||||
log.Debugf("choosing %s as version to save to env file", app.Recipe.Version)
|
||||
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getChaosVersion(app app.App, toDeployVersion, toDeployChaosVersion *string) error {
|
||||
var err error
|
||||
*toDeployChaosVersion, err = app.Recipe.ChaosVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
// validateChaosXORVersion xor checks version/chaos mode
|
||||
func validateChaosXORVersion(args []string) (bool, error) {
|
||||
if getSpecifiedVersion(args) != "" && internal.Chaos {
|
||||
return false, errors.New("cannot use <version> and --chaos together")
|
||||
}
|
||||
|
||||
*toDeployVersion, err = app.Recipe.GetVersionLabelLocal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getLatestVersionOrCommit(app app.App, toDeployVersion *string) error {
|
||||
versions, err := app.Recipe.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
// getSpecifiedVersion retrieves the specific version if available
|
||||
func getSpecifiedVersion(args []string) string {
|
||||
if len(args) >= 2 {
|
||||
return args[1]
|
||||
}
|
||||
|
||||
if len(versions) > 0 && !internal.Chaos {
|
||||
*toDeployVersion = versions[len(versions)-1]
|
||||
|
||||
log.Debugf("choosing %s as version to deploy", *toDeployVersion)
|
||||
|
||||
if _, err := app.Recipe.EnsureVersion(*toDeployVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
head, err := app.Recipe.Head()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*toDeployVersion = formatter.SmallSHA(head.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateArgsAndFlags ensures compatible args/flags.
|
||||
func validateArgsAndFlags(args []string) error {
|
||||
if len(args) == 2 && args[1] != "" && internal.Chaos {
|
||||
return fmt.Errorf("cannot use [version] and --chaos together")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateSecrets(cl *dockerClient.Client, app app.App) error {
|
||||
secStats, err := secret.PollSecretsStatus(cl, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, secStat := range secStats {
|
||||
if !secStat.CreatedOnRemote {
|
||||
return fmt.Errorf("secret not generated: %s", secStat.LocalName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -353,6 +324,6 @@ func init() {
|
||||
"no-converge-checks",
|
||||
"c",
|
||||
false,
|
||||
"disable converge logic checks",
|
||||
"do not wait for converge logic checks",
|
||||
)
|
||||
}
|
||||
|
57
cli/app/deploy_test.go
Normal file
57
cli/app/deploy_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
)
|
||||
|
||||
func TestGetSpecificVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
input []string
|
||||
expectedOutput string
|
||||
}{
|
||||
// No specified version when command has one or less args
|
||||
{[]string{}, ""},
|
||||
{[]string{"arg0"}, ""},
|
||||
// Second in arg (index-1) is the specified result when command has more than 1 args
|
||||
{[]string{"arg0", "arg1"}, "arg1"},
|
||||
{[]string{"arg0", "arg1", "arg2"}, "arg1"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.expectedOutput != getSpecifiedVersion(test.input) {
|
||||
t.Fatalf("result for %s should be %s", test.input, test.expectedOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateChaosXORVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
input []string
|
||||
isChaos bool
|
||||
expectedResult bool
|
||||
}{
|
||||
// Chaos = true, Specified Version absent
|
||||
{[]string{}, true, true},
|
||||
// Chaos = false, Specified Version absent
|
||||
{[]string{}, false, true},
|
||||
// Chaos = true, Specified Version present
|
||||
{[]string{"arg0", "arg1"}, true, false},
|
||||
// Chaos = false, Specified Version present
|
||||
{[]string{"arg0", "arg1", "arg2"}, false, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
internal.Chaos = test.isChaos
|
||||
res, _ := validateChaosXORVersion(test.input)
|
||||
if res != test.expectedResult {
|
||||
t.Fatalf(
|
||||
"When args are %s and Chaos mode is %t result needs to be %t",
|
||||
test.input,
|
||||
test.isChaos,
|
||||
test.expectedResult,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var AppEnvCommand = &cobra.Command{
|
||||
Use: "env <domain> [flags]",
|
||||
Aliases: []string{"e"},
|
||||
Short: "Show app .env values",
|
||||
Example: " abra app env 1312.net",
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return autocomplete.AppNameComplete()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
var envKeys []string
|
||||
for k := range app.Env {
|
||||
envKeys = append(envKeys, k)
|
||||
}
|
||||
|
||||
sort.Strings(envKeys)
|
||||
|
||||
var rows [][]string
|
||||
for _, k := range envKeys {
|
||||
rows = append(rows, []string{k, app.Env[k]})
|
||||
}
|
||||
|
||||
overview := formatter.CreateOverview("ENV OVERVIEW", rows)
|
||||
fmt.Println(overview)
|
||||
},
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/client"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"coopcloud.tech/abra/pkg/upstream/convert"
|
||||
composetypes "github.com/docker/cli/cli/compose/types"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var AppLabelsCommand = &cobra.Command{
|
||||
Use: "labels <domain> [flags]",
|
||||
Aliases: []string{"lb"},
|
||||
Short: "Show deployment labels",
|
||||
Long: "Both local recipe and live deployment labels are shown.",
|
||||
Example: " abra app labels 1312.net",
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return autocomplete.AppNameComplete()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cl, err := client.New(app.Server)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
remoteLabels, err := getLabels(cl, app.StackName())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rows := [][]string{
|
||||
{"DEPLOYED LABELS", "---"},
|
||||
}
|
||||
|
||||
remoteLabelKeys := make([]string, 0, len(remoteLabels))
|
||||
for k := range remoteLabels {
|
||||
remoteLabelKeys = append(remoteLabelKeys, k)
|
||||
}
|
||||
|
||||
sort.Strings(remoteLabelKeys)
|
||||
|
||||
for _, k := range remoteLabelKeys {
|
||||
rows = append(rows, []string{
|
||||
k,
|
||||
remoteLabels[k],
|
||||
})
|
||||
}
|
||||
|
||||
if len(remoteLabelKeys) == 0 {
|
||||
rows = append(rows, []string{"unknown"})
|
||||
}
|
||||
|
||||
rows = append(rows, []string{"RECIPE LABELS", "---"})
|
||||
|
||||
config, err := app.Recipe.GetComposeConfig(app.Env)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var localLabelKeys []string
|
||||
var appServiceConfig composetypes.ServiceConfig
|
||||
for _, service := range config.Services {
|
||||
if service.Name == "app" {
|
||||
appServiceConfig = service
|
||||
|
||||
for k := range service.Deploy.Labels {
|
||||
localLabelKeys = append(localLabelKeys, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(localLabelKeys)
|
||||
|
||||
for _, k := range localLabelKeys {
|
||||
rows = append(rows, []string{
|
||||
k,
|
||||
appServiceConfig.Deploy.Labels[k],
|
||||
})
|
||||
}
|
||||
|
||||
overview := formatter.CreateOverview("LABELS OVERVIEW", rows)
|
||||
fmt.Println(overview)
|
||||
},
|
||||
}
|
||||
|
||||
// getLabels reads docker labels from running services in the format of "coop-cloud.${STACK_NAME}.${LABEL}".
|
||||
func getLabels(cl *dockerClient.Client, stackName string) (map[string]string, error) {
|
||||
labels := make(map[string]string)
|
||||
|
||||
filter := filters.NewArgs()
|
||||
filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName))
|
||||
|
||||
services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter})
|
||||
if err != nil {
|
||||
return labels, err
|
||||
}
|
||||
|
||||
for _, service := range services {
|
||||
if service.Spec.Name != fmt.Sprintf("%s_app", stackName) {
|
||||
continue
|
||||
}
|
||||
|
||||
for k, v := range service.Spec.Labels {
|
||||
labels[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
AppLabelsCommand.Flags().BoolVarP(
|
||||
&internal.Chaos,
|
||||
"chaos",
|
||||
"C",
|
||||
false,
|
||||
"ignore uncommitted recipes changes",
|
||||
)
|
||||
}
|
@ -173,7 +173,7 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
||||
stats.LatestCount++
|
||||
}
|
||||
} else {
|
||||
newUpdates = internal.SortVersionsDesc(newUpdates)
|
||||
newUpdates = internal.ReverseStringList(newUpdates)
|
||||
appStats.Upgrade = strings.Join(newUpdates, "\n")
|
||||
stats.UpgradeCount++
|
||||
}
|
||||
@ -208,7 +208,7 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
||||
|
||||
serverStat := allStats[app.Server]
|
||||
|
||||
headers := []string{"RECIPE", "DOMAIN", "SERVER"}
|
||||
headers := []string{"RECIPE", "DOMAIN"}
|
||||
if status {
|
||||
headers = append(headers, []string{
|
||||
"STATUS",
|
||||
@ -228,7 +228,7 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
||||
|
||||
var rows [][]string
|
||||
for _, appStat := range serverStat.Apps {
|
||||
row := []string{appStat.Recipe, appStat.Domain, appStat.Server}
|
||||
row := []string{appStat.Recipe, appStat.Domain}
|
||||
if status {
|
||||
chaosStatus := appStat.Chaos
|
||||
if chaosStatus != "unknown" {
|
||||
@ -256,8 +256,20 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
||||
table.Rows(rows...)
|
||||
|
||||
if len(rows) > 0 {
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
fmt.Println(table)
|
||||
|
||||
if status {
|
||||
fmt.Println(fmt.Sprintf(
|
||||
"SERVER: %s | TOTAL APPS: %v | VERSIONED: %v | UNVERSIONED: %v | LATEST : %v | UPGRADE: %v",
|
||||
app.Server,
|
||||
serverStat.AppCount,
|
||||
serverStat.VersionCount,
|
||||
serverStat.UnversionedCount,
|
||||
serverStat.LatestCount,
|
||||
serverStat.UpgradeCount,
|
||||
))
|
||||
} else {
|
||||
log.Infof("SERVER: %s TOTAL APPS: %v", app.Server, serverStat.AppCount)
|
||||
}
|
||||
|
||||
if len(allStats) > 1 && len(rows) > 0 {
|
||||
@ -267,6 +279,12 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
||||
|
||||
alreadySeen[app.Server] = true
|
||||
}
|
||||
|
||||
if len(allStats) > 1 {
|
||||
totalServers := formatter.BoldStyle.Render("TOTAL SERVERS")
|
||||
totalApps := formatter.BoldStyle.Render("TOTAL APPS")
|
||||
log.Infof("%s: %v | %s: %v ", totalServers, totalServersCount, totalApps, totalAppsCount)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
@ -24,7 +23,7 @@ import (
|
||||
)
|
||||
|
||||
var AppLogsCommand = &cobra.Command{
|
||||
Use: "logs <domain> [service] [flags]",
|
||||
Use: "logs <app> [service] [flags]",
|
||||
Aliases: []string{"l"},
|
||||
Short: "Tail app logs",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
@ -38,8 +37,8 @@ var AppLogsCommand = &cobra.Command{
|
||||
case 1:
|
||||
app, err := appPkg.Get(args[0])
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
return autocomplete.ServiceNameComplete(app.Name)
|
||||
default:
|
||||
|
148
cli/app/new.go
148
cli/app/new.go
@ -25,7 +25,7 @@ This new app configuration is stored in your $ABRA_DIR directory under the
|
||||
appropriate server.
|
||||
|
||||
This command does not deploy your app for you. You will need to run "abra app
|
||||
deploy <domain>" to do so.
|
||||
deploy <app>" to do so.
|
||||
|
||||
You can see what recipes are available (i.e. values for the [recipe] argument)
|
||||
by running "abra recipe ls".
|
||||
@ -64,78 +64,53 @@ var AppNewCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
recipe := internal.ValidateRecipe(args, cmd.Name())
|
||||
|
||||
if len(args) == 2 && internal.Chaos {
|
||||
log.Fatal("cannot use [version] and --chaos together")
|
||||
}
|
||||
|
||||
var recipeVersion string
|
||||
if len(args) == 2 {
|
||||
recipeVersion = args[1]
|
||||
}
|
||||
|
||||
chaosVersion := config.CHAOS_DEFAULT
|
||||
if internal.Chaos {
|
||||
var err error
|
||||
chaosVersion, err = recipe.ChaosVersion()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// NOTE(d1): rely on tags as there is no recipe.EnvVersion yet because
|
||||
// the app has not been fully created. we rely on the local git state of
|
||||
// the repository
|
||||
tags, err := recipe.Tags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
internal.SortVersionsDesc(tags)
|
||||
|
||||
if len(tags) == 0 {
|
||||
// NOTE(d1): this is a new recipe with no released versions
|
||||
recipeVersion = config.UNKNOWN_DEFAULT
|
||||
} else {
|
||||
recipeVersion = tags[len(tags)-1]
|
||||
}
|
||||
|
||||
if err := recipe.IsDirty(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !internal.Offline && !recipe.Dirty {
|
||||
if err := recipe.EnsureUpToDate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !internal.Chaos {
|
||||
if err := recipe.EnsureIsClean(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var recipeVersions recipePkg.RecipeVersions
|
||||
if recipeVersion == "" {
|
||||
var err error
|
||||
recipeVersions, _, err = recipe.GetRecipeVersions()
|
||||
if err != nil {
|
||||
if !internal.Offline {
|
||||
if err := recipe.EnsureUpToDate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(recipeVersions) > 0 {
|
||||
latest := recipeVersions[len(recipeVersions)-1]
|
||||
for tag := range latest {
|
||||
recipeVersion = tag
|
||||
if len(args) == 2 {
|
||||
recipeVersion = args[1]
|
||||
}
|
||||
|
||||
if recipeVersion == "" {
|
||||
recipeVersions, err := recipe.GetRecipeVersions()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(recipeVersions) > 0 {
|
||||
latest := recipeVersions[len(recipeVersions)-1]
|
||||
for tag := range latest {
|
||||
recipeVersion = tag
|
||||
}
|
||||
|
||||
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
if err := recipe.EnsureLatest(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
if err := recipe.EnsureLatest(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Chaos && recipeVersion == "" {
|
||||
var err error
|
||||
recipeVersion, err = recipe.ChaosVersion()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,22 +187,39 @@ var AppNewCommand = &cobra.Command{
|
||||
newAppServer = "local"
|
||||
}
|
||||
|
||||
log.Infof("%s created successfully (version: %s, chaos: %s)", appDomain, recipeVersion, chaosVersion)
|
||||
table, err := formatter.CreateTable()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
headers := []string{"SERVER", "DOMAIN", "RECIPE", "VERSION"}
|
||||
table.Headers(headers...)
|
||||
|
||||
table.Row(newAppServer, appDomain, recipe.Name, recipeVersion)
|
||||
|
||||
log.Infof("new app '%s' created 🌞", recipe.Name)
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println(table)
|
||||
fmt.Println("")
|
||||
|
||||
fmt.Println("Configure this app:")
|
||||
fmt.Println(fmt.Sprintf("\n abra app config %s", appDomain))
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("Deploy this app:")
|
||||
fmt.Println(fmt.Sprintf("\n abra app deploy %s", appDomain))
|
||||
|
||||
if len(appSecrets) > 0 {
|
||||
rows := [][]string{}
|
||||
for k, v := range appSecrets {
|
||||
rows = append(rows, []string{k, v})
|
||||
}
|
||||
|
||||
overview := formatter.CreateOverview("SECRETS OVERVIEW", rows)
|
||||
|
||||
fmt.Println(overview)
|
||||
fmt.Println("")
|
||||
fmt.Println("Generated secrets:")
|
||||
fmt.Println("")
|
||||
fmt.Println(secretsTable)
|
||||
|
||||
log.Warnf(
|
||||
"secrets are %s shown again, please save them %s",
|
||||
formatter.BoldUnderlineStyle.Render("NOT"),
|
||||
formatter.BoldUnderlineStyle.Render("NOW"),
|
||||
"generated secrets %s shown again, please take note of them %s",
|
||||
formatter.BoldStyle.Render("NOT"),
|
||||
formatter.BoldStyle.Render("NOW"),
|
||||
)
|
||||
}
|
||||
|
||||
@ -236,17 +228,9 @@ var AppNewCommand = &cobra.Command{
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := app.Recipe.IsDirty(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
toWriteVersion := recipeVersion
|
||||
if internal.Chaos || app.Recipe.Dirty {
|
||||
toWriteVersion = chaosVersion
|
||||
}
|
||||
|
||||
if err := app.WriteRecipeVersion(toWriteVersion, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
log.Debugf("choosing %s as version to save to env file", recipeVersion)
|
||||
if err := app.WriteRecipeVersion(recipeVersion, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
appPkg "coopcloud.tech/abra/pkg/app"
|
||||
@ -23,9 +22,9 @@ import (
|
||||
)
|
||||
|
||||
var AppPsCommand = &cobra.Command{
|
||||
Use: "ps <domain> [flags]",
|
||||
Use: "ps <app> [flags]",
|
||||
Aliases: []string{"p"},
|
||||
Short: "Check app deployment status",
|
||||
Short: "Check app status",
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: func(
|
||||
cmd *cobra.Command,
|
||||
@ -36,7 +35,7 @@ var AppPsCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -58,11 +57,9 @@ var AppPsCommand = &cobra.Command{
|
||||
statuses, err := appPkg.GetAppStatuses([]appPkg.App{app}, true)
|
||||
if statusMeta, ok := statuses[app.StackName()]; ok {
|
||||
if isChaos, exists := statusMeta["chaos"]; exists && isChaos == "true" {
|
||||
if cVersion, exists := statusMeta["chaosVersion"]; exists {
|
||||
chaosVersion = cVersion
|
||||
if strings.HasSuffix(chaosVersion, config.DIRTY_DEFAULT) {
|
||||
chaosVersion = formatter.BoldDirtyDefault(chaosVersion)
|
||||
}
|
||||
chaosVersion, err = app.Recipe.ChaosVersion()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,35 +128,24 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao
|
||||
|
||||
allContainerStats[containerStats["service"]] = containerStats
|
||||
|
||||
// NOTE(d1): don't clobber these variables for --machine output
|
||||
dVersion := deployedVersion
|
||||
cVersion := chaosVersion
|
||||
|
||||
if containerStats["service"] != "app" {
|
||||
// NOTE(d1): don't repeat info which only relevant for the "app" service
|
||||
dVersion = ""
|
||||
cVersion = ""
|
||||
}
|
||||
|
||||
row := []string{
|
||||
containerStats["service"],
|
||||
containerStats["image"],
|
||||
dVersion,
|
||||
cVersion,
|
||||
containerStats["created"],
|
||||
containerStats["status"],
|
||||
containerStats["state"],
|
||||
containerStats["ports"],
|
||||
}
|
||||
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
if internal.MachineReadable {
|
||||
rendered, err := json.Marshal(allContainerStats)
|
||||
jsonstring, err := json.Marshal(allContainerStats)
|
||||
if err != nil {
|
||||
log.Fatal("unable to convert to JSON: %s", err)
|
||||
}
|
||||
|
||||
fmt.Println(string(rendered))
|
||||
|
||||
fmt.Println(string(jsonstring))
|
||||
return
|
||||
}
|
||||
|
||||
@ -171,18 +157,19 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao
|
||||
headers := []string{
|
||||
"SERVICE",
|
||||
"IMAGE",
|
||||
"VERSION",
|
||||
"CHAOS",
|
||||
"CREATED",
|
||||
"STATUS",
|
||||
"STATE",
|
||||
"PORTS",
|
||||
}
|
||||
|
||||
table.
|
||||
Headers(headers...).
|
||||
Rows(rows...)
|
||||
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
|
||||
log.Infof("VERSION: %s CHAOS: %s", deployedVersion, chaosVersion)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
var AppRemoveCommand = &cobra.Command{
|
||||
Use: "remove <domain> [flags]",
|
||||
Use: "remove <app> [flags]",
|
||||
Aliases: []string{"rm"},
|
||||
Short: "Remove all app data, locally and remotely",
|
||||
Long: `Remove everything related to an app which is already undeployed.
|
||||
|
@ -15,12 +15,12 @@ import (
|
||||
)
|
||||
|
||||
var AppRestartCommand = &cobra.Command{
|
||||
Use: "restart <domain> [[service] | --all-services] [flags]",
|
||||
Use: "restart <app> [[service] | --all-services] [flags]",
|
||||
Aliases: []string{"re"},
|
||||
Short: "Restart an app",
|
||||
Long: `This command restarts services within a deployed app.
|
||||
|
||||
Run "abra app ps <domain>" to see a list of service names.
|
||||
Run "abra app ps <app>" to see a list of service names.
|
||||
|
||||
Pass "--all-services/-a" to restart all services.`,
|
||||
Example: ` # restart a single app service
|
||||
@ -48,7 +48,7 @@ Pass "--all-services/-a" to restart all services.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(false, false); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
var AppRestoreCommand = &cobra.Command{
|
||||
Use: "restore <domain> [flags]",
|
||||
Use: "restore <app> [flags]",
|
||||
Aliases: []string{"rs"},
|
||||
Short: "Restore a snapshot",
|
||||
Long: `Snapshots are restored while apps are deployed.
|
||||
@ -28,7 +28,7 @@ Some restore scenarios may require service / app restarts.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"coopcloud.tech/abra/pkg/app"
|
||||
appPkg "coopcloud.tech/abra/pkg/app"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/envfile"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
"coopcloud.tech/abra/pkg/lint"
|
||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||
"coopcloud.tech/tagcmp"
|
||||
@ -21,23 +20,16 @@ import (
|
||||
)
|
||||
|
||||
var AppRollbackCommand = &cobra.Command{
|
||||
Use: "rollback <domain> [version] [flags]",
|
||||
Use: "rollback <app> [version] [flags]",
|
||||
Aliases: []string{"rl"},
|
||||
Short: "Roll an app back to a previous version",
|
||||
Long: `This command rolls an app back to a previous version.
|
||||
|
||||
Unlike "abra app deploy", chaos operations are not supported here. Only recipe
|
||||
versions are supported values for "[version]".
|
||||
Unlike "deploy", chaos operations are not supported here. Only recipe versions
|
||||
are supported values for "[<version>]".
|
||||
|
||||
It is possible to "--force/-f" an downgrade if you want to re-deploy a specific
|
||||
version.
|
||||
|
||||
Only the deployed version is consulted when trying to determine what downgrades
|
||||
are available. The live deployment version is the "source of truth" in this
|
||||
case. The stored .env version is not consulted.
|
||||
|
||||
A downgrade can be destructive, please ensure you have a copy of your app data
|
||||
beforehand. See "abra app backup" for more.`,
|
||||
A rollback can be destructive, please ensure you have a copy of your app data
|
||||
beforehand.`,
|
||||
Example: ` # standard rollback
|
||||
abra app rollback 1312.net
|
||||
|
||||
@ -54,8 +46,8 @@ beforehand. See "abra app backup" for more.`,
|
||||
case 1:
|
||||
app, err := appPkg.Get(args[0])
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
||||
default:
|
||||
@ -63,15 +55,26 @@ beforehand. See "abra app backup" for more.`,
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
downgradeWarnMessages []string
|
||||
chosenDowngrade string
|
||||
availableDowngrades []string
|
||||
)
|
||||
var warnMessages []string
|
||||
|
||||
app := internal.ValidateApp(args)
|
||||
stackName := app.StackName()
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
var specificVersion string
|
||||
if len(args) == 2 {
|
||||
specificVersion = args[1]
|
||||
}
|
||||
|
||||
if specificVersion != "" {
|
||||
log.Debugf("overriding env file version (%s) with %s", app.Recipe.Version, specificVersion)
|
||||
app.Recipe.Version = specificVersion
|
||||
}
|
||||
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := lint.LintForErrors(app.Recipe); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -80,13 +83,15 @@ beforehand. See "abra app backup" for more.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
deployMeta, err := ensureDeployed(cl, app)
|
||||
log.Debugf("checking whether %s is already deployed", stackName)
|
||||
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := lint.LintForErrors(app.Recipe); err != nil {
|
||||
log.Fatal(err)
|
||||
if !deployMeta.IsDeployed {
|
||||
log.Fatalf("%s is not deployed?", app.Name)
|
||||
}
|
||||
|
||||
versions, err := app.Recipe.Tags()
|
||||
@ -94,56 +99,84 @@ beforehand. See "abra app backup" for more.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// NOTE(d1): we've no idea what the live deployment version is, so every
|
||||
// possible downgrade can be shown. it's up to the user to make the choice
|
||||
if deployMeta.Version == config.UNKNOWN_DEFAULT {
|
||||
var availableDowngrades []string
|
||||
if deployMeta.Version == "unknown" {
|
||||
availableDowngrades = versions
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("failed to determine deployed version of %s", app.Name))
|
||||
}
|
||||
|
||||
if len(args) == 2 && args[1] != "" {
|
||||
chosenDowngrade = args[1]
|
||||
|
||||
if err := validateDowngradeVersionArg(chosenDowngrade, app, deployMeta); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
availableDowngrades = append(availableDowngrades, chosenDowngrade)
|
||||
}
|
||||
|
||||
if deployMeta.Version != config.UNKNOWN_DEFAULT && chosenDowngrade == "" {
|
||||
downgradeAvailable, err := ensureDowngradesAvailable(versions, &availableDowngrades, deployMeta)
|
||||
if specificVersion != "" {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatalf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)
|
||||
}
|
||||
|
||||
if !downgradeAvailable {
|
||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||
if err != nil {
|
||||
log.Fatalf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
availableDowngrades = append(availableDowngrades, specificVersion)
|
||||
}
|
||||
|
||||
if deployMeta.Version != "unknown" && specificVersion == "" {
|
||||
if deployMeta.IsChaos {
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("attempting to rollback a chaos deployment"))
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if parsedVersion.IsLessThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
availableDowngrades = append(availableDowngrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(availableDowngrades) == 0 && !internal.Force {
|
||||
log.Info("no available downgrades")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Force || internal.NoInput || chosenDowngrade != "" {
|
||||
if len(availableDowngrades) > 0 {
|
||||
var chosenDowngrade string
|
||||
if len(availableDowngrades) > 0 {
|
||||
if internal.Force || internal.NoInput || specificVersion != "" {
|
||||
chosenDowngrade = availableDowngrades[len(availableDowngrades)-1]
|
||||
}
|
||||
} else {
|
||||
if err := chooseDowngrade(availableDowngrades, deployMeta, &chosenDowngrade); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
log.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade)
|
||||
} else {
|
||||
msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version)
|
||||
if deployMeta.IsChaos {
|
||||
msg = fmt.Sprintf("please select a downgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
||||
}
|
||||
|
||||
if internal.Force &&
|
||||
chosenDowngrade == "" &&
|
||||
deployMeta.Version != config.UNKNOWN_DEFAULT {
|
||||
chosenDowngrade = deployMeta.Version
|
||||
}
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.ReverseStringList(availableDowngrades),
|
||||
}
|
||||
|
||||
if chosenDowngrade == "" {
|
||||
log.Fatal("unknown deployed version, unable to downgrade")
|
||||
if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("choosing %s as version to rollback", chosenDowngrade)
|
||||
|
||||
if _, err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -161,7 +194,6 @@ beforehand. See "abra app backup" for more.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
@ -178,9 +210,7 @@ beforehand. See "abra app backup" for more.`,
|
||||
appPkg.ExposeAllEnv(stackName, compose, app.Env)
|
||||
appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name)
|
||||
appPkg.SetChaosLabel(compose, stackName, internal.Chaos)
|
||||
if internal.Chaos {
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
||||
}
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
||||
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
||||
|
||||
chaosVersion := config.CHAOS_DEFAULT
|
||||
@ -191,13 +221,12 @@ beforehand. See "abra app backup" for more.`,
|
||||
// NOTE(d1): no release notes implemeneted for rolling back
|
||||
if err := internal.NewVersionOverview(
|
||||
app,
|
||||
downgradeWarnMessages,
|
||||
warnMessages,
|
||||
"rollback",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
chosenDowngrade,
|
||||
"",
|
||||
); err != nil {
|
||||
""); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -205,100 +234,14 @@ beforehand. See "abra app backup" for more.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := app.WriteRecipeVersion(chosenDowngrade, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
app.Recipe.Version = chosenDowngrade
|
||||
log.Debugf("choosing %s as version to save to env file", app.Recipe.Version)
|
||||
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// chooseDowngrade prompts the user to choose an downgrade interactively.
|
||||
func chooseDowngrade(
|
||||
availableDowngrades []string,
|
||||
deployMeta stack.DeployMeta,
|
||||
chosenDowngrade *string,
|
||||
) error {
|
||||
msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version)
|
||||
|
||||
if deployMeta.IsChaos {
|
||||
chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion)
|
||||
|
||||
msg = fmt.Sprintf(
|
||||
"please select a downgrade (version: %s, chaos: %s):",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.SortVersionsDesc(availableDowngrades),
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, chosenDowngrade); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateDownpgradeVersionArg validates the specific version.
|
||||
func validateDowngradeVersionArg(
|
||||
specificVersion string,
|
||||
app app.App,
|
||||
deployMeta stack.DeployMeta,
|
||||
) error {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)
|
||||
}
|
||||
|
||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) &&
|
||||
!parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureDowngradesAvailable ensures that there are available downgrades.
|
||||
func ensureDowngradesAvailable(
|
||||
versions []string,
|
||||
availableDowngrades *[]string,
|
||||
deployMeta stack.DeployMeta,
|
||||
) (bool, error) {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if parsedVersion.IsLessThan(parsedDeployedVersion) &&
|
||||
!(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
*availableDowngrades = append(*availableDowngrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(*availableDowngrades) == 0 && !internal.Force {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
AppRollbackCommand.Flags().BoolVarP(
|
||||
&internal.Force,
|
||||
@ -320,6 +263,6 @@ func init() {
|
||||
&internal.DontWaitConverge, "no-converge-checks",
|
||||
"c",
|
||||
false,
|
||||
"disable converge logic checks",
|
||||
"do not wait for converge logic checks",
|
||||
)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
var AppRunCommand = &cobra.Command{
|
||||
Use: "run <domain> <service> <cmd> [[args] [flags] | [flags] -- [args]]",
|
||||
Use: "run <app> <service> <cmd> [[args] [flags] | [flags] -- [args]]",
|
||||
Aliases: []string{"r"},
|
||||
Short: "Run a command inside a service container",
|
||||
Example: ` # run <cmd> with args/flags
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
var AppSecretGenerateCommand = &cobra.Command{
|
||||
Use: "generate <domain> [[secret] [version] | --all] [flags]",
|
||||
Use: "generate <app> [[secret] [version] | --all] [flags]",
|
||||
Aliases: []string{"g"},
|
||||
Short: "Generate secrets",
|
||||
Args: cobra.RangeArgs(1, 3),
|
||||
@ -34,8 +34,8 @@ var AppSecretGenerateCommand = &cobra.Command{
|
||||
case 1:
|
||||
app, err := appPkg.Get(args[0])
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
return autocomplete.SecretComplete(app.Recipe.Name)
|
||||
default:
|
||||
@ -45,7 +45,7 @@ var AppSecretGenerateCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -127,9 +127,7 @@ var AppSecretGenerateCommand = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
|
||||
log.Warnf(
|
||||
"generated secrets %s shown again, please take note of them %s",
|
||||
@ -140,7 +138,7 @@ var AppSecretGenerateCommand = &cobra.Command{
|
||||
}
|
||||
|
||||
var AppSecretInsertCommand = &cobra.Command{
|
||||
Use: "insert <domain> <secret> <version> <data> [flags]",
|
||||
Use: "insert <app> <secret> <version> <data> [flags]",
|
||||
Aliases: []string{"i"},
|
||||
Short: "Insert secret",
|
||||
Long: `This command inserts a secret into an app environment.
|
||||
@ -159,8 +157,8 @@ environment. Typically, you can let Abra generate them for you on app creation
|
||||
case 1:
|
||||
app, err := appPkg.Get(args[0])
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
return autocomplete.SecretComplete(app.Recipe.Name)
|
||||
default:
|
||||
@ -170,7 +168,7 @@ environment. Typically, you can let Abra generate them for you on app creation
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -230,7 +228,7 @@ func secretRm(cl *dockerClient.Client, app appPkg.App, secretName, parsed string
|
||||
}
|
||||
|
||||
var AppSecretRmCommand = &cobra.Command{
|
||||
Use: "remove <domain> [[secret] | --all] [flags]",
|
||||
Use: "remove <app> [[secret] | --all] [flags]",
|
||||
Aliases: []string{"rm"},
|
||||
Short: "Remove a secret",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
@ -245,8 +243,8 @@ var AppSecretRmCommand = &cobra.Command{
|
||||
if !rmAllSecrets {
|
||||
app, err := appPkg.Get(args[0])
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
return autocomplete.SecretComplete(app.Recipe.Name)
|
||||
}
|
||||
@ -258,7 +256,7 @@ var AppSecretRmCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -338,7 +336,7 @@ var AppSecretRmCommand = &cobra.Command{
|
||||
}
|
||||
|
||||
var AppSecretLsCommand = &cobra.Command{
|
||||
Use: "list <domain>",
|
||||
Use: "list <app>",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List all secrets",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
@ -351,7 +349,7 @@ var AppSecretLsCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -396,10 +394,7 @@ var AppSecretLsCommand = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(table)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
var AppServicesCommand = &cobra.Command{
|
||||
Use: "services <domain> [flags]",
|
||||
Use: "services <app> [flags]",
|
||||
Aliases: []string{"sr"},
|
||||
Short: "Display all services of an app",
|
||||
Args: cobra.ExactArgs(1),
|
||||
@ -30,7 +30,7 @@ var AppServicesCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
app := internal.ValidateApp(args)
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ var AppServicesCommand = &cobra.Command{
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
headers := []string{"SERVICE (SHORT)", "SERVICE (LONG)"}
|
||||
headers := []string{"SERVICE (SHORT)", "SERVICE (LONG)", "IMAGE"}
|
||||
table.Headers(headers...)
|
||||
|
||||
var rows [][]string
|
||||
@ -80,6 +80,7 @@ var AppServicesCommand = &cobra.Command{
|
||||
row := []string{
|
||||
serviceShortName,
|
||||
serviceLongName,
|
||||
formatter.RemoveSha(container.Image),
|
||||
}
|
||||
|
||||
rows = append(rows, row)
|
||||
@ -88,9 +89,7 @@ var AppServicesCommand = &cobra.Command{
|
||||
table.Rows(rows...)
|
||||
|
||||
if len(rows) > 0 {
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ import (
|
||||
)
|
||||
|
||||
var AppUndeployCommand = &cobra.Command{
|
||||
Use: "undeploy <domain> [flags]",
|
||||
Use: "undeploy <app> [flags]",
|
||||
Aliases: []string{"un"},
|
||||
Short: "Undeploy an app",
|
||||
Long: `This does not destroy any application data.
|
||||
Long: `This does not destroy any of the application data.
|
||||
|
||||
However, you should remain vigilant, as your swarm installation will consider
|
||||
any previously attached volumes as eligible for pruning once undeployed.
|
||||
@ -59,22 +59,12 @@ Passing "--prune/-p" does not remove those volumes.`,
|
||||
chaosVersion = deployMeta.ChaosVersion
|
||||
}
|
||||
|
||||
toWriteVersion := deployMeta.Version
|
||||
if deployMeta.IsChaos {
|
||||
toWriteVersion = chaosVersion
|
||||
}
|
||||
|
||||
if err := internal.UndeployOverview(
|
||||
app,
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
toWriteVersion,
|
||||
); err != nil {
|
||||
if err := internal.DeployOverview(app, []string{}, deployMeta.Version, chaosVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rmOpts := stack.Remove{
|
||||
Namespaces: []string{stackName},
|
||||
Namespaces: []string{app.StackName()},
|
||||
Detach: false,
|
||||
}
|
||||
if err := stack.RunRemove(context.Background(), cl, rmOpts); err != nil {
|
||||
@ -86,10 +76,6 @@ Passing "--prune/-p" does not remove those volumes.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.WriteRecipeVersion(toWriteVersion, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -5,40 +5,30 @@ import (
|
||||
"fmt"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/app"
|
||||
appPkg "coopcloud.tech/abra/pkg/app"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/client"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/envfile"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
"coopcloud.tech/abra/pkg/lint"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||
"coopcloud.tech/tagcmp"
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var AppUpgradeCommand = &cobra.Command{
|
||||
Use: "upgrade <domain> [version] [flags]",
|
||||
Use: "upgrade <app> [version] [flags]",
|
||||
Aliases: []string{"up"},
|
||||
Short: "Upgrade an app",
|
||||
Long: `Upgrade an app.
|
||||
|
||||
Unlike "abra app deploy", chaos operations are not supported here. Only recipe
|
||||
versions are supported values for "[version]".
|
||||
|
||||
It is possible to "--force/-f" an upgrade if you want to re-deploy a specific
|
||||
version.
|
||||
|
||||
Only the deployed version is consulted when trying to determine what upgrades
|
||||
are available. The live deployment version is the "source of truth" in this
|
||||
case. The stored .env version is not consulted.
|
||||
Unlike "deploy", chaos operations are not supported here. Only recipe versions
|
||||
are supported values for "[version]".
|
||||
|
||||
An upgrade can be destructive, please ensure you have a copy of your app data
|
||||
beforehand. See "abra app backup" for more.`,
|
||||
beforehand.`,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
ValidArgsFunction: func(
|
||||
cmd *cobra.Command,
|
||||
@ -50,8 +40,8 @@ beforehand. See "abra app backup" for more.`,
|
||||
case 1:
|
||||
app, err := appPkg.Get(args[0])
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
||||
default:
|
||||
@ -59,26 +49,22 @@ beforehand. See "abra app backup" for more.`,
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
upgradeWarnMessages []string
|
||||
chosenUpgrade string
|
||||
availableUpgrades []string
|
||||
upgradeReleaseNotes string
|
||||
)
|
||||
var warnMessages []string
|
||||
|
||||
app := internal.ValidateApp(args)
|
||||
stackName := app.StackName()
|
||||
|
||||
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
log.Fatal(err)
|
||||
var specificVersion string
|
||||
if len(args) == 2 {
|
||||
specificVersion = args[1]
|
||||
}
|
||||
|
||||
cl, err := client.New(app.Server)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
if specificVersion != "" {
|
||||
log.Debugf("overriding env file version (%s) with %s", app.Recipe.Version, specificVersion)
|
||||
app.Recipe.Version = specificVersion
|
||||
}
|
||||
|
||||
deployMeta, err := ensureDeployed(cl, app)
|
||||
if err != nil {
|
||||
if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -86,69 +72,134 @@ beforehand. See "abra app backup" for more.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("checking whether %s is already deployed", stackName)
|
||||
|
||||
cl, err := client.New(app.Server)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !deployMeta.IsDeployed {
|
||||
log.Fatalf("%s is not deployed?", app.Name)
|
||||
}
|
||||
|
||||
versions, err := app.Recipe.Tags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// NOTE(d1): we've no idea what the live deployment version is, so every
|
||||
// possible upgrade can be shown. it's up to the user to make the choice
|
||||
if deployMeta.Version == config.UNKNOWN_DEFAULT {
|
||||
var availableUpgrades []string
|
||||
if deployMeta.Version == "unknown" {
|
||||
availableUpgrades = versions
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("failed to determine deployed version of %s", app.Name))
|
||||
}
|
||||
|
||||
if len(args) == 2 && args[1] != "" {
|
||||
chosenUpgrade = args[1]
|
||||
|
||||
if err := validateUpgradeVersionArg(chosenUpgrade, app, deployMeta); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
availableUpgrades = append(availableUpgrades, chosenUpgrade)
|
||||
}
|
||||
|
||||
if deployMeta.Version != config.UNKNOWN_DEFAULT && chosenUpgrade == "" {
|
||||
upgradeAvailable, err := ensureUpgradesAvailable(versions, &availableUpgrades, deployMeta)
|
||||
if specificVersion != "" {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatalf("'%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)
|
||||
}
|
||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||
if err != nil {
|
||||
log.Fatalf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||
}
|
||||
|
||||
if !upgradeAvailable {
|
||||
if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
availableUpgrades = append(availableUpgrades, specificVersion)
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if deployMeta.Version != "unknown" && specificVersion == "" {
|
||||
if deployMeta.IsChaos {
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("attempting to upgrade a chaos deployment"))
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
availableUpgrades = append(availableUpgrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(availableUpgrades) == 0 && !internal.Force {
|
||||
log.Info("no available upgrades")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Force || internal.NoInput || chosenUpgrade != "" {
|
||||
if len(availableUpgrades) > 0 {
|
||||
var chosenUpgrade string
|
||||
if len(availableUpgrades) > 0 {
|
||||
if internal.Force || internal.NoInput || specificVersion != "" {
|
||||
chosenUpgrade = availableUpgrades[len(availableUpgrades)-1]
|
||||
}
|
||||
} else {
|
||||
if err := chooseUpgrade(availableUpgrades, deployMeta, &chosenUpgrade); err != nil {
|
||||
log.Fatal(err)
|
||||
log.Debugf("choosing %s as version to upgrade to", chosenUpgrade)
|
||||
} else {
|
||||
msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version)
|
||||
if deployMeta.IsChaos {
|
||||
msg = fmt.Sprintf("please select an upgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.ReverseStringList(availableUpgrades),
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, &chosenUpgrade); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Force &&
|
||||
chosenUpgrade == "" &&
|
||||
deployMeta.Version != config.UNKNOWN_DEFAULT {
|
||||
if internal.Force && chosenUpgrade == "" {
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("%s is already upgraded to latest", app.Name))
|
||||
chosenUpgrade = deployMeta.Version
|
||||
}
|
||||
|
||||
if chosenUpgrade == "" {
|
||||
log.Fatal("unknown deployed version, unable to upgrade")
|
||||
// if release notes written after git tag published, read them before we
|
||||
// check out the tag and then they'll appear to be missing. this covers
|
||||
// when we obviously will forget to write release notes before publishing
|
||||
var releaseNotes string
|
||||
if chosenUpgrade != "" {
|
||||
parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, version := range versions {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) && parsedVersion.IsLessThan(parsedChosenUpgrade) {
|
||||
note, err := app.Recipe.GetReleaseNotes(version)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if note != "" {
|
||||
releaseNotes += fmt.Sprintf("%s\n", note)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("choosing %s as version to upgrade", chosenUpgrade)
|
||||
|
||||
// NOTE(d1): if release notes written after git tag published, read them
|
||||
// before we check out the tag and then they'll appear to be missing. this
|
||||
// covers when we obviously will forget to write release notes before
|
||||
// publishing
|
||||
if err := getReleaseNotes(app, versions, chosenUpgrade, deployMeta, &upgradeReleaseNotes); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := app.Recipe.EnsureVersion(chosenUpgrade); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -166,7 +217,6 @@ beforehand. See "abra app backup" for more.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
@ -183,9 +233,7 @@ beforehand. See "abra app backup" for more.`,
|
||||
appPkg.ExposeAllEnv(stackName, compose, app.Env)
|
||||
appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name)
|
||||
appPkg.SetChaosLabel(compose, stackName, internal.Chaos)
|
||||
if internal.Chaos {
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
|
||||
}
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
|
||||
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
||||
|
||||
envVars, err := appPkg.CheckEnv(app)
|
||||
@ -195,35 +243,31 @@ beforehand. See "abra app backup" for more.`,
|
||||
|
||||
for _, envVar := range envVars {
|
||||
if !envVar.Present {
|
||||
upgradeWarnMessages = append(upgradeWarnMessages,
|
||||
fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain),
|
||||
warnMessages = append(warnMessages,
|
||||
fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if showReleaseNotes {
|
||||
fmt.Print(upgradeReleaseNotes)
|
||||
fmt.Println()
|
||||
fmt.Print(releaseNotes)
|
||||
return
|
||||
}
|
||||
|
||||
chaosVersion := config.CHAOS_DEFAULT
|
||||
if deployMeta.IsChaos {
|
||||
chaosVersion = deployMeta.ChaosVersion
|
||||
|
||||
if deployMeta.ChaosVersion == "" {
|
||||
chaosVersion = config.UNKNOWN_DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
if err := internal.NewVersionOverview(
|
||||
app,
|
||||
upgradeWarnMessages,
|
||||
warnMessages,
|
||||
"upgrade",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
chosenUpgrade,
|
||||
upgradeReleaseNotes,
|
||||
); err != nil {
|
||||
releaseNotes); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -231,8 +275,7 @@ beforehand. See "abra app backup" for more.`,
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout)
|
||||
log.Debugf("set waiting timeout to %d s", stack.WaitTimeout)
|
||||
|
||||
if err := stack.RunDeploy(cl, deployOpts, compose, stackName, internal.DontWaitConverge); err != nil {
|
||||
log.Fatal(err)
|
||||
@ -241,162 +284,19 @@ beforehand. See "abra app backup" for more.`,
|
||||
postDeployCmds, ok := app.Env["POST_UPGRADE_CMDS"]
|
||||
if ok && !internal.DontWaitConverge {
|
||||
log.Debugf("run the following post-deploy commands: %s", postDeployCmds)
|
||||
|
||||
if err := internal.PostCmds(cl, app, postDeployCmds); err != nil {
|
||||
log.Fatalf("attempting to run post deploy commands, saw: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.WriteRecipeVersion(chosenUpgrade, false); err != nil {
|
||||
log.Fatalf("writing recipe version failed: %s", err)
|
||||
app.Recipe.Version = chosenUpgrade
|
||||
log.Debugf("choosing %s as version to save to env file", app.Recipe.Version)
|
||||
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// chooseUpgrade prompts the user to choose an upgrade interactively.
|
||||
func chooseUpgrade(
|
||||
availableUpgrades []string,
|
||||
deployMeta stack.DeployMeta,
|
||||
chosenUpgrade *string,
|
||||
) error {
|
||||
msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version)
|
||||
|
||||
if deployMeta.IsChaos {
|
||||
chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion)
|
||||
|
||||
msg = fmt.Sprintf(
|
||||
"please select an upgrade (version: %s, chaos: %s):",
|
||||
deployMeta.Version,
|
||||
chaosVersion,
|
||||
)
|
||||
}
|
||||
|
||||
prompt := &survey.Select{
|
||||
Message: msg,
|
||||
Options: internal.SortVersionsDesc(availableUpgrades),
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, chosenUpgrade); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getReleaseNotes(
|
||||
app app.App,
|
||||
versions []string,
|
||||
chosenUpgrade string,
|
||||
deployMeta stack.DeployMeta,
|
||||
upgradeReleaseNotes *string,
|
||||
) error {
|
||||
parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, version := range internal.SortVersionsDesc(versions) {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) &&
|
||||
parsedVersion.IsLessThan(parsedChosenUpgrade) {
|
||||
note, err := app.Recipe.GetReleaseNotes(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if note != "" {
|
||||
*upgradeReleaseNotes += fmt.Sprintf("%s\n", note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureUpgradesAvailable ensures that there are available upgrades.
|
||||
func ensureUpgradesAvailable(
|
||||
versions []string,
|
||||
availableUpgrades *[]string,
|
||||
deployMeta stack.DeployMeta,
|
||||
) (bool, error) {
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
parsedVersion, err := tagcmp.Parse(version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) &&
|
||||
!(parsedVersion.Equals(parsedDeployedVersion)) {
|
||||
*availableUpgrades = append(*availableUpgrades, version)
|
||||
}
|
||||
}
|
||||
|
||||
if len(*availableUpgrades) == 0 && !internal.Force {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// validateUpgradeVersionArg validates the specific version.
|
||||
func validateUpgradeVersionArg(
|
||||
specificVersion string,
|
||||
app app.App,
|
||||
deployMeta stack.DeployMeta,
|
||||
) error {
|
||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) &&
|
||||
!parsedSpecificVersion.Equals(parsedDeployedVersion) {
|
||||
return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force {
|
||||
return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureDeployed ensures the app is deployed and if so, returns deployment
|
||||
// meta info.
|
||||
func ensureDeployed(cl *dockerClient.Client, app app.App) (stack.DeployMeta, error) {
|
||||
log.Debugf("checking whether %s is already deployed", app.StackName())
|
||||
|
||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName())
|
||||
if err != nil {
|
||||
return stack.DeployMeta{}, err
|
||||
}
|
||||
|
||||
if !deployMeta.IsDeployed {
|
||||
return stack.DeployMeta{}, fmt.Errorf("%s is not deployed?", app.Name)
|
||||
}
|
||||
|
||||
return deployMeta, nil
|
||||
}
|
||||
|
||||
var (
|
||||
showReleaseNotes bool
|
||||
)
|
||||
@ -422,7 +322,7 @@ func init() {
|
||||
&internal.DontWaitConverge, "no-converge-checks",
|
||||
"c",
|
||||
false,
|
||||
"disable converge logic checks",
|
||||
"do not wait for converge logic checks",
|
||||
)
|
||||
|
||||
AppUpgradeCommand.Flags().BoolVarP(
|
||||
|
@ -2,6 +2,7 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
@ -14,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
var AppVolumeListCommand = &cobra.Command{
|
||||
Use: "list <domain> [flags]",
|
||||
Use: "list <app> [flags]",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List volumes associated with an app",
|
||||
Args: cobra.ExactArgs(1),
|
||||
@ -42,7 +43,7 @@ var AppVolumeListCommand = &cobra.Command{
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
headers := []string{"NAME", "ON SERVER"}
|
||||
headers := []string{"name", "created", "mounted"}
|
||||
|
||||
table, err := formatter.CreateTable()
|
||||
if err != nil {
|
||||
@ -53,16 +54,14 @@ var AppVolumeListCommand = &cobra.Command{
|
||||
|
||||
var rows [][]string
|
||||
for _, volume := range volumes {
|
||||
row := []string{volume.Name, volume.Mountpoint}
|
||||
row := []string{volume.Name, volume.CreatedAt, volume.Mountpoint}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
table.Rows(rows...)
|
||||
|
||||
if len(rows) > 0 {
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
return
|
||||
}
|
||||
|
||||
@ -71,12 +70,12 @@ var AppVolumeListCommand = &cobra.Command{
|
||||
}
|
||||
|
||||
var AppVolumeRemoveCommand = &cobra.Command{
|
||||
Use: "remove <domain> [flags]",
|
||||
Use: "remove <app> [flags]",
|
||||
Short: "Remove volume(s) associated with an app",
|
||||
Long: `Remove volumes associated with an app.
|
||||
|
||||
The app in question must be undeployed before you try to remove volumes. See
|
||||
"abra app undeploy <domain>" for more.
|
||||
"abra app undeploy <app>" for more.
|
||||
|
||||
The command is interactive and will show a multiple select input which allows
|
||||
you to make a seclection. Use the "?" key to see more help on navigating this
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"slices"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
@ -25,17 +24,12 @@ var CatalogueGenerateCommand = &cobra.Command{
|
||||
Short: "Generate the recipe catalogue",
|
||||
Long: `Generate a new copy of the recipe catalogue.
|
||||
|
||||
N.B. this command **will** wipe local unstaged changes from your local recipes
|
||||
if present. "--chaos/-C" on this command refers to the catalogue repository
|
||||
("$ABRA_DIR/catalogue") and not the recipes. Please take care not to lose your
|
||||
changes.
|
||||
|
||||
It is possible to generate new metadata for a single recipe by passing
|
||||
[recipe]. The existing local catalogue will be updated, not overwritten.
|
||||
|
||||
It is quite easy to get rate limited by Docker Hub when running this command.
|
||||
If you have a Hub account you can "docker login" and Abra will automatically
|
||||
use those details.
|
||||
If you have a Hub account you can have Abra log you in to avoid this. Pass
|
||||
"--user" and "--pass".
|
||||
|
||||
Push your new release to git.coopcloud.tech with "--publish/-p". This requires
|
||||
that you have permission to git push to these repositories and have your SSH
|
||||
@ -53,62 +47,56 @@ keys configured on your account.`,
|
||||
recipeName = args[0]
|
||||
}
|
||||
|
||||
r := recipe.Get(recipeName)
|
||||
|
||||
if recipeName != "" {
|
||||
internal.ValidateRecipe(args, cmd.Name())
|
||||
}
|
||||
|
||||
if err := catalogue.EnsureCatalogue(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !internal.Chaos {
|
||||
if err := catalogue.EnsureIsClean(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
repos, err := recipe.ReadReposMetadata(internal.Debug)
|
||||
repos, err := recipe.ReadReposMetadata()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
barLength := len(repos)
|
||||
var barLength int
|
||||
var logMsg string
|
||||
if recipeName != "" {
|
||||
barLength = 1
|
||||
logMsg = fmt.Sprintf("ensuring %v recipe is cloned & up-to-date", barLength)
|
||||
} else {
|
||||
barLength = len(repos)
|
||||
logMsg = fmt.Sprintf("ensuring %v recipes are cloned & up-to-date, this could take some time...", barLength)
|
||||
}
|
||||
|
||||
if !skipUpdates {
|
||||
if err := recipe.UpdateRepositories(repos, recipeName, internal.Debug); err != nil {
|
||||
log.Warn(logMsg)
|
||||
if err := recipe.UpdateRepositories(repos, recipeName); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var warnings []string
|
||||
catl := make(recipe.RecipeCatalogue)
|
||||
catlBar := formatter.CreateProgressbar(barLength, "collecting catalogue metadata")
|
||||
catlBar := formatter.CreateProgressbar(barLength, "generating catalogue metadata...")
|
||||
for _, recipeMeta := range repos {
|
||||
if recipeName != "" && recipeName != recipeMeta.Name {
|
||||
if !internal.Debug {
|
||||
catlBar.Add(1)
|
||||
}
|
||||
catlBar.Add(1)
|
||||
continue
|
||||
}
|
||||
|
||||
r := recipe.Get(recipeMeta.Name)
|
||||
versions, warnMsgs, err := r.GetRecipeVersions()
|
||||
versions, err := r.GetRecipeVersions()
|
||||
if err != nil {
|
||||
warnings = append(warnings, err.Error())
|
||||
}
|
||||
if len(warnMsgs) > 0 {
|
||||
warnings = append(warnings, warnMsgs...)
|
||||
log.Warn(err)
|
||||
}
|
||||
|
||||
features, category, warnMsgs, err := recipe.GetRecipeFeaturesAndCategory(r)
|
||||
features, category, err := recipe.GetRecipeFeaturesAndCategory(r)
|
||||
if err != nil {
|
||||
warnings = append(warnings, err.Error())
|
||||
}
|
||||
if len(warnMsgs) > 0 {
|
||||
warnings = append(warnings, warnMsgs...)
|
||||
log.Warn(err)
|
||||
}
|
||||
|
||||
catl[recipeMeta.Name] = recipe.RecipeMeta{
|
||||
@ -124,24 +112,7 @@ keys configured on your account.`,
|
||||
Features: features,
|
||||
}
|
||||
|
||||
if !internal.Debug {
|
||||
catlBar.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := catlBar.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var uniqueWarnings []string
|
||||
for _, w := range warnings {
|
||||
if !slices.Contains(uniqueWarnings, w) {
|
||||
uniqueWarnings = append(uniqueWarnings, w)
|
||||
}
|
||||
}
|
||||
|
||||
for _, warnMsg := range uniqueWarnings {
|
||||
log.Warn(warnMsg)
|
||||
catlBar.Add(1)
|
||||
}
|
||||
|
||||
recipesJSON, err := json.MarshalIndent(catl, "", " ")
|
||||
@ -171,7 +142,7 @@ keys configured on your account.`,
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("generated recipe catalogue: %s", config.RECIPES_JSON)
|
||||
log.Infof("generated new recipe catalogue in %s", config.RECIPES_JSON)
|
||||
|
||||
cataloguePath := path.Join(config.ABRA_DIR, "catalogue")
|
||||
if publishChanges {
|
||||
@ -197,7 +168,7 @@ keys configured on your account.`,
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sshURL := fmt.Sprintf(config.TOOLSHED_SSH_URL_TEMPLATE, config.CATALOGUE_JSON_REPO_NAME)
|
||||
sshURL := fmt.Sprintf(config.SSH_URL_TEMPLATE, config.CATALOGUE_JSON_REPO_NAME)
|
||||
if err := gitPkg.CreateRemote(repo, "origin-ssh", sshURL, internal.Dry); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -12,16 +12,17 @@ var AutocompleteCommand = &cobra.Command{
|
||||
Long: `To load completions:
|
||||
|
||||
Bash:
|
||||
# Load autocompletion for the current Bash session
|
||||
|
||||
$ source <(abra autocomplete bash)
|
||||
|
||||
# To load autocompletion for each session, execute once:
|
||||
# Linux:
|
||||
$ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra
|
||||
$ abra autocomplete bash > /etc/bash_completion.d/abra
|
||||
# macOS:
|
||||
$ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/abra
|
||||
$ abra autocomplete bash > $(brew --prefix)/etc/bash_completion.d/abra
|
||||
|
||||
Zsh:
|
||||
|
||||
# If shell autocompletion is not already enabled in your environment,
|
||||
# you will need to enable it. You can execute the following once:
|
||||
|
||||
@ -33,12 +34,14 @@ Zsh:
|
||||
# You will need to start a new shell for this setup to take effect.
|
||||
|
||||
fish:
|
||||
|
||||
$ abra autocomplete fish | source
|
||||
|
||||
# To load autocompletions for each session, execute once:
|
||||
$ abra autocomplete fish > ~/.config/fish/completions/abra.fish
|
||||
|
||||
PowerShell:
|
||||
|
||||
PS> abra autocomplete powershell | Out-String | Invoke-Expression
|
||||
|
||||
# To load autocompletions for every new session, run:
|
||||
|
@ -2,10 +2,9 @@ package internal
|
||||
|
||||
var (
|
||||
// NOTE(d1): global
|
||||
Debug bool
|
||||
NoInput bool
|
||||
Offline bool
|
||||
IgnoreEnvVersion bool
|
||||
Debug bool
|
||||
NoInput bool
|
||||
Offline bool
|
||||
|
||||
// NOTE(d1): sub-command specific
|
||||
Chaos bool
|
||||
|
@ -3,15 +3,10 @@ package internal
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
appPkg "coopcloud.tech/abra/pkg/app"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"coopcloud.tech/abra/pkg/recipe"
|
||||
"coopcloud.tech/tagcmp"
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
@ -25,8 +20,7 @@ var borderStyle = lipgloss.NewStyle().
|
||||
|
||||
var headerStyle = lipgloss.NewStyle().
|
||||
Underline(true).
|
||||
Bold(true).
|
||||
PaddingBottom(1)
|
||||
Bold(true)
|
||||
|
||||
var leftStyle = lipgloss.NewStyle().
|
||||
Bold(true)
|
||||
@ -43,13 +37,13 @@ func NewVersionOverview(
|
||||
app appPkg.App,
|
||||
warnMessages []string,
|
||||
kind,
|
||||
deployedVersion,
|
||||
deployedChaosVersion,
|
||||
toDeployVersion,
|
||||
currentVersion,
|
||||
chaosVersion,
|
||||
newVersion,
|
||||
releaseNotes string) error {
|
||||
deployConfig := "compose.yml"
|
||||
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
||||
deployConfig = composeFiles
|
||||
deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
|
||||
}
|
||||
|
||||
server := app.Server
|
||||
@ -57,54 +51,32 @@ func NewVersionOverview(
|
||||
server = "local"
|
||||
}
|
||||
|
||||
domain := app.Domain
|
||||
if domain == "" {
|
||||
domain = config.NO_DOMAIN_DEFAULT
|
||||
}
|
||||
|
||||
upperKind := strings.ToUpper(kind)
|
||||
|
||||
envVersion, err := recipe.GetEnvVersionRaw(app.Recipe.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if envVersion == "" {
|
||||
envVersion = config.NO_VERSION_DEFAULT
|
||||
}
|
||||
|
||||
rows := [][]string{
|
||||
{"DOMAIN", domain},
|
||||
{"RECIPE", app.Recipe.Name},
|
||||
{"SERVER", server},
|
||||
{"CONFIG", deployConfig},
|
||||
|
||||
{"CURRENT DEPLOYMENT", "---"},
|
||||
{"VERSION", formatter.BoldDirtyDefault(deployedVersion)},
|
||||
{"CHAOS ", formatter.BoldDirtyDefault(deployedChaosVersion)},
|
||||
|
||||
{upperKind, "---"},
|
||||
{"VERSION", formatter.BoldDirtyDefault(toDeployVersion)},
|
||||
|
||||
{fmt.Sprintf("%s.ENV", strings.ToUpper(app.Domain)), "---"},
|
||||
{"CURRENT VERSION", formatter.BoldDirtyDefault(envVersion)},
|
||||
{"NEW VERSION", formatter.BoldDirtyDefault(toDeployVersion)},
|
||||
}
|
||||
|
||||
overview := formatter.CreateOverview(
|
||||
fmt.Sprintf("%s OVERVIEW", upperKind),
|
||||
rows,
|
||||
body := strings.Builder{}
|
||||
body.WriteString(
|
||||
borderStyle.Render(
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Center,
|
||||
headerStyle.Render(fmt.Sprintf("%s OVERVIEW", strings.ToUpper(kind))),
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
horizontal(leftStyle.Render("SERVER"), " ", rightStyle.Render(server)),
|
||||
horizontal(leftStyle.Render("DOMAIN"), " ", rightStyle.Render(app.Domain)),
|
||||
horizontal(leftStyle.Render("RECIPE"), " ", rightStyle.Render(app.Recipe.Name)),
|
||||
horizontal(leftStyle.Render("CONFIG"), " ", rightStyle.Render(deployConfig)),
|
||||
horizontal(leftStyle.Render("VERSION"), " ", rightStyle.Render(currentVersion)),
|
||||
horizontal(leftStyle.Render("CHAOS"), " ", rightStyle.Render(chaosVersion)),
|
||||
horizontal(leftStyle.Render("DEPLOY"), " ", rightStyle.Padding(0).Render(newVersion)),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
fmt.Println(body.String())
|
||||
|
||||
fmt.Println(overview)
|
||||
|
||||
if releaseNotes != "" && toDeployVersion != "" {
|
||||
if releaseNotes != "" && newVersion != "" {
|
||||
fmt.Println()
|
||||
fmt.Print(releaseNotes)
|
||||
} else {
|
||||
warnMessages = append(
|
||||
warnMessages,
|
||||
fmt.Sprintf("no release notes available for %s", toDeployVersion),
|
||||
)
|
||||
warnMessages = append(warnMessages, fmt.Sprintf("no release notes available for %s", newVersion))
|
||||
}
|
||||
|
||||
for _, msg := range warnMessages {
|
||||
@ -129,18 +101,10 @@ func NewVersionOverview(
|
||||
}
|
||||
|
||||
// DeployOverview shows a deployment overview
|
||||
func DeployOverview(
|
||||
app appPkg.App,
|
||||
warnMessages []string,
|
||||
deployedVersion string,
|
||||
deployedChaosVersion string,
|
||||
toDeployVersion,
|
||||
toDeployChaosVersion string,
|
||||
toWriteVersion string,
|
||||
) error {
|
||||
func DeployOverview(app appPkg.App, warnMessages []string, version, chaosVersion string) error {
|
||||
deployConfig := "compose.yml"
|
||||
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
||||
deployConfig = composeFiles
|
||||
deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
|
||||
}
|
||||
|
||||
server := app.Server
|
||||
@ -148,52 +112,25 @@ func DeployOverview(
|
||||
server = "local"
|
||||
}
|
||||
|
||||
domain := app.Domain
|
||||
if domain == "" {
|
||||
domain = config.NO_DOMAIN_DEFAULT
|
||||
}
|
||||
|
||||
if app.Recipe.Dirty {
|
||||
toWriteVersion = formatter.AddDirtyMarker(toWriteVersion)
|
||||
toDeployChaosVersion = formatter.AddDirtyMarker(toDeployChaosVersion)
|
||||
}
|
||||
|
||||
recipeName, exists := app.Env["RECIPE"]
|
||||
if !exists {
|
||||
recipeName = app.Env["TYPE"]
|
||||
}
|
||||
|
||||
envVersion, err := recipe.GetEnvVersionRaw(recipeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if envVersion == "" {
|
||||
envVersion = config.NO_VERSION_DEFAULT
|
||||
}
|
||||
|
||||
rows := [][]string{
|
||||
{"DOMAIN", domain},
|
||||
{"RECIPE", app.Recipe.Name},
|
||||
{"SERVER", server},
|
||||
{"CONFIG", deployConfig},
|
||||
|
||||
{"CURRENT DEPLOYMENT", "---"},
|
||||
{"VERSION", formatter.BoldDirtyDefault(deployedVersion)},
|
||||
{"CHAOS", formatter.BoldDirtyDefault(deployedChaosVersion)},
|
||||
|
||||
{"NEW DEPLOYMENT", "---"},
|
||||
{"VERSION", formatter.BoldDirtyDefault(toDeployVersion)},
|
||||
{"CHAOS", formatter.BoldDirtyDefault(toDeployChaosVersion)},
|
||||
|
||||
{fmt.Sprintf("%s.ENV", strings.ToUpper(app.Name)), "---"},
|
||||
{"CURRENT VERSION", formatter.BoldDirtyDefault(envVersion)},
|
||||
{"NEW VERSION", formatter.BoldDirtyDefault(toWriteVersion)},
|
||||
}
|
||||
|
||||
overview := formatter.CreateOverview("DEPLOY OVERVIEW", rows)
|
||||
|
||||
fmt.Println(overview)
|
||||
body := strings.Builder{}
|
||||
body.WriteString(
|
||||
borderStyle.Render(
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Center,
|
||||
headerStyle.Render("DEPLOY OVERVIEW"),
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
horizontal(leftStyle.Render("SERVER"), " ", rightStyle.Render(server)),
|
||||
horizontal(leftStyle.Render("DOMAIN"), " ", rightStyle.Render(app.Domain)),
|
||||
horizontal(leftStyle.Render("RECIPE"), " ", rightStyle.Render(app.Recipe.Name)),
|
||||
horizontal(leftStyle.Render("CONFIG"), " ", rightStyle.Render(deployConfig)),
|
||||
horizontal(leftStyle.Render("VERSION"), " ", rightStyle.Render(version)),
|
||||
horizontal(leftStyle.Render("CHAOS"), " ", rightStyle.Padding(0).Render(chaosVersion)),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
fmt.Println(body.String())
|
||||
|
||||
for _, msg := range warnMessages {
|
||||
log.Warn(msg)
|
||||
@ -216,78 +153,6 @@ func DeployOverview(
|
||||
return nil
|
||||
}
|
||||
|
||||
// UndeployOverview shows an undeployment overview
|
||||
func UndeployOverview(
|
||||
app appPkg.App,
|
||||
deployedVersion,
|
||||
deployedChaosVersion,
|
||||
toWriteVersion string,
|
||||
) error {
|
||||
deployConfig := "compose.yml"
|
||||
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
||||
deployConfig = composeFiles
|
||||
}
|
||||
|
||||
server := app.Server
|
||||
if app.Server == "default" {
|
||||
server = "local"
|
||||
}
|
||||
|
||||
domain := app.Domain
|
||||
if domain == "" {
|
||||
domain = config.NO_DOMAIN_DEFAULT
|
||||
}
|
||||
|
||||
recipeName, exists := app.Env["RECIPE"]
|
||||
if !exists {
|
||||
recipeName = app.Env["TYPE"]
|
||||
}
|
||||
|
||||
envVersion, err := recipe.GetEnvVersionRaw(recipeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if envVersion == "" {
|
||||
envVersion = config.NO_VERSION_DEFAULT
|
||||
}
|
||||
|
||||
rows := [][]string{
|
||||
{"DOMAIN", domain},
|
||||
{"RECIPE", app.Recipe.Name},
|
||||
{"SERVER", server},
|
||||
{"CONFIG", deployConfig},
|
||||
|
||||
{"CURRENT DEPLOYMENT", "---"},
|
||||
{"VERSION", formatter.BoldDirtyDefault(deployedVersion)},
|
||||
{"CHAOS", formatter.BoldDirtyDefault(deployedChaosVersion)},
|
||||
|
||||
{fmt.Sprintf("%s.ENV", strings.ToUpper(app.Name)), "---"},
|
||||
{"CURRENT VERSION", formatter.BoldDirtyDefault(envVersion)},
|
||||
{"NEW VERSION", formatter.BoldDirtyDefault(toWriteVersion)},
|
||||
}
|
||||
|
||||
overview := formatter.CreateOverview("UNDEPLOY OVERVIEW", rows)
|
||||
|
||||
fmt.Println(overview)
|
||||
|
||||
if NoInput {
|
||||
return nil
|
||||
}
|
||||
|
||||
response := false
|
||||
prompt := &survey.Confirm{Message: "proceed?"}
|
||||
if err := survey.AskOne(prompt, &response); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !response {
|
||||
log.Fatal("undeploy cancelled")
|
||||
}
|
||||
|
||||
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>|... "
|
||||
@ -345,22 +210,3 @@ func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SortVersionsDesc sorts versions in descending order.
|
||||
func SortVersionsDesc(versions []string) []string {
|
||||
var tags []tagcmp.Tag
|
||||
|
||||
for _, v := range versions {
|
||||
parsed, _ := tagcmp.Parse(v) // skips unsupported tags
|
||||
tags = append(tags, parsed)
|
||||
}
|
||||
|
||||
sort.Sort(tagcmp.ByTagDesc(tags))
|
||||
|
||||
var desc []string
|
||||
for _, t := range tags {
|
||||
desc = append(desc, t.String())
|
||||
}
|
||||
|
||||
return desc
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSortVersionsDesc(t *testing.T) {
|
||||
versions := SortVersionsDesc([]string{
|
||||
"0.2.3+1.2.2",
|
||||
"1.0.0+2.2.2",
|
||||
})
|
||||
|
||||
assert.Equal(t, "1.0.0+2.2.2", versions[0])
|
||||
assert.Equal(t, "0.2.3+1.2.2", versions[1])
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package internal
|
||||
|
||||
import "coopcloud.tech/abra/pkg/recipe"
|
||||
|
||||
func GetEnsureContext() recipe.EnsureContext {
|
||||
return recipe.EnsureContext{
|
||||
Chaos,
|
||||
Offline,
|
||||
IgnoreEnvVersion,
|
||||
}
|
||||
}
|
18
cli/internal/errors.go
Normal file
18
cli/internal/errors.go
Normal file
@ -0,0 +1,18 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// ShowSubcommandHelpAndError exits the program on error, logs the error to the
|
||||
// terminal, and shows the help command.
|
||||
func ShowSubcommandHelpAndError(cmd *cli.Command, err interface{}) {
|
||||
if err2 := cli.ShowSubcommandHelp(cmd); err2 != nil {
|
||||
log.Error(err2)
|
||||
}
|
||||
log.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
10
cli/internal/list.go
Normal file
10
cli/internal/list.go
Normal file
@ -0,0 +1,10 @@
|
||||
package internal
|
||||
|
||||
// ReverseStringList reverses a list of a strings. Roll on Go generics.
|
||||
func ReverseStringList(strings []string) []string {
|
||||
for i, j := 0, len(strings)-1; i < j; i, j = i+1, j-1 {
|
||||
strings[i], strings[j] = strings[j], strings[i]
|
||||
}
|
||||
|
||||
return strings
|
||||
}
|
@ -10,9 +10,10 @@ import (
|
||||
)
|
||||
|
||||
var RecipeFetchCommand = &cobra.Command{
|
||||
Use: "fetch [recipe | --all] [flags]",
|
||||
Use: "fetch [recipe] [flags]",
|
||||
Aliases: []string{"f"},
|
||||
Short: "Clone recipe(s) locally",
|
||||
Short: "Fetch recipe(s)",
|
||||
Long: "Retrieves all recipes if no [recipe] argument is passed.",
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
ValidArgsFunction: func(
|
||||
cmd *cobra.Command,
|
||||
@ -26,18 +27,9 @@ var RecipeFetchCommand = &cobra.Command{
|
||||
recipeName = args[0]
|
||||
}
|
||||
|
||||
if recipeName == "" && !fetchAllRecipes {
|
||||
log.Fatal("missing [recipe] or --all/-a")
|
||||
}
|
||||
|
||||
if recipeName != "" && fetchAllRecipes {
|
||||
log.Fatal("cannot use [recipe] and --all/-a together")
|
||||
}
|
||||
|
||||
ensureCtx := internal.GetEnsureContext()
|
||||
if recipeName != "" {
|
||||
r := internal.ValidateRecipe(args, cmd.Name())
|
||||
if err := r.Ensure(ensureCtx); err != nil {
|
||||
if err := r.Ensure(false, false); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
@ -51,24 +43,10 @@ var RecipeFetchCommand = &cobra.Command{
|
||||
catlBar := formatter.CreateProgressbar(len(catalogue), "fetching latest recipes...")
|
||||
for recipeName := range catalogue {
|
||||
r := recipe.Get(recipeName)
|
||||
if err := r.Ensure(ensureCtx); err != nil {
|
||||
if err := r.Ensure(false, false); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
catlBar.Add(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
fetchAllRecipes bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
RecipeFetchCommand.Flags().BoolVarP(
|
||||
&fetchAllRecipes,
|
||||
"all",
|
||||
"a",
|
||||
false,
|
||||
"fetch all recipes",
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package recipe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
@ -23,7 +25,7 @@ var RecipeLintCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
recipe := internal.ValidateRecipe(args, cmd.Name())
|
||||
|
||||
if err := recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -102,9 +104,7 @@ var RecipeLintCommand = &cobra.Command{
|
||||
}
|
||||
|
||||
if len(rows) > 0 {
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
|
||||
for _, warnMsg := range warnMessages {
|
||||
log.Warn(warnMsg)
|
||||
|
@ -79,9 +79,8 @@ var RecipeListCommand = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
log.Infof("total recipes: %v", len(rows))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ var RecipeNewCommand = &cobra.Command{
|
||||
if err := os.RemoveAll(gitRepo); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Debugf("removed .git repo in %s", gitRepo)
|
||||
log.Debugf("removed example git repo in %s", gitRepo)
|
||||
|
||||
meta := newRecipeMeta(recipeName)
|
||||
|
||||
@ -76,6 +76,7 @@ var RecipeNewCommand = &cobra.Command{
|
||||
if err := os.WriteFile(path, templated.Bytes(), 0o644); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := git.Init(r.Dir, true, gitName, gitEmail); err != nil {
|
||||
|
@ -252,14 +252,7 @@ func getTagCreateOptions(tag string) (git.CreateTagOptions, error) {
|
||||
// addReleaseNotes checks if the release/next release note exists and moves the
|
||||
// file to release/<tag>.
|
||||
func addReleaseNotes(recipe recipe.Recipe, tag string) error {
|
||||
releaseDir := path.Join(recipe.Dir, "release")
|
||||
if _, err := os.Stat(releaseDir); errors.Is(err, os.ErrNotExist) {
|
||||
if err := os.Mkdir(releaseDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tagReleaseNotePath := path.Join(releaseDir, tag)
|
||||
tagReleaseNotePath := path.Join(recipe.Dir, "release", tag)
|
||||
if _, err := os.Stat(tagReleaseNotePath); err == nil {
|
||||
// Release note for current tag already exist exists.
|
||||
return nil
|
||||
@ -267,9 +260,7 @@ func addReleaseNotes(recipe recipe.Recipe, tag string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var addNextAsReleaseNotes bool
|
||||
|
||||
nextReleaseNotePath := path.Join(releaseDir, "next")
|
||||
nextReleaseNotePath := path.Join(recipe.Dir, "release", "next")
|
||||
if _, err := os.Stat(nextReleaseNotePath); err == nil {
|
||||
// release/next note exists. Move it to release/<tag>
|
||||
if internal.Dry {
|
||||
@ -278,37 +269,38 @@ func addReleaseNotes(recipe recipe.Recipe, tag string) error {
|
||||
}
|
||||
|
||||
if !internal.NoInput {
|
||||
prompt := &survey.Confirm{
|
||||
prompt := &survey.Input{
|
||||
Message: "Use release note in release/next?",
|
||||
}
|
||||
|
||||
if err := survey.AskOne(prompt, &addNextAsReleaseNotes); err != nil {
|
||||
var addReleaseNote bool
|
||||
if err := survey.AskOne(prompt, &addReleaseNote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !addNextAsReleaseNotes {
|
||||
if !addReleaseNote {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Rename(nextReleaseNotePath, tagReleaseNotePath); err != nil {
|
||||
err := os.Rename(nextReleaseNotePath, tagReleaseNotePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gitPkg.Add(recipe.Dir, path.Join("release", "next"), internal.Dry); err != nil {
|
||||
err = gitPkg.Add(recipe.Dir, path.Join("release", "next"), internal.Dry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gitPkg.Add(recipe.Dir, path.Join("release", tag), internal.Dry); err != nil {
|
||||
err = gitPkg.Add(recipe.Dir, path.Join("release", tag), internal.Dry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE(d1): No release note exists for the current release. Or, we've
|
||||
// already used release/next as the release note
|
||||
if internal.NoInput || addNextAsReleaseNotes {
|
||||
// No release note exists for the current release.
|
||||
if internal.NoInput {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
recipe := internal.ValidateRecipe(args, cmd.Name())
|
||||
|
||||
if err := recipe.Ensure(internal.GetEnsureContext()); err != nil {
|
||||
if err := recipe.Ensure(internal.Chaos, internal.Offline); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -37,13 +37,10 @@ var RecipeVersionCommand = &cobra.Command{
|
||||
if !ok {
|
||||
warnMessages = append(warnMessages, "retrieved versions from local recipe repository")
|
||||
|
||||
recipeVersions, warnMsg, err := recipe.GetRecipeVersions()
|
||||
recipeVersions, err := recipe.GetRecipeVersions()
|
||||
if err != nil {
|
||||
warnMessages = append(warnMessages, err.Error())
|
||||
}
|
||||
if len(warnMsg) > 0 {
|
||||
warnMessages = append(warnMessages, warnMsg...)
|
||||
}
|
||||
|
||||
recipeMeta = recipePkg.RecipeMeta{Versions: recipeVersions}
|
||||
}
|
||||
@ -58,32 +55,15 @@ var RecipeVersionCommand = &cobra.Command{
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
table.Headers("SERVICE", "IMAGE", "TAG", "VERSION")
|
||||
table.Headers("SERVICE", "NAME", "TAG")
|
||||
|
||||
for version, meta := range recipeMeta.Versions[i] {
|
||||
var allRows [][]string
|
||||
var rows [][]string
|
||||
|
||||
for service, serviceMeta := range meta {
|
||||
recipeVersion := version
|
||||
if service != "app" {
|
||||
recipeVersion = ""
|
||||
}
|
||||
|
||||
rows = append(rows, []string{
|
||||
service,
|
||||
serviceMeta.Image,
|
||||
serviceMeta.Tag,
|
||||
recipeVersion,
|
||||
})
|
||||
|
||||
allRows = append(allRows, []string{
|
||||
version,
|
||||
service,
|
||||
serviceMeta.Image,
|
||||
serviceMeta.Tag,
|
||||
recipeVersion,
|
||||
})
|
||||
rows = append(rows, []string{service, serviceMeta.Image, serviceMeta.Tag})
|
||||
allRows = append(allRows, []string{version, service, serviceMeta.Image, serviceMeta.Tag})
|
||||
}
|
||||
|
||||
sort.Slice(rows, sortServiceByName(rows))
|
||||
@ -91,9 +71,9 @@ var RecipeVersionCommand = &cobra.Command{
|
||||
table.Rows(rows...)
|
||||
|
||||
if !internal.MachineReadable {
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
log.Infof("VERSION: %s", version)
|
||||
fmt.Println()
|
||||
continue
|
||||
}
|
||||
|
||||
@ -120,7 +100,11 @@ var RecipeVersionCommand = &cobra.Command{
|
||||
|
||||
func sortServiceByName(versions [][]string) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
return versions[i][0] < versions[j][0]
|
||||
// NOTE(d1): corresponds to the `tableCol` definition below
|
||||
if versions[i][1] == "app" {
|
||||
return true
|
||||
}
|
||||
return versions[i][1] < versions[j][1]
|
||||
}
|
||||
}
|
||||
|
||||
|
33
cli/run.go
33
cli/run.go
@ -48,13 +48,9 @@ func Run(version, commit string) {
|
||||
}
|
||||
}
|
||||
|
||||
log.Logger.SetStyles(charmLog.DefaultStyles())
|
||||
log.Logger.SetStyles(log.Styles())
|
||||
charmLog.SetDefault(log.Logger)
|
||||
|
||||
if internal.MachineReadable {
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
|
||||
if internal.Debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetOutput(os.Stderr)
|
||||
@ -99,37 +95,20 @@ func Run(version, commit string) {
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(
|
||||
&internal.Debug,
|
||||
"debug",
|
||||
"d",
|
||||
false,
|
||||
&internal.Debug, "debug", "d", false,
|
||||
"show debug messages",
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(
|
||||
&internal.NoInput,
|
||||
"no-input",
|
||||
"n",
|
||||
false,
|
||||
&internal.NoInput, "no-input", "n", false,
|
||||
"toggle non-interactive mode",
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(
|
||||
&internal.Offline,
|
||||
"offline",
|
||||
"o",
|
||||
false,
|
||||
&internal.Offline, "offline", "o", false,
|
||||
"prefer offline & filesystem access",
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(
|
||||
&internal.IgnoreEnvVersion,
|
||||
"ignore-env-version",
|
||||
"i",
|
||||
false,
|
||||
"ignore .env version checkout",
|
||||
)
|
||||
|
||||
catalogue.CatalogueCommand.AddCommand(
|
||||
catalogue.CatalogueGenerateCommand,
|
||||
)
|
||||
@ -208,11 +187,9 @@ func Run(version, commit string) {
|
||||
app.AppUndeployCommand,
|
||||
app.AppUpgradeCommand,
|
||||
app.AppVolumeCommand,
|
||||
app.AppLabelsCommand,
|
||||
app.AppEnvCommand,
|
||||
)
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,10 @@ developer machine. The domain is then set to "default".`,
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := dns.EnsureIPv4(name); err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
|
||||
_, err := createServerDir(name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -96,28 +100,21 @@ developer machine. The domain is then set to "default".`,
|
||||
created, err := newContext(name)
|
||||
if err != nil {
|
||||
cleanUp(name)
|
||||
log.Fatalf("unable to create local context: %s", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("attempting to create client for %s", name)
|
||||
|
||||
if _, err := client.New(name, timeout); err != nil {
|
||||
cleanUp(name)
|
||||
log.Debugf("ssh %s error: %s", name, sshPkg.Fatal(name, err))
|
||||
log.Fatalf("can't ssh to %s, make sure \"ssh %s\" works", name, name)
|
||||
log.Fatal(sshPkg.Fatal(name, err))
|
||||
}
|
||||
|
||||
if created {
|
||||
log.Infof("%s successfully added", name)
|
||||
|
||||
if _, err := dns.EnsureIPv4(name); err != nil {
|
||||
log.Warnf("unable to resolve IPv4 for %s", name)
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
log.Warnf("%s already exists", name)
|
||||
}
|
||||
|
||||
log.Warnf("%s already exists", name)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -86,9 +86,7 @@ var ServerListCommand = &cobra.Command{
|
||||
return
|
||||
}
|
||||
|
||||
if err := formatter.PrintTable(table); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(table)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -452,7 +452,7 @@ func newKadabraApp(version, commit string) *cobra.Command {
|
||||
Version: fmt.Sprintf("%s-%s", version, commit[:7]),
|
||||
Short: "The Co-op Cloud auto-updater 🤖 🚀",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
log.Logger.SetStyles(charmLog.DefaultStyles())
|
||||
log.Logger.SetStyles(log.Styles())
|
||||
charmLog.SetDefault(log.Logger)
|
||||
|
||||
if internal.Debug {
|
||||
|
38
go.mod
38
go.mod
@ -4,9 +4,11 @@ go 1.22.7
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
replace github.com/urfave/cli/v3 => github.com/urfave/cli/v3 v3.0.0-alpha9.1.0.20241019193437-5053ec708a44
|
||||
|
||||
require (
|
||||
coopcloud.tech/tagcmp v0.0.0-20230809071031-eb3e7758d4eb
|
||||
git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c
|
||||
git.coopcloud.tech/coop-cloud/godotenv v1.5.2-0.20231130100509-01bff8284355
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/charmbracelet/lipgloss v1.0.0
|
||||
github.com/charmbracelet/log v0.4.0
|
||||
@ -14,13 +16,14 @@ require (
|
||||
github.com/docker/cli v27.4.1+incompatible
|
||||
github.com/docker/docker v27.4.1+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/go-git/go-git/v5 v5.13.1
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/schollz/progressbar/v3 v3.17.1
|
||||
golang.org/x/term v0.28.0
|
||||
github.com/urfave/cli/v3 v3.0.0-alpha9
|
||||
golang.org/x/term v0.27.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.5.1
|
||||
)
|
||||
@ -28,7 +31,7 @@ require (
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.3 // indirect
|
||||
@ -52,7 +55,7 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.6.0 // 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.6.1 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@ -75,7 +78,6 @@ require (
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mmcloughlin/avo v0.6.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
@ -87,7 +89,7 @@ require (
|
||||
github.com/opencontainers/runc v1.1.13 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.1 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.61.0 // indirect
|
||||
@ -111,19 +113,17 @@ require (
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||
google.golang.org/grpc v1.69.2 // indirect
|
||||
google.golang.org/protobuf v1.36.2 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
@ -146,5 +146,5 @@ require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/sys v0.29.0
|
||||
golang.org/x/sys v0.28.0
|
||||
)
|
||||
|
127
go.sum
127
go.sum
@ -27,16 +27,16 @@ coopcloud.tech/tagcmp v0.0.0-20230809071031-eb3e7758d4eb/go.mod h1:ESVm0wQKcbcFi
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c h1:oeKnUB79PKYD8D0/unYuu7MRcWryQQWOns8+JL+acrs=
|
||||
git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c/go.mod h1:fQuhwrpg6qb9NlFXKYi/LysWu1wxjraS8sxyW12CUF0=
|
||||
git.coopcloud.tech/coop-cloud/godotenv v1.5.2-0.20231130100509-01bff8284355 h1:tCv2B4qoN6RMheKDnCzIafOkWS5BB1h7hwhmo+9bVeE=
|
||||
git.coopcloud.tech/coop-cloud/godotenv v1.5.2-0.20231130100509-01bff8284355/go.mod h1:Q8V1zbtPAlzYSr/Dvky3wS6x58IQAl3rot2me1oSO2Q=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
||||
@ -140,6 +140,8 @@ github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O
|
||||
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
|
||||
github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
|
||||
github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
|
||||
github.com/charmbracelet/x/ansi v0.5.2 h1:dEa1x2qdOZXD/6439s+wF7xjV+kZLu/iN00GuXXrU9E=
|
||||
github.com/charmbracelet/x/ansi v0.5.2/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q=
|
||||
github.com/charmbracelet/x/ansi v0.6.0 h1:qOznutrb93gx9oMiGf7caF7bqqubh6YIM0SWKyA08pA=
|
||||
github.com/charmbracelet/x/ansi v0.6.0/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
|
||||
@ -276,6 +278,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
@ -286,6 +289,8 @@ 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.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
|
||||
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||
@ -306,6 +311,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
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 v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ=
|
||||
github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI=
|
||||
github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
@ -314,6 +321,8 @@ github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+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 v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4=
|
||||
github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||
@ -341,8 +350,8 @@ 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/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 v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
|
||||
github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
@ -373,16 +382,16 @@ github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYis
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
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/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
|
||||
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
|
||||
github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
|
||||
github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
|
||||
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
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-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -522,6 +531,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
@ -651,8 +662,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
@ -675,8 +684,6 @@ github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcY
|
||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@ -754,8 +761,8 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pjbgf/sha1cd v0.3.1 h1:Dh2GYdpJnO84lIw0LJwTFXjcNbasP/bklicSznyAaPI=
|
||||
github.com/pjbgf/sha1cd v0.3.1/go.mod h1:Y8t7jSB/dEI/lQE04A1HVKteqjj9bX5O4+Cex0TCu8s=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -786,6 +793,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
|
||||
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
|
||||
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
|
||||
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
@ -896,6 +905,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v3 v3.0.0-alpha9.1.0.20241019193437-5053ec708a44 h1:BeSTAZEDkDVNv9EOrycIGCkEg+6EhRRgSsbdc93Q3OM=
|
||||
github.com/urfave/cli/v3 v3.0.0-alpha9.1.0.20241019193437-5053ec708a44/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
||||
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
|
||||
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
@ -936,31 +947,49 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 h1:7F29RDmnlqk6B5d+sUqemt8TBfDqxryYW5gX6L74RFA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0/go.mod h1:ZiGDq7xwDMKmWDrN1XsXAj0iC7hns+2DhxBFSncNHSE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@ -985,10 +1014,10 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
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.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -999,10 +1028,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -1025,8 +1054,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1068,10 +1095,10 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -1089,6 +1116,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -1170,17 +1199,17 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1190,6 +1219,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -1200,8 +1231,6 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -1247,10 +1276,6 @@ golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4X
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -1299,14 +1324,14 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
@ -1326,6 +1351,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
||||
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
@ -1341,10 +1368,10 @@ 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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
|
||||
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII=
|
||||
|
@ -36,7 +36,7 @@ func Get(appName string) (App, error) {
|
||||
return App{}, err
|
||||
}
|
||||
|
||||
log.Debugf("loaded app %s: %s", appName, app)
|
||||
log.Debugf("retrieved %s for %s", app, appName)
|
||||
|
||||
return app, nil
|
||||
}
|
||||
@ -91,17 +91,6 @@ type App struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// String outputs a human-friendly string representation.
|
||||
func (a App) String() string {
|
||||
out := fmt.Sprintf("{name: %s, ", a.Name)
|
||||
out += fmt.Sprintf("recipe: %s, ", a.Recipe)
|
||||
out += fmt.Sprintf("domain: %s, ", a.Domain)
|
||||
out += fmt.Sprintf("env %s, ", a.Env)
|
||||
out += fmt.Sprintf("server %s, ", a.Server)
|
||||
out += fmt.Sprintf("path %s}", a.Path)
|
||||
return out
|
||||
}
|
||||
|
||||
// Type aliases to make code hints easier to understand
|
||||
|
||||
// AppName is AppName
|
||||
@ -246,6 +235,8 @@ func ReadAppEnvFile(appFile AppFile, name AppName) (App, error) {
|
||||
return App{}, fmt.Errorf("env file for %s couldn't be read: %s", name, err.Error())
|
||||
}
|
||||
|
||||
log.Debugf("read env %s from %s", env, appFile.Path)
|
||||
|
||||
app, err := NewApp(env, name, appFile)
|
||||
if err != nil {
|
||||
return App{}, fmt.Errorf("env file for %s has issues: %s", name, err.Error())
|
||||
@ -503,13 +494,13 @@ func GetAppComposeConfig(recipe string, opts stack.Deploy, appEnv envfile.AppEnv
|
||||
func ExposeAllEnv(stackName string, compose *composetypes.Config, appEnv envfile.AppEnv) {
|
||||
for _, service := range compose.Services {
|
||||
if service.Name == "app" {
|
||||
log.Debugf("adding env vars to %s service config", stackName)
|
||||
log.Debugf("add the following environment to the app service config of %s:", stackName)
|
||||
for k, v := range appEnv {
|
||||
_, exists := service.Environment[k]
|
||||
if !exists {
|
||||
value := v
|
||||
service.Environment[k] = &value
|
||||
log.Debugf("%s: %s: %s", stackName, k, value)
|
||||
log.Debugf("add env var: %s value: %s to %s", k, value, stackName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -578,49 +569,6 @@ func ReadAbraShCmdNames(abraSh string) ([]string, error) {
|
||||
return cmdNames, nil
|
||||
}
|
||||
|
||||
// Wipe removes the version from the app .env file.
|
||||
func (a App) WipeRecipeVersion() error {
|
||||
file, err := os.Open(a.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var (
|
||||
lines []string
|
||||
scanner = bufio.NewScanner(file)
|
||||
)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if !strings.HasPrefix(line, "RECIPE=") && !strings.HasPrefix(line, "TYPE=") {
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "#") {
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
splitted := strings.Split(line, ":")
|
||||
lines = append(lines, splitted[0])
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(a.Path, []byte(strings.Join(lines, "\n")), os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Debugf("version wiped from %s.env", a.Domain)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRecipeVersion writes the recipe version to the app .env file.
|
||||
func (a App) WriteRecipeVersion(version string, dryRun bool) error {
|
||||
file, err := os.Open(a.Path)
|
||||
if err != nil {
|
||||
@ -628,13 +576,9 @@ func (a App) WriteRecipeVersion(version string, dryRun bool) error {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var (
|
||||
dirtyVersion string
|
||||
skipped bool
|
||||
lines []string
|
||||
scanner = bufio.NewScanner(file)
|
||||
)
|
||||
|
||||
skipped := false
|
||||
scanner := bufio.NewScanner(file)
|
||||
lines := []string{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if !strings.HasPrefix(line, "RECIPE=") && !strings.HasPrefix(line, "TYPE=") {
|
||||
@ -647,27 +591,13 @@ func (a App) WriteRecipeVersion(version string, dryRun bool) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(line, version) && !a.Recipe.Dirty && !strings.HasSuffix(line, config.DIRTY_DEFAULT) {
|
||||
if strings.Contains(line, version) {
|
||||
skipped = true
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
splitted := strings.Split(line, ":")
|
||||
|
||||
if a.Recipe.Dirty {
|
||||
dirtyVersion = fmt.Sprintf("%s%s", version, config.DIRTY_DEFAULT)
|
||||
if strings.Contains(line, dirtyVersion) {
|
||||
skipped = true
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
line = fmt.Sprintf("%s:%s", splitted[0], dirtyVersion)
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
line = fmt.Sprintf("%s:%s", splitted[0], version)
|
||||
lines = append(lines, line)
|
||||
}
|
||||
@ -676,10 +606,6 @@ func (a App) WriteRecipeVersion(version string, dryRun bool) error {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if a.Recipe.Dirty && dirtyVersion != "" {
|
||||
version = dirtyVersion
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
if err := os.WriteFile(a.Path, []byte(strings.Join(lines, "\n")), os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
@ -689,7 +615,7 @@ func (a App) WriteRecipeVersion(version string, dryRun bool) error {
|
||||
}
|
||||
|
||||
if !skipped {
|
||||
log.Debugf("version %s saved to %s.env", version, a.Domain)
|
||||
log.Infof("version %s saved to %s.env", version, a.Domain)
|
||||
} else {
|
||||
log.Debugf("skipping version %s write as already exists in %s.env", version, a.Domain)
|
||||
}
|
||||
|
@ -198,41 +198,3 @@ func compareFilter(t *testing.T, f1 filters.Args, f2 map[string]map[string]bool)
|
||||
t.Errorf("filters mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteRecipeVersionOverwrite(t *testing.T) {
|
||||
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer t.Cleanup(func() {
|
||||
if err := app.WipeRecipeVersion(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
assert.Equal(t, "", app.Recipe.EnvVersion)
|
||||
|
||||
if err := app.WriteRecipeVersion("foo", false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
app, err = appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "foo", app.Recipe.EnvVersion)
|
||||
|
||||
app.Recipe.Dirty = true
|
||||
if err := app.WriteRecipeVersion("foo+U", false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
app, err = appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "foo+U", app.Recipe.EnvVersion)
|
||||
}
|
||||
|
@ -1,26 +1,21 @@
|
||||
package autocomplete
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"coopcloud.tech/abra/pkg/app"
|
||||
appPkg "coopcloud.tech/abra/pkg/app"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"coopcloud.tech/abra/pkg/recipe"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AppNameComplete copletes app names.
|
||||
func AppNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||
appFiles, err := app.LoadAppFiles("")
|
||||
appNames, err := app.GetAppNames()
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
var appNames []string
|
||||
for appName := range appFiles {
|
||||
appNames = append(appNames, appName)
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
return appNames, cobra.ShellCompDirectiveDefault
|
||||
@ -29,8 +24,8 @@ func AppNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||
func ServiceNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
||||
serviceNames, err := app.GetAppServiceNames(appName)
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
return serviceNames, cobra.ShellCompDirectiveDefault
|
||||
@ -40,8 +35,8 @@ func ServiceNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
||||
func RecipeNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||
catl, err := recipe.ReadRecipeCatalogue(false)
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
var recipeNames []string
|
||||
@ -54,10 +49,10 @@ func RecipeNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||
|
||||
// RecipeVersionComplete completes versions for the recipe.
|
||||
func RecipeVersionComplete(recipeName string) ([]string, cobra.ShellCompDirective) {
|
||||
catl, err := recipe.ReadRecipeCatalogue(true)
|
||||
catl, err := recipe.ReadRecipeCatalogue(false)
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
var recipeVersions []string
|
||||
@ -74,8 +69,8 @@ func RecipeVersionComplete(recipeName string) ([]string, cobra.ShellCompDirectiv
|
||||
func ServerNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||
files, err := app.LoadAppFiles("")
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
var serverNames []string
|
||||
@ -90,14 +85,14 @@ func ServerNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||
func CommandNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
||||
app, err := app.Get(appName)
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
cmdNames, err := appPkg.ReadAbraShCmdNames(app.Recipe.AbraShPath)
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
sort.Strings(cmdNames)
|
||||
@ -111,8 +106,8 @@ func SecretComplete(recipeName string) ([]string, cobra.ShellCompDirective) {
|
||||
|
||||
config, err := r.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||
return []string{err}, cobra.ShellCompDirectiveError
|
||||
log.Debugf("autocomplete failed: %s", err)
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
var secretNames []string
|
||||
|
@ -16,12 +16,13 @@ import (
|
||||
func EnsureCatalogue() error {
|
||||
catalogueDir := path.Join(config.ABRA_DIR, "catalogue")
|
||||
if _, err := os.Stat(catalogueDir); err != nil && os.IsNotExist(err) {
|
||||
log.Debugf("catalogue is missing, retrieving now")
|
||||
|
||||
log.Warnf("local recipe catalogue is missing, retrieving now")
|
||||
url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME)
|
||||
if err := gitPkg.Clone(catalogueDir, url); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("cloned catalogue repository to %s", catalogueDir)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -97,28 +97,15 @@ func (a Abra) GetCatalogueDir() string { return path.Join(a.GetAbraDir(), "catal
|
||||
var config = LoadAbraConfig()
|
||||
|
||||
var (
|
||||
ABRA_DIR = config.GetAbraDir()
|
||||
SERVERS_DIR = config.GetServersDir()
|
||||
RECIPES_DIR = config.GetRecipesDir()
|
||||
VENDOR_DIR = config.GetVendorDir()
|
||||
BACKUP_DIR = config.GetBackupDir()
|
||||
CATALOGUE_DIR = config.GetCatalogueDir()
|
||||
RECIPES_JSON = path.Join(config.GetCatalogueDir(), "recipes.json")
|
||||
REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud"
|
||||
CATALOGUE_JSON_REPO_NAME = "recipes-catalogue-json"
|
||||
TOOLSHED_SSH_URL_TEMPLATE = "ssh://git@git.coopcloud.tech:2222/toolshed/%s.git"
|
||||
RECIPES_SSH_URL_TEMPLATE = "ssh://git@git.coopcloud.tech:2222/coop-cloud/%s.git"
|
||||
|
||||
// NOTE(d1): please note, this was done purely out of laziness on our part
|
||||
// AFAICR. it's easy to punt the value into the label because that is what is
|
||||
// expects. it's not particularly useful in terms of UI/UX but hey, nobody
|
||||
// complained yet!
|
||||
CHAOS_DEFAULT = "false"
|
||||
|
||||
DIRTY_DEFAULT = "+U"
|
||||
|
||||
NO_DOMAIN_DEFAULT = "N/A"
|
||||
NO_VERSION_DEFAULT = "N/A"
|
||||
|
||||
UNKNOWN_DEFAULT = "unknown"
|
||||
ABRA_DIR = config.GetAbraDir()
|
||||
SERVERS_DIR = config.GetServersDir()
|
||||
RECIPES_DIR = config.GetRecipesDir()
|
||||
VENDOR_DIR = config.GetVendorDir()
|
||||
BACKUP_DIR = config.GetBackupDir()
|
||||
CATALOGUE_DIR = config.GetCatalogueDir()
|
||||
RECIPES_JSON = path.Join(config.GetCatalogueDir(), "recipes.json")
|
||||
REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud"
|
||||
CATALOGUE_JSON_REPO_NAME = "recipes-catalogue-json"
|
||||
SSH_URL_TEMPLATE = "ssh://git@git.coopcloud.tech:2222/coop-cloud/%s.git"
|
||||
CHAOS_DEFAULT = "false"
|
||||
)
|
||||
|
@ -26,16 +26,9 @@ func GetServers() ([]string, error) {
|
||||
return servers, err
|
||||
}
|
||||
|
||||
var filtered []string
|
||||
for _, s := range servers {
|
||||
if !strings.HasPrefix(s, ".") {
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
}
|
||||
log.Debugf("retrieved %v servers: %s", len(servers), servers)
|
||||
|
||||
log.Debugf("retrieved %v servers: %s", len(filtered), filtered)
|
||||
|
||||
return filtered, nil
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// ReadServerNames retrieves all server names.
|
||||
|
@ -9,11 +9,12 @@ import (
|
||||
func EnsureIPv4(domainName string) (string, error) {
|
||||
ipv4, err := net.ResolveIPAddr("ip4", domainName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s: unable to resolve IPv4 address: %s", domainName, err)
|
||||
return "", fmt.Errorf("unable to resolve ipv4 address for %s, %s", domainName, err)
|
||||
}
|
||||
|
||||
// NOTE(d1): e.g. when there is only an ipv6 record available
|
||||
if ipv4 == nil {
|
||||
return "", fmt.Errorf("%s: no IPv4 available", domainName)
|
||||
return "", fmt.Errorf("unable to resolve ipv4 address for %s", domainName)
|
||||
}
|
||||
|
||||
return ipv4.String(), nil
|
||||
|
@ -15,7 +15,7 @@ func TestEnsureDomainsResolveSameIPv4(t *testing.T) {
|
||||
}{
|
||||
// NOTE(d1): DNS records get checked, so use something that is maintained
|
||||
// within the federation. if you're here because of a failing test, try
|
||||
// `dig +short <domain>` to ensure stuff matches first! If flakyness
|
||||
// `dig +short <app>` to ensure stuff matches first! If flakyness
|
||||
// becomes an issue we can look into mocking
|
||||
{"docs.coopcloud.tech", "swarm-0.coopcloud.tech", true},
|
||||
{"docs.coopcloud.tech", "coopcloud.tech", true},
|
||||
@ -43,7 +43,7 @@ func TestEnsureDomainsResolveSameIPv4(t *testing.T) {
|
||||
func TestEnsureIpv4(t *testing.T) {
|
||||
// NOTE(d1): DNS records get checked, so use something that is maintained
|
||||
// within the federation. if you're here because of a failing test, try `dig
|
||||
// +short <domain>` to ensure stuff matches first! If flakyness becomes an
|
||||
// +short <app>` to ensure stuff matches first! If flakyness becomes an
|
||||
// issue we can look into mocking
|
||||
domainName := "collabora.ostrom.collective.tools"
|
||||
serverName := "ostrom.collective.tools"
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"git.coopcloud.tech/toolshed/godotenv"
|
||||
"git.coopcloud.tech/coop-cloud/godotenv"
|
||||
)
|
||||
|
||||
// envVarModifiers is a list of env var modifier strings. These are added to
|
||||
@ -31,6 +31,8 @@ func ReadEnv(filePath string) (AppEnv, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("read %s from %s", envVars, filePath)
|
||||
|
||||
return envVars, nil
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ func TestEnvVarCommentsRemoved(t *testing.T) {
|
||||
|
||||
envVar, exists = envSample["SECRET_TEST_PASS_TWO_VERSION"]
|
||||
if !exists {
|
||||
t.Fatal("SECRET_TEST_PASS_TWO_VERSION env var should be present in .env.sample")
|
||||
t.Fatal("WITH_COMMENT env var should be present in .env.sample")
|
||||
}
|
||||
|
||||
if strings.Contains(envVar, "length") {
|
||||
|
@ -13,15 +13,11 @@ import (
|
||||
"github.com/docker/go-units"
|
||||
"golang.org/x/term"
|
||||
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
)
|
||||
|
||||
var BoldStyle = lipgloss.NewStyle().
|
||||
Bold(true)
|
||||
|
||||
var BoldUnderlineStyle = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Underline(true)
|
||||
|
||||
@ -47,134 +43,33 @@ func HumanDuration(timestamp int64) string {
|
||||
|
||||
// CreateTable prepares a table layout for output.
|
||||
func CreateTable() (*table.Table, error) {
|
||||
var (
|
||||
renderer = lipgloss.NewRenderer(os.Stdout)
|
||||
headerStyle = renderer.NewStyle().Bold(true).Align(lipgloss.Center)
|
||||
cellStyle = renderer.NewStyle().Padding(0, 1)
|
||||
borderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("63"))
|
||||
)
|
||||
|
||||
table := table.New().
|
||||
Border(lipgloss.ThickBorder()).
|
||||
BorderStyle(borderStyle).
|
||||
StyleFunc(func(row, col int) lipgloss.Style {
|
||||
var style lipgloss.Style
|
||||
BorderStyle(
|
||||
lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("63")),
|
||||
)
|
||||
|
||||
switch {
|
||||
case row == table.HeaderRow:
|
||||
return headerStyle
|
||||
default:
|
||||
style = cellStyle
|
||||
}
|
||||
|
||||
return style
|
||||
})
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func PrintTable(t *table.Table) error {
|
||||
if isAbraCI, ok := os.LookupEnv("ABRA_CI"); ok && isAbraCI == "1" {
|
||||
// NOTE(d1): no width limits for CI testing since we test against outputs
|
||||
log.Debug("detected ABRA_CI=1")
|
||||
fmt.Println(t)
|
||||
return nil
|
||||
return table, nil
|
||||
}
|
||||
|
||||
tWidth, _ := lipgloss.Size(t.String())
|
||||
|
||||
width, _, err := term.GetSize(0)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tWidth > width {
|
||||
t.Width(width - 10)
|
||||
if width-10 < 79 {
|
||||
// NOTE(d1): maintain standard minimum width
|
||||
table.Width(79)
|
||||
} else {
|
||||
// NOTE(d1): tests show that this produces stable border drawing
|
||||
table.Width(width - 10)
|
||||
}
|
||||
|
||||
fmt.Println(t)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// horizontal is a JoinHorizontal helper function.
|
||||
func horizontal(left, mid, right string) string {
|
||||
return lipgloss.JoinHorizontal(lipgloss.Right, left, mid, right)
|
||||
}
|
||||
|
||||
func CreateOverview(header string, rows [][]string) string {
|
||||
var borderStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.ThickBorder()).
|
||||
Padding(0, 1, 0, 1).
|
||||
BorderForeground(lipgloss.Color("63"))
|
||||
|
||||
var headerStyle = lipgloss.NewStyle().
|
||||
Underline(true).
|
||||
Bold(true).
|
||||
PaddingBottom(1)
|
||||
|
||||
var leftStyle = lipgloss.NewStyle()
|
||||
var rightStyle = lipgloss.NewStyle()
|
||||
|
||||
var longest int
|
||||
for _, row := range rows {
|
||||
if len(row[0]) > longest {
|
||||
longest = len(row[0])
|
||||
}
|
||||
}
|
||||
|
||||
var renderedRows []string
|
||||
for _, row := range rows {
|
||||
if len(row) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(row) > 2 {
|
||||
panic("CreateOverview: only accepts rows of len == 2")
|
||||
}
|
||||
|
||||
lenOffset := 4
|
||||
if len(row[0]) < longest {
|
||||
lenOffset += longest - len(row[0])
|
||||
}
|
||||
|
||||
offset := ""
|
||||
for range lenOffset {
|
||||
offset = offset + " "
|
||||
}
|
||||
|
||||
rendered := horizontal(leftStyle.Render(row[0]), offset, rightStyle.Render(row[1]))
|
||||
|
||||
if row[1] == "---" {
|
||||
rendered = horizontal(
|
||||
leftStyle.
|
||||
Bold(true).
|
||||
Underline(true).
|
||||
PaddingTop(1).
|
||||
Render(row[0]),
|
||||
offset,
|
||||
rightStyle.Render(""),
|
||||
)
|
||||
}
|
||||
|
||||
renderedRows = append(renderedRows, rendered)
|
||||
}
|
||||
|
||||
body := strings.Builder{}
|
||||
body.WriteString(
|
||||
borderStyle.Render(
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Center,
|
||||
headerStyle.Render(header),
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
renderedRows...,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return body.String()
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// ToJSON converts a lipgloss.Table to JSON representation. It's not a robust
|
||||
@ -217,6 +112,7 @@ func CreateProgressbar(length int, title string) *progressbar.ProgressBar {
|
||||
progressbar.OptionClearOnFinish(),
|
||||
progressbar.OptionSetPredictTime(false),
|
||||
progressbar.OptionShowCount(),
|
||||
progressbar.OptionFullWidth(),
|
||||
progressbar.OptionSetDescription(title),
|
||||
)
|
||||
}
|
||||
@ -257,18 +153,3 @@ func ByteCountSI(b uint64) string {
|
||||
|
||||
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
||||
}
|
||||
|
||||
// BoldDirtyDefault ensures a dirty modifier is rendered in bold.
|
||||
func BoldDirtyDefault(v string) string {
|
||||
if strings.HasSuffix(v, config.DIRTY_DEFAULT) {
|
||||
vBold := BoldStyle.Render(config.DIRTY_DEFAULT)
|
||||
v = strings.Replace(v, config.DIRTY_DEFAULT, vBold, 1)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// AddDirtyMarker adds the dirty marker to a version string.
|
||||
func AddDirtyMarker(v string) string {
|
||||
return fmt.Sprintf("%s%s", v, config.DIRTY_DEFAULT)
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBoldDirtyDefault(t *testing.T) {
|
||||
assert.Equal(t, "foo", BoldDirtyDefault("foo"))
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
@ -9,59 +11,39 @@ import (
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
// gitCloneIgnoreErr checks whether we can ignore a git clone error or not.
|
||||
func gitCloneIgnoreErr(err error) bool {
|
||||
if strings.Contains(err.Error(), "authentication required") {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "remote repository is empty") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Clone runs a git clone which accounts for different default branches.
|
||||
func Clone(dir, url string) error {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
log.Debugf("git clone: %s", dir, url)
|
||||
log.Debugf("%s does not exist, attempting to git clone from %s", dir, url)
|
||||
|
||||
_, err := git.PlainClone(dir, false, &git.CloneOptions{
|
||||
URL: url,
|
||||
Tags: git.AllTags,
|
||||
ReferenceName: plumbing.ReferenceName("refs/heads/main"),
|
||||
ReferenceName: plumbing.ReferenceName("refs/heads/master"),
|
||||
SingleBranch: true,
|
||||
})
|
||||
|
||||
if err != nil && gitCloneIgnoreErr(err) {
|
||||
log.Debugf("git clone: %s cloned successfully", dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Debug("git clone: main branch failed, attempting master branch")
|
||||
log.Debugf("cloning %s default branch failed, attempting from main branch", url)
|
||||
|
||||
_, err := git.PlainClone(dir, false, &git.CloneOptions{
|
||||
URL: url,
|
||||
Tags: git.AllTags,
|
||||
ReferenceName: plumbing.ReferenceName("refs/heads/master"),
|
||||
ReferenceName: plumbing.ReferenceName("refs/heads/main"),
|
||||
SingleBranch: true,
|
||||
})
|
||||
|
||||
if err != nil && gitCloneIgnoreErr(err) {
|
||||
log.Debugf("git clone: %s cloned successfully", dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "authentication required") {
|
||||
name := filepath.Base(dir)
|
||||
return fmt.Errorf("unable to clone %s, does %s exist?", name, url)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("git clone: %s cloned successfully", dir)
|
||||
log.Debugf("%s has been git cloned successfully", dir)
|
||||
} else {
|
||||
log.Debugf("git clone: %s already exists", dir)
|
||||
log.Debugf("%s already exists", dir)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -15,11 +15,9 @@ func Init(repoPath string, commit bool, gitName, gitEmail string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("git init: %s", err)
|
||||
}
|
||||
|
||||
if err = SwitchToMain(repo); err != nil {
|
||||
return fmt.Errorf("git branch rename: %s", err)
|
||||
}
|
||||
|
||||
log.Debugf("initialised new git repo in %s", repoPath)
|
||||
|
||||
if commit {
|
||||
@ -41,11 +39,9 @@ func Init(repoPath string, commit bool, gitName, gitEmail string) error {
|
||||
if gitName != "" && gitEmail != "" {
|
||||
author = &object.Signature{Name: gitName, Email: gitEmail}
|
||||
}
|
||||
|
||||
if _, err = commitWorktree.Commit("init", &git.CommitOptions{Author: author}); err != nil {
|
||||
return fmt.Errorf("git commit: %s", err)
|
||||
}
|
||||
|
||||
log.Debugf("init committed all files for new git repo in %s", repoPath)
|
||||
}
|
||||
|
||||
@ -58,18 +54,14 @@ func SwitchToMain(repo *git.Repository) error {
|
||||
if err := repo.Storer.SetReference(ref); err != nil {
|
||||
return fmt.Errorf("set reference: %s", err)
|
||||
}
|
||||
|
||||
cfg, err := repo.Config()
|
||||
if err != nil {
|
||||
return fmt.Errorf("repo config: %s", err)
|
||||
}
|
||||
|
||||
cfg.Init.DefaultBranch = "main"
|
||||
if err := repo.SetConfig(cfg); err != nil {
|
||||
return fmt.Errorf("repo set config: %s", err)
|
||||
}
|
||||
|
||||
log.Debug("set 'main' as the default branch")
|
||||
|
||||
log.Debug("set 'main' as the default branch.")
|
||||
return nil
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
@ -19,16 +17,12 @@ import (
|
||||
func IsClean(repoPath string) (bool, error) {
|
||||
repo, err := git.PlainOpen(repoPath)
|
||||
if err != nil {
|
||||
if errors.Is(err, git.ErrRepositoryNotExists) {
|
||||
return false, git.ErrRepositoryNotExists
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("unable to open %s: %s", repoPath, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
worktree, err := repo.Worktree()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to open worktree of %s: %s", repoPath, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
patterns, err := GetExcludesFiles()
|
||||
@ -42,14 +36,13 @@ func IsClean(repoPath string) (bool, error) {
|
||||
|
||||
status, err := worktree.Status()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to query status of %s: %s", repoPath, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if status.String() != "" {
|
||||
noNewline := strings.TrimSuffix(status.String(), "\n")
|
||||
log.Debugf("git status: %s: %s", repoPath, noNewline)
|
||||
log.Debugf("discovered git status in %s: %s", repoPath, status.String())
|
||||
} else {
|
||||
log.Debugf("git status: %s: clean", repoPath)
|
||||
log.Debugf("discovered clean git status in %s", repoPath)
|
||||
}
|
||||
|
||||
return status.IsClean(), nil
|
||||
|
@ -1,15 +0,0 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsClean(t *testing.T) {
|
||||
isClean, err := IsClean("/tmp")
|
||||
assert.Equal(t, isClean, false)
|
||||
assert.True(t, errors.Is(err, git.ErrRepositoryNotExists))
|
||||
}
|
@ -137,13 +137,6 @@ var LintRules = map[string][]LintRule{
|
||||
HowToResolve: "name a servce 'app'",
|
||||
Function: LintAppService,
|
||||
},
|
||||
{
|
||||
Ref: "R015",
|
||||
Level: "error",
|
||||
Description: "deploy labels stanza present",
|
||||
HowToResolve: "include \"deploy: labels: ...\" stanza",
|
||||
Function: LintDeployLabelsPresent,
|
||||
},
|
||||
{
|
||||
Ref: "R010",
|
||||
Level: "error",
|
||||
@ -276,21 +269,6 @@ func LintTraefikEnabled(recipe recipe.Recipe) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func LintDeployLabelsPresent(recipe recipe.Recipe) (bool, error) {
|
||||
config, err := recipe.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, service := range config.Services {
|
||||
if service.Name == "app" && service.Deploy.Labels != nil {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func LintHealthchecks(recipe recipe.Recipe) (bool, error) {
|
||||
config, err := recipe.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
@ -409,7 +387,7 @@ func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) {
|
||||
}
|
||||
|
||||
func LintMetadataFilledIn(r recipe.Recipe) (bool, error) {
|
||||
features, category, _, err := recipe.GetRecipeFeaturesAndCategory(r)
|
||||
features, category, err := recipe.GetRecipeFeaturesAndCategory(r)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package log
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
charmLog "github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
@ -32,3 +34,42 @@ var SetLevel = Logger.SetLevel
|
||||
var DebugLevel = charmLog.DebugLevel
|
||||
var SetOutput = charmLog.SetOutput
|
||||
var SetReportCaller = charmLog.SetReportCaller
|
||||
|
||||
func Styles() *charmLog.Styles {
|
||||
styles := charmLog.DefaultStyles()
|
||||
|
||||
styles.Levels = map[charmLog.Level]lipgloss.Style{
|
||||
charmLog.DebugLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(DebugLevel.String())).
|
||||
Bold(true).
|
||||
Padding(0, 1, 0, 1).
|
||||
Background(lipgloss.Color("63")).
|
||||
Foreground(lipgloss.Color("15")),
|
||||
charmLog.InfoLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(charmLog.InfoLevel.String())).
|
||||
Bold(true).
|
||||
Padding(0, 1, 0, 1).
|
||||
Background(lipgloss.Color("86")).
|
||||
Foreground(lipgloss.Color("16")),
|
||||
charmLog.WarnLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(charmLog.WarnLevel.String())).
|
||||
Bold(true).
|
||||
Padding(0, 1, 0, 1).
|
||||
Background(lipgloss.Color("192")).
|
||||
Foreground(lipgloss.Color("16")),
|
||||
charmLog.ErrorLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(charmLog.ErrorLevel.String())).
|
||||
Bold(true).
|
||||
Padding(0, 1, 0, 1).
|
||||
Background(lipgloss.Color("204")).
|
||||
Foreground(lipgloss.Color("15")),
|
||||
charmLog.FatalLevel: lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(charmLog.FatalLevel.String())).
|
||||
Bold(true).
|
||||
Padding(0, 1, 0, 1).
|
||||
Background(lipgloss.Color("134")).
|
||||
Foreground(lipgloss.Color("15")),
|
||||
}
|
||||
|
||||
return styles
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"path"
|
||||
|
||||
"coopcloud.tech/abra/pkg/envfile"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
)
|
||||
|
||||
func (r Recipe) SampleEnv() (map[string]string, error) {
|
||||
@ -30,10 +29,7 @@ func (r Recipe) GetReleaseNotes(version string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
title := formatter.BoldStyle.Render(fmt.Sprintf("%s release notes:", version))
|
||||
withTitle := fmt.Sprintf("%s\n%s\n", title, releaseNotes)
|
||||
|
||||
withTitle := fmt.Sprintf("%s release notes:\n%s", version, string(releaseNotes))
|
||||
return withTitle, nil
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package recipe
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
@ -14,20 +14,13 @@ import (
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
type EnsureContext struct {
|
||||
Chaos bool
|
||||
Offline bool
|
||||
IgnoreEnvVersion bool
|
||||
}
|
||||
|
||||
// Ensure makes sure the recipe exists, is up to date and has the specific
|
||||
// version checked out.
|
||||
func (r Recipe) Ensure(ctx EnsureContext) error {
|
||||
// Ensure makes sure the recipe exists, is up to date and has the latest version checked out.
|
||||
func (r Recipe) Ensure(chaos bool, offline bool) error {
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.Chaos {
|
||||
if chaos {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -35,16 +28,15 @@ func (r Recipe) Ensure(ctx EnsureContext) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ctx.Offline {
|
||||
if !offline {
|
||||
if err := r.EnsureUpToDate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if r.EnvVersion != "" && !ctx.IgnoreEnvVersion {
|
||||
log.Debugf("ensuring env version %s", r.EnvVersion)
|
||||
|
||||
if _, err := r.EnsureVersion(r.EnvVersion); err != nil {
|
||||
if r.Version != "" {
|
||||
log.Debugf("ensuring version %s", r.Version)
|
||||
if _, err := r.EnsureVersion(r.Version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -61,6 +53,7 @@ func (r Recipe) Ensure(ctx EnsureContext) error {
|
||||
// EnsureExists ensures that the recipe is locally cloned
|
||||
func (r Recipe) EnsureExists() error {
|
||||
if _, err := os.Stat(r.Dir); os.IsNotExist(err) {
|
||||
log.Debugf("%s does not exist, attemmpting to clone", r.Dir)
|
||||
if err := gitPkg.Clone(r.Dir, r.GitURL); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -73,43 +66,11 @@ func (r Recipe) EnsureExists() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsChaosCommit determines if a version sttring is a chaos commit or not.
|
||||
func (r Recipe) IsChaosCommit(version string) (bool, error) {
|
||||
isChaosCommit := false
|
||||
|
||||
if err := gitPkg.EnsureGitRepo(r.Dir); err != nil {
|
||||
return isChaosCommit, err
|
||||
}
|
||||
|
||||
repo, err := git.PlainOpen(r.Dir)
|
||||
if err != nil {
|
||||
return isChaosCommit, err
|
||||
}
|
||||
|
||||
tags, err := repo.Tags()
|
||||
if err != nil {
|
||||
return isChaosCommit, err
|
||||
}
|
||||
|
||||
var tagRef plumbing.ReferenceName
|
||||
if err := tags.ForEach(func(ref *plumbing.Reference) (err error) {
|
||||
if ref.Name().Short() == version {
|
||||
tagRef = ref.Name()
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return isChaosCommit, err
|
||||
}
|
||||
|
||||
if tagRef.String() == "" {
|
||||
isChaosCommit = true
|
||||
}
|
||||
|
||||
return isChaosCommit, nil
|
||||
}
|
||||
|
||||
// EnsureVersion checks whether a specific version exists for a recipe.
|
||||
func (r Recipe) EnsureVersion(version string) (bool, error) {
|
||||
if strings.Contains(version, "+ unstaged changes") {
|
||||
return true, errors.New("A chaos version is configured. \nMaybe your coworker deployed a local version of the recipe?\nOr did you forget to add --chaos?")
|
||||
}
|
||||
isChaosCommit := false
|
||||
|
||||
if err := gitPkg.EnsureGitRepo(r.Dir); err != nil {
|
||||
@ -180,7 +141,8 @@ func (r Recipe) EnsureIsClean() error {
|
||||
}
|
||||
|
||||
if !isClean {
|
||||
return fmt.Errorf("%s (%s) has locally unstaged changes?", r.Name, r.Dir)
|
||||
msg := "%s (%s) has locally unstaged changes? please commit/remove your changes before proceeding"
|
||||
return fmt.Errorf(msg, r.Name, r.Dir)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -272,23 +234,8 @@ func (r Recipe) EnsureUpToDate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDirty checks whether a recipe is dirty or not. N.B., if you call IsDirty
|
||||
// from another Recipe method, you should propagate the pointer reference (*).
|
||||
func (r *Recipe) IsDirty() error {
|
||||
isClean, err := gitPkg.IsClean(r.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isClean {
|
||||
r.Dirty = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChaosVersion constructs a chaos mode recipe version.
|
||||
func (r *Recipe) ChaosVersion() (string, error) {
|
||||
func (r Recipe) ChaosVersion() (string, error) {
|
||||
var version string
|
||||
|
||||
head, err := r.Head()
|
||||
@ -298,10 +245,15 @@ func (r *Recipe) ChaosVersion() (string, error) {
|
||||
|
||||
version = formatter.SmallSHA(head.String())
|
||||
|
||||
if err := r.IsDirty(); err != nil {
|
||||
isClean, err := gitPkg.IsClean(r.Dir)
|
||||
if err != nil {
|
||||
return version, err
|
||||
}
|
||||
|
||||
if !isClean {
|
||||
version = fmt.Sprintf("%s + unstaged changes", version)
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
@ -351,26 +303,23 @@ func (r Recipe) Tags() ([]string, error) {
|
||||
}
|
||||
|
||||
// GetRecipeVersions retrieves all recipe versions.
|
||||
func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
var warnMsg []string
|
||||
|
||||
func (r Recipe) GetRecipeVersions() (RecipeVersions, error) {
|
||||
versions := RecipeVersions{}
|
||||
|
||||
log.Debugf("git: opening repository in %s", r.Dir)
|
||||
log.Debugf("attempting to open git repository in %s", r.Dir)
|
||||
|
||||
repo, err := git.PlainOpen(r.Dir)
|
||||
if err != nil {
|
||||
return versions, warnMsg, nil
|
||||
return versions, err
|
||||
}
|
||||
|
||||
worktree, err := repo.Worktree()
|
||||
if err != nil {
|
||||
return versions, warnMsg, nil
|
||||
return versions, err
|
||||
}
|
||||
|
||||
gitTags, err := repo.Tags()
|
||||
if err != nil {
|
||||
return versions, warnMsg, nil
|
||||
return versions, err
|
||||
}
|
||||
|
||||
if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) {
|
||||
@ -388,7 +337,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("git checkout: %s in %s", ref.Name(), r.Dir)
|
||||
log.Debugf("successfully checked out %s in %s", ref.Name(), r.Dir)
|
||||
|
||||
config, err := r.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
@ -412,7 +361,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
case reference.NamedTagged:
|
||||
tag = img.(reference.NamedTagged).Tag()
|
||||
case reference.Named:
|
||||
warnMsg = append(warnMsg, fmt.Sprintf("%s service is missing image tag?", path))
|
||||
log.Warnf("%s service is missing image tag?", path)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -426,26 +375,19 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return versions, warnMsg, nil
|
||||
return versions, err
|
||||
}
|
||||
|
||||
_, err = gitPkg.CheckoutDefaultBranch(repo, r.Dir)
|
||||
if err != nil {
|
||||
return versions, warnMsg, nil
|
||||
return versions, err
|
||||
}
|
||||
|
||||
sortRecipeVersions(versions)
|
||||
|
||||
log.Debugf("collected %s for %s", versions, r.Dir)
|
||||
|
||||
var uniqueWarnings []string
|
||||
for _, w := range warnMsg {
|
||||
if !slices.Contains(uniqueWarnings, w) {
|
||||
uniqueWarnings = append(uniqueWarnings, w)
|
||||
}
|
||||
}
|
||||
|
||||
return versions, uniqueWarnings, nil
|
||||
return versions, nil
|
||||
}
|
||||
|
||||
// Head retrieves latest HEAD metadata.
|
||||
|
@ -1,39 +0,0 @@
|
||||
package recipe
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsDirty(t *testing.T) {
|
||||
r := Get("abra-test-recipe")
|
||||
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := r.IsDirty(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, r.Dirty)
|
||||
|
||||
fpath := filepath.Join(r.Dir, "foo.txt")
|
||||
f, err := os.Create(fpath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer t.Cleanup(func() {
|
||||
os.Remove(fpath)
|
||||
})
|
||||
|
||||
if err := r.IsDirty(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, r.Dirty)
|
||||
}
|
@ -2,12 +2,12 @@ package recipe
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -20,7 +20,6 @@ import (
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"coopcloud.tech/abra/pkg/web"
|
||||
"coopcloud.tech/tagcmp"
|
||||
"github.com/go-git/go-git/v5"
|
||||
)
|
||||
|
||||
// RecipeCatalogueURL is the only current recipe catalogue available.
|
||||
@ -58,6 +57,11 @@ type RecipeMeta struct {
|
||||
Website string `json:"website"`
|
||||
}
|
||||
|
||||
// TopicMeta represents a list of topics for a repository.
|
||||
type TopicMeta struct {
|
||||
Topics []string `json:"topics"`
|
||||
}
|
||||
|
||||
// LatestVersion returns the latest version of a recipe.
|
||||
func (r RecipeMeta) LatestVersion() string {
|
||||
var version string
|
||||
@ -119,20 +123,6 @@ type Features struct {
|
||||
SSO string `json:"sso"`
|
||||
}
|
||||
|
||||
func GetEnvVersionRaw(name string) (string, error) {
|
||||
var version string
|
||||
|
||||
if strings.Contains(name, ":") {
|
||||
split := strings.Split(name, ":")
|
||||
if len(split) > 2 {
|
||||
return version, fmt.Errorf("version seems invalid: %s", name)
|
||||
}
|
||||
version = split[1]
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func Get(name string) Recipe {
|
||||
version := ""
|
||||
if strings.Contains(name, ":") {
|
||||
@ -141,16 +131,11 @@ func Get(name string) Recipe {
|
||||
log.Fatalf("version seems invalid: %s", name)
|
||||
}
|
||||
name = split[0]
|
||||
|
||||
version = split[1]
|
||||
if strings.HasSuffix(version, config.DIRTY_DEFAULT) {
|
||||
version = strings.Replace(split[1], config.DIRTY_DEFAULT, "", 1)
|
||||
log.Debugf("removed dirty suffix from .env version: %s -> %s", split[1], version)
|
||||
}
|
||||
}
|
||||
|
||||
gitURL := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, name)
|
||||
sshURL := fmt.Sprintf(config.RECIPES_SSH_URL_TEMPLATE, name)
|
||||
sshURL := fmt.Sprintf(config.SSH_URL_TEMPLATE, name)
|
||||
if strings.Contains(name, "/") {
|
||||
u, err := url.Parse(name)
|
||||
if err != nil {
|
||||
@ -166,33 +151,26 @@ func Get(name string) Recipe {
|
||||
|
||||
dir := path.Join(config.RECIPES_DIR, escapeRecipeName(name))
|
||||
|
||||
r := Recipe{
|
||||
Name: name,
|
||||
EnvVersion: version,
|
||||
Dir: dir,
|
||||
GitURL: gitURL,
|
||||
SSHURL: sshURL,
|
||||
return Recipe{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Dir: dir,
|
||||
GitURL: gitURL,
|
||||
SSHURL: sshURL,
|
||||
|
||||
ComposePath: path.Join(dir, "compose.yml"),
|
||||
ReadmePath: path.Join(dir, "README.md"),
|
||||
SampleEnvPath: path.Join(dir, ".env.sample"),
|
||||
AbraShPath: path.Join(dir, "abra.sh"),
|
||||
}
|
||||
|
||||
if err := r.IsDirty(); err != nil && !errors.Is(err, git.ErrRepositoryNotExists) {
|
||||
log.Fatalf("failed to check git status of %s: %s", r.Name, err)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type Recipe struct {
|
||||
Name string
|
||||
EnvVersion string
|
||||
Dirty bool // NOTE(d1): git terminology for unstaged changes
|
||||
Dir string
|
||||
GitURL string
|
||||
SSHURL string
|
||||
Name string
|
||||
Version string
|
||||
Dir string
|
||||
GitURL string
|
||||
SSHURL string
|
||||
|
||||
ComposePath string
|
||||
ReadmePath string
|
||||
@ -200,21 +178,6 @@ type Recipe struct {
|
||||
AbraShPath string
|
||||
}
|
||||
|
||||
// String outputs a human-friendly string representation.
|
||||
func (r Recipe) String() string {
|
||||
out := fmt.Sprintf("{name: %s, ", r.Name)
|
||||
out += fmt.Sprintf("version : %s, ", r.EnvVersion)
|
||||
out += fmt.Sprintf("dirty: %v, ", r.Dirty)
|
||||
out += fmt.Sprintf("dir: %s, ", r.Dir)
|
||||
out += fmt.Sprintf("git url: %s, ", r.GitURL)
|
||||
out += fmt.Sprintf("ssh url: %s, ", r.SSHURL)
|
||||
out += fmt.Sprintf("compose: %s, ", r.ComposePath)
|
||||
out += fmt.Sprintf("readme: %s, ", r.ReadmePath)
|
||||
out += fmt.Sprintf("sample env: %s, ", r.SampleEnvPath)
|
||||
out += fmt.Sprintf("abra.sh: %s}", r.AbraShPath)
|
||||
return out
|
||||
}
|
||||
|
||||
func escapeRecipeName(recipeName string) string {
|
||||
recipeName = strings.ReplaceAll(recipeName, "/", "_")
|
||||
recipeName = strings.ReplaceAll(recipeName, ".", "_")
|
||||
@ -233,18 +196,16 @@ func GetRecipesLocal() ([]string, error) {
|
||||
return recipes, nil
|
||||
}
|
||||
|
||||
func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, []string, error) {
|
||||
var (
|
||||
category string
|
||||
warnMsgs []string
|
||||
feat = Features{}
|
||||
)
|
||||
func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, error) {
|
||||
feat := Features{}
|
||||
|
||||
log.Debugf("%s: attempt recipe metadata parse", r.ReadmePath)
|
||||
var category string
|
||||
|
||||
log.Debugf("attempting to open %s for recipe metadata parsing", r.ReadmePath)
|
||||
|
||||
readmeFS, err := ioutil.ReadFile(r.ReadmePath)
|
||||
if err != nil {
|
||||
return feat, category, warnMsgs, err
|
||||
return feat, category, err
|
||||
}
|
||||
|
||||
readmeMetadata, err := GetStringInBetween( // Find text between delimiters
|
||||
@ -253,7 +214,7 @@ func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, []string, error)
|
||||
"<!-- metadata -->", "<!-- endmetadata -->",
|
||||
)
|
||||
if err != nil {
|
||||
return feat, category, warnMsgs, err
|
||||
return feat, category, err
|
||||
}
|
||||
|
||||
readmeLines := strings.Split( // Array item from lines
|
||||
@ -297,25 +258,20 @@ func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, []string, error)
|
||||
)
|
||||
}
|
||||
if strings.Contains(val, "**Image**") {
|
||||
imageMetadata, warnings, err := GetImageMetadata(strings.TrimSpace(
|
||||
imageMetadata, err := GetImageMetadata(strings.TrimSpace(
|
||||
strings.TrimPrefix(val, "* **Image**:"),
|
||||
), r.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(warnings) > 0 {
|
||||
warnMsgs = append(warnMsgs, warnings...)
|
||||
}
|
||||
feat.Image = imageMetadata
|
||||
}
|
||||
}
|
||||
|
||||
return feat, category, warnMsgs, nil
|
||||
return feat, category, nil
|
||||
}
|
||||
|
||||
func GetImageMetadata(imageRowString, recipeName string) (Image, []string, error) {
|
||||
var warnMsgs []string
|
||||
|
||||
func GetImageMetadata(imageRowString, recipeName string) (Image, error) {
|
||||
img := Image{}
|
||||
|
||||
imgFields := strings.Split(imageRowString, ",")
|
||||
@ -326,18 +282,11 @@ func GetImageMetadata(imageRowString, recipeName string) (Image, []string, error
|
||||
|
||||
if len(imgFields) < 3 {
|
||||
if imageRowString != "" {
|
||||
warnMsgs = append(
|
||||
warnMsgs,
|
||||
fmt.Sprintf("%s: image meta has incorrect format: %s", recipeName, imageRowString),
|
||||
)
|
||||
log.Warnf("%s image meta has incorrect format: %s", recipeName, imageRowString)
|
||||
} else {
|
||||
warnMsgs = append(
|
||||
warnMsgs,
|
||||
fmt.Sprintf("%s: image meta is empty?", recipeName),
|
||||
)
|
||||
log.Warnf("%s image meta is empty?", recipeName)
|
||||
}
|
||||
|
||||
return img, warnMsgs, nil
|
||||
return img, nil
|
||||
}
|
||||
|
||||
img.Rating = imgFields[1]
|
||||
@ -347,17 +296,17 @@ func GetImageMetadata(imageRowString, recipeName string) (Image, []string, error
|
||||
|
||||
imageName, err := GetStringInBetween(recipeName, imgString, "[", "]")
|
||||
if err != nil {
|
||||
return img, warnMsgs, err
|
||||
log.Fatal(err)
|
||||
}
|
||||
img.Image = strings.ReplaceAll(imageName, "`", "")
|
||||
|
||||
imageURL, err := GetStringInBetween(recipeName, imgString, "(", ")")
|
||||
if err != nil {
|
||||
return img, warnMsgs, err
|
||||
log.Fatal(err)
|
||||
}
|
||||
img.URL = imageURL
|
||||
|
||||
return img, warnMsgs, nil
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// GetStringInBetween returns empty string if no start or end string found
|
||||
@ -548,11 +497,11 @@ type InternalTracker struct {
|
||||
type RepoCatalogue map[string]RepoMeta
|
||||
|
||||
// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
|
||||
func ReadReposMetadata(debug bool) (RepoCatalogue, error) {
|
||||
func ReadReposMetadata() (RepoCatalogue, error) {
|
||||
reposMeta := make(RepoCatalogue)
|
||||
|
||||
pageIdx := 1
|
||||
bar := formatter.CreateProgressbar(-1, "collecting recipe listing")
|
||||
bar := formatter.CreateProgressbar(-1, "retrieving recipe repos list from git.coopcloud.tech...")
|
||||
for {
|
||||
var reposList []RepoMeta
|
||||
|
||||
@ -565,32 +514,28 @@ func ReadReposMetadata(debug bool) (RepoCatalogue, error) {
|
||||
}
|
||||
|
||||
if len(reposList) == 0 {
|
||||
if !debug {
|
||||
bar.Add(1)
|
||||
}
|
||||
bar.Add(1)
|
||||
break
|
||||
}
|
||||
|
||||
for idx, repo := range reposList {
|
||||
// NOTE(d1): the "example" recipe is a temporary special case
|
||||
// https://git.coopcloud.tech/toolshed/organising/issues/666
|
||||
if repo.Name == "example" {
|
||||
continue
|
||||
var topicMeta TopicMeta
|
||||
|
||||
topicsURL := getReposTopicUrl(repo.Name)
|
||||
if err := web.ReadJSON(topicsURL, &topicMeta); err != nil {
|
||||
return reposMeta, err
|
||||
}
|
||||
|
||||
reposMeta[repo.Name] = reposList[idx]
|
||||
if slices.Contains(topicMeta.Topics, "recipe") && repo.Name != "example" {
|
||||
reposMeta[repo.Name] = reposList[idx]
|
||||
}
|
||||
}
|
||||
|
||||
pageIdx++
|
||||
|
||||
if !debug {
|
||||
bar.Add(1)
|
||||
}
|
||||
bar.Add(1)
|
||||
}
|
||||
|
||||
if err := bar.Close(); err != nil {
|
||||
return reposMeta, err
|
||||
}
|
||||
fmt.Println() // newline for spinner
|
||||
|
||||
return reposMeta, nil
|
||||
}
|
||||
@ -652,7 +597,7 @@ func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]stri
|
||||
}
|
||||
|
||||
// UpdateRepositories clones and updates all recipe repositories locally.
|
||||
func UpdateRepositories(repos RepoCatalogue, recipeName string, debug bool) error {
|
||||
func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
|
||||
var barLength int
|
||||
if recipeName != "" {
|
||||
barLength = 1
|
||||
@ -660,9 +605,9 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, debug bool) erro
|
||||
barLength = len(repos)
|
||||
}
|
||||
|
||||
cloneLimiter := limit.New(3)
|
||||
cloneLimiter := limit.New(10)
|
||||
|
||||
retrieveBar := formatter.CreateProgressbar(barLength, "retrieving recipes")
|
||||
retrieveBar := formatter.CreateProgressbar(barLength, "ensuring recipes are cloned & up-to-date...")
|
||||
ch := make(chan string, barLength)
|
||||
for _, repoMeta := range repos {
|
||||
go func(rm RepoMeta) {
|
||||
@ -671,9 +616,7 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, debug bool) erro
|
||||
|
||||
if recipeName != "" && recipeName != rm.Name {
|
||||
ch <- rm.Name
|
||||
if !debug {
|
||||
retrieveBar.Add(1)
|
||||
}
|
||||
retrieveBar.Add(1)
|
||||
return
|
||||
}
|
||||
|
||||
@ -682,9 +625,7 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, debug bool) erro
|
||||
}
|
||||
|
||||
ch <- rm.Name
|
||||
if !debug {
|
||||
retrieveBar.Add(1)
|
||||
}
|
||||
retrieveBar.Add(1)
|
||||
}(repoMeta)
|
||||
}
|
||||
|
||||
@ -692,13 +633,14 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, debug bool) erro
|
||||
<-ch // wait for everything
|
||||
}
|
||||
|
||||
if err := retrieveBar.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getReposTopicUrl retrieves the repository specific topic listing.
|
||||
func getReposTopicUrl(repoName string) string {
|
||||
return fmt.Sprintf("https://git.coopcloud.tech/api/v1/repos/coop-cloud/%s/topics", repoName)
|
||||
}
|
||||
|
||||
// ensurePathExists ensures that a path exists.
|
||||
func ensurePathExists(path string) error {
|
||||
if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
|
||||
|
@ -33,7 +33,7 @@ func TestGet(t *testing.T) {
|
||||
name: "foo:1.2.3",
|
||||
recipe: Recipe{
|
||||
Name: "foo",
|
||||
EnvVersion: "1.2.3",
|
||||
Version: "1.2.3",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/foo"),
|
||||
GitURL: "https://git.coopcloud.tech/coop-cloud/foo.git",
|
||||
SSHURL: "ssh://git@git.coopcloud.tech:2222/coop-cloud/foo.git",
|
||||
@ -60,7 +60,7 @@ func TestGet(t *testing.T) {
|
||||
name: "mygit.org/myorg/cool-recipe:1.2.4",
|
||||
recipe: Recipe{
|
||||
Name: "mygit.org/myorg/cool-recipe",
|
||||
EnvVersion: "1.2.4",
|
||||
Version: "1.2.4",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"),
|
||||
GitURL: "https://mygit.org/myorg/cool-recipe.git",
|
||||
SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
@ -105,16 +105,3 @@ func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) {
|
||||
assert.NotEqual(t, label, defaultTimeoutLabel)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirtyMarkerRemoved(t *testing.T) {
|
||||
r := Get("abra-test-recipe:1e83340e+U")
|
||||
assert.Equal(t, "1e83340e", r.EnvVersion)
|
||||
}
|
||||
|
||||
func TestGetEnvVersionRaw(t *testing.T) {
|
||||
v, err := GetEnvVersionRaw("abra-test-recipe:1e83340e+U")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, "1e83340e+U", v)
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
|
||||
stdlibErr "errors"
|
||||
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"coopcloud.tech/abra/pkg/upstream/convert"
|
||||
"github.com/docker/cli/cli/command/service/progress"
|
||||
@ -107,22 +106,13 @@ type DeployMeta struct {
|
||||
ChaosVersion string // the --chaos deployment version
|
||||
}
|
||||
|
||||
func (d DeployMeta) String() string {
|
||||
var out string
|
||||
out += fmt.Sprintf("{isDeployed: %v, ", d.IsDeployed)
|
||||
out += fmt.Sprintf("version: %s, ", d.Version)
|
||||
out += fmt.Sprintf("isChaos: %v, ", d.IsChaos)
|
||||
out += fmt.Sprintf("chaosVersion: %s}", d.ChaosVersion)
|
||||
return out
|
||||
}
|
||||
|
||||
// IsDeployed gathers metadata about an app deployment.
|
||||
func IsDeployed(ctx context.Context, cl *dockerClient.Client, stackName string) (DeployMeta, error) {
|
||||
deployMeta := DeployMeta{
|
||||
IsDeployed: false,
|
||||
Version: "unknown",
|
||||
IsChaos: false,
|
||||
ChaosVersion: config.CHAOS_DEFAULT,
|
||||
ChaosVersion: "false", // NOTE(d1): match string type used on label
|
||||
}
|
||||
|
||||
filter := filters.NewArgs()
|
||||
|
@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ABRA_VERSION="0.9.0-beta"
|
||||
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$ABRA_VERSION"
|
||||
RC_VERSION="0.10.0-rc1-beta"
|
||||
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$RC_VERSION"
|
||||
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
|
||||
RC_VERSION="0.8.0-rc1-beta"
|
||||
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$RC_VERSION"
|
||||
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" == "--rc" ]; then
|
||||
@ -40,7 +40,7 @@ function install_abra_release {
|
||||
if ! type "wget" > /dev/null 2>&1; then
|
||||
echo "'wget' is not installed, cannot proceed..."
|
||||
echo "perhaps try installing manually via the releases URL?"
|
||||
echo "https://git.coopcloud.tech/toolshed/abra/releases"
|
||||
echo "https://git.coopcloud.tech/coop-cloud/abra/releases"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -42,7 +42,7 @@ echo "========================================================================"
|
||||
echo "CLONING ABRA"
|
||||
echo "========================================================================"
|
||||
rm -rf abra
|
||||
git clone ssh://git@git.coopcloud.tech:2222/toolshed/abra.git
|
||||
git clone ssh://git@git.coopcloud.tech:2222/coop-cloud/abra.git
|
||||
cd abra
|
||||
git checkout main
|
||||
echo "========================================================================"
|
||||
|
@ -50,9 +50,6 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -65,9 +62,6 @@ teardown(){
|
||||
run $ABRA app check "$TEST_APP_DOMAIN" --chaos
|
||||
assert_success
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
@ -53,9 +53,6 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -69,9 +66,6 @@ teardown(){
|
||||
assert_success
|
||||
assert_output --partial 'baz'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
@ -24,9 +24,6 @@ teardown(){
|
||||
_rm_remote "/etc/*.txt"
|
||||
|
||||
_rm "$BATS_TMPDIR/mydir"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
||||
@test "validate app argument" {
|
||||
@ -37,42 +34,6 @@ teardown(){
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "bail if unstaged changes and no --chaos" {
|
||||
_mkdir "$BATS_TMPDIR/mydir"
|
||||
_mkfile "$BATS_TMPDIR/mydir/myfile.txt" "foo"
|
||||
|
||||
run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/mydir" app:/etc
|
||||
assert_failure
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
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" {
|
||||
_mkdir "$BATS_TMPDIR/mydir"
|
||||
_mkfile "$BATS_TMPDIR/mydir/myfile.txt" "foo"
|
||||
|
||||
run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/mydir" app:/etc --chaos
|
||||
assert_success
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
||||
@test "error if missing src/dest arguments" {
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN"
|
||||
assert_failure
|
||||
|
@ -21,8 +21,8 @@ setup(){
|
||||
|
||||
teardown(){
|
||||
_reset_recipe
|
||||
_undeploy_app
|
||||
_reset_app
|
||||
_undeploy_app
|
||||
_reset_tags
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
@ -46,9 +46,6 @@ teardown(){
|
||||
assert_success
|
||||
assert_output --partial 'foo'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
@ -65,12 +62,10 @@ teardown(){
|
||||
assert_success
|
||||
assert_output --partial 'foo'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--chaos --no-input --no-converge-checks
|
||||
assert_success
|
||||
assert_output --partial 'chaos'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@ -116,6 +111,8 @@ teardown(){
|
||||
--no-input --no-converge-checks --offline
|
||||
assert_success
|
||||
assert_output --partial "$latestCommit"
|
||||
assert_output --partial 'using latest commit'
|
||||
refute_output --partial 'chaos'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@ -133,6 +130,7 @@ teardown(){
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
assert_output --partial "${wantHash:0:8}"
|
||||
assert_output --partial 'chaos'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@ -167,32 +165,6 @@ teardown(){
|
||||
assert_output --partial 'already deployed'
|
||||
}
|
||||
|
||||
@test "no re-deploy after chaos deploy without --force/--chaos" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_failure
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "no re-deploy without --force" {
|
||||
_deploy_app
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_failure
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "re-deploy deployed app if --force/--chaos" {
|
||||
_deploy_app
|
||||
@ -200,10 +172,12 @@ teardown(){
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
assert_output --partial 'already deployed'
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
assert_output --partial 'already deployed'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@ -254,6 +228,7 @@ teardown(){
|
||||
|
||||
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
|
||||
@ -287,6 +262,7 @@ teardown(){
|
||||
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'
|
||||
}
|
||||
|
||||
@test "error if specific version does not exist" {
|
||||
@ -334,6 +310,7 @@ teardown(){
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||
assert_failure
|
||||
assert_output --partial 'unable to deploy, secrets not generated'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@ -369,72 +346,10 @@ teardown(){
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
refute_output --partial 'no such file or directory'
|
||||
|
||||
_undeploy_app
|
||||
|
||||
run $ABRA app secret rm "$TEST_APP_DOMAIN" --all
|
||||
assert_success
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "chaos version label includes dirty marker" {
|
||||
run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN" --chaos
|
||||
assert_success
|
||||
assert_output --regexp 'chaos-version.*+U'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "ignore env version checkout after deploy" {
|
||||
tagHash=$(_get_tag_hash "0.1.0+1.20.0")
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
assert_equal $(_get_current_hash) "$tagHash"
|
||||
|
||||
run $ABRA app check --ignore-env-version "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
|
||||
assert_equal $(_get_current_hash) "$(_get_head_hash)"
|
||||
|
||||
run $ABRA app check "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
|
||||
assert_equal $(_get_current_hash) "$tagHash"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "ignore env version on new deploy" {
|
||||
tagHash=$(_get_tag_hash "0.1.0+1.20.0")
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
_undeploy_app
|
||||
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --ignore-env-version
|
||||
assert_success
|
||||
assert_output --partial "$latestRelease"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "no chaos version label if no chaos" {
|
||||
_deploy_app
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
refute_output --regexp "coop-cloud.abra-test-recipe.$TEST_SERVER.chaos-version"
|
||||
}
|
||||
|
@ -25,6 +25,17 @@ teardown(){
|
||||
_reset_app
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "fails to deploy + unstaged changes version" {
|
||||
run sed -i 's/TYPE=abra-test-recipe.*/TYPE=git.coopcloud.tech\/coop-cloud\/abra-test-recipe:111111 + unstaged changes/g' \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_failure
|
||||
assert_output --partial 'A chaos version is configured. \nMaybe your coworker deployed a local version of the recipe?\nOr did you forget to add --chaos?'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "deploy version written to env" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks
|
||||
@ -91,27 +102,9 @@ teardown(){
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks --force --debug
|
||||
assert_success
|
||||
assert_output --partial "overriding env file version"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.2.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "deploy overwrites chaos deploy" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "1e83340e" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:1e83340e" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--force --no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:1e83340e" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_failure
|
||||
}
|
||||
|
@ -1,320 +0,0 @@
|
||||
#!/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(){
|
||||
_reset_recipe
|
||||
_undeploy_app
|
||||
_reset_app
|
||||
_reset_tags
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
||||
@test "first deploy with empty env version" {
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
_wipe_env_version
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*N/A'
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.* ' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*N/A'
|
||||
assert_output --regexp 'NEW VERSION.*' + "${latestRelease}"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "first deploy to latest version" {
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*N/A'
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.* ' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${latestRelease}"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "deploy, re-deploy, choose env version" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
_undeploy_app
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*N/A'
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.1.1+1.20.2"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "0.1.1+1.20.2"
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.1.1+1.20.2"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.1.1+1.20.2" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "deploy, re-deploy, ignore env version" {
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
_undeploy_app
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --ignore-env-version
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*N/A'
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "0.1.1+1.20.2"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${latestRelease}"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "deploy then chaos deploy" {
|
||||
headHash=$(_get_head_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
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 deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}+U"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${headHash:0:8}+U"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${headHash:0:8}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "chaos deploy then force deploy" {
|
||||
headHash=$(_get_head_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
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 deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}+U"
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${headHash:0:8}+U"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${latestRelease}"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "deploy then force chaos commit deploy" {
|
||||
headHash=$(_get_head_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "${headHash:0:8}" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${headHash:0:8}"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${headHash:0:8}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "chaos deploy then chaos deploy with unstaged" {
|
||||
headHash=$(_get_head_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*N/A'
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${headHash:0:8}"
|
||||
|
||||
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 deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}"
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}+U"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${headHash:0:8}+U"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${headHash:0:8}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
||||
@test "hash deploy then force deploy" {
|
||||
headHash=$(_get_head_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "${headHash:0:8}" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}"
|
||||
|
||||
# new deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${headHash:0:8}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${latestRelease}"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
@ -27,18 +27,18 @@ teardown(){
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "deploy remote recipe" {
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/toolshed\/abra-test-recipe/g' \
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/coop-cloud\/abra-test-recipe/g' \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||
assert_success
|
||||
assert_output --partial "git.coopcloud.tech/toolshed/abra-test-recipe"
|
||||
assert_output --partial "git.coopcloud.tech/coop-cloud/abra-test-recipe"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "deploy remote recipe with version" {
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/toolshed\/abra-test-recipe:0.2.0+1.21.0/g' \
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/coop-cloud\/abra-test-recipe:0.2.0+1.21.0/g' \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
@ -49,7 +49,7 @@ teardown(){
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "deploy remote recipe with chaos commit" {
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/toolshed\/abra-test-recipe:1e83340e/g' \
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/coop-cloud\/abra-test-recipe:1e83340e/g' \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
@ -60,14 +60,14 @@ teardown(){
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "remote recipe version written to env" {
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/toolshed\/abra-test-recipe/g' \
|
||||
run sed -i 's/TYPE=abra-test-recipe:.*/TYPE=git.coopcloud.tech\/coop-cloud\/abra-test-recipe/g' \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run grep -q "TYPE=git.coopcloud.tech\/toolshed\/abra-test-recipe:$(_latest_release)" \
|
||||
run grep -q "TYPE=git.coopcloud.tech\/coop-cloud\/abra-test-recipe:$(_latest_release)" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
#!/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(){
|
||||
_reset_recipe
|
||||
_undeploy_app
|
||||
_reset_app
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
||||
@test "validate app argument" {
|
||||
run $ABRA app env
|
||||
assert_failure
|
||||
|
||||
run $ABRA app env DOESNTEXIST
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "show env version" {
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app env "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
assert_output --partial "$latestRelease"
|
||||
}
|
||||
|
||||
@test "show env version despite --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 env "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
#!/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
|
||||
_ensure_catalogue
|
||||
}
|
||||
|
||||
teardown(){
|
||||
_reset_recipe
|
||||
_undeploy_app
|
||||
_reset_app
|
||||
_reset_tags
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
||||
@test "validate app argument" {
|
||||
run $ABRA app labels
|
||||
assert_failure
|
||||
|
||||
run $ABRA app labels DOESNTEXIST
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@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'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN" --no-input
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@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'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN" --chaos
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "ensure recipe up to date if no --offline" {
|
||||
wantHash=$(_get_n_hash 3)
|
||||
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" reset --hard HEAD~3
|
||||
assert_success
|
||||
|
||||
assert_equal $(_get_current_hash) "$wantHash"
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
|
||||
assert_equal $(_get_head_hash) $(_get_current_hash)
|
||||
}
|
||||
|
||||
@test "ensure recipe not up to date if --offline" {
|
||||
_ensure_env_version "0.1.0+1.20.0"
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag -d "$latestRelease"
|
||||
assert_success
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN" --offline
|
||||
assert_success
|
||||
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag -l
|
||||
refute_output --partial "$latestRelease"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "show deploy labels when deployed" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
assert_output --partial 'com.docker.stack.image'
|
||||
}
|
@ -60,6 +60,11 @@ teardown(){
|
||||
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"
|
||||
@ -89,6 +94,33 @@ teardown(){
|
||||
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
|
||||
|
||||
|
@ -20,10 +20,6 @@ setup(){
|
||||
teardown(){
|
||||
_rm_app
|
||||
_reset_recipe
|
||||
_reset_tags
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
||||
@test "create new app" {
|
||||
@ -51,22 +47,25 @@ teardown(){
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
|
||||
assert_equal $(_get_tag_hash 0.3.0+1.21.0) $(_get_current_hash)
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.3.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "create new app with chaos commit" {
|
||||
tagHash=$(_get_tag_hash "0.3.0+1.21.0")
|
||||
|
||||
run $ABRA app new "$TEST_RECIPE" "$tagHash" \
|
||||
run $ABRA app new "$TEST_RECIPE" 1e83340e \
|
||||
--no-input \
|
||||
--server "$TEST_SERVER" \
|
||||
--domain "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${tagHash}" \
|
||||
currentHash=$(_get_current_hash)
|
||||
assert_equal 1e83340e ${currentHash:0:8}
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:1e83340e" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
@ -102,9 +101,6 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -126,9 +122,6 @@ teardown(){
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -174,77 +167,17 @@ teardown(){
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "generate secrets" {
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
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'
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "app new from chaos recipe" {
|
||||
currentHash=$(_get_current_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
|
||||
run $ABRA app new "$TEST_RECIPE" \
|
||||
--no-input \
|
||||
--server "$TEST_SERVER" \
|
||||
--domain "$TEST_APP_DOMAIN" \
|
||||
--secrets \
|
||||
--chaos
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_output --partial "version: $latestRelease"
|
||||
assert_output --partial "chaos: ${currentHash:0:8}"
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${currentHash:0:8}+U" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "app new, no releases, from chaos recipe" {
|
||||
currentHash=$(_get_current_hash)
|
||||
_remove_tags
|
||||
|
||||
run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
|
||||
run $ABRA app new "$TEST_RECIPE" \
|
||||
--no-input \
|
||||
--server "$TEST_SERVER" \
|
||||
--domain "$TEST_APP_DOMAIN" \
|
||||
--secrets \
|
||||
--chaos
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_output --partial "version: unknown"
|
||||
assert_output --partial "chaos: ${currentHash:0:8}"
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${currentHash:0:8}+U" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
@ -55,9 +55,6 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -73,9 +70,6 @@ teardown(){
|
||||
run $ABRA app ps --chaos "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -123,8 +117,6 @@ teardown(){
|
||||
@test "show ps report" {
|
||||
_deploy_app
|
||||
|
||||
_ensure_env_version "$(_latest_release)"
|
||||
|
||||
run $ABRA app ps "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
assert_output --partial 'app'
|
||||
@ -145,16 +137,3 @@ teardown(){
|
||||
assert_output --partial "$latestRelease"
|
||||
assert_output --partial "${headHash:0:8}" # is a chaos deploy
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "ensure live chaos commit is shown" {
|
||||
headHash=$(_get_head_hash)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0f5a0570" --no-input
|
||||
assert_success
|
||||
|
||||
run $ABRA app ps "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
assert_output --partial "0f5a0570" # is not latest HEAD
|
||||
refute_output --partial "${headHash:0:8}"
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ setup(){
|
||||
}
|
||||
|
||||
teardown(){
|
||||
_reset_app
|
||||
_undeploy_app
|
||||
_reset_recipe
|
||||
}
|
||||
@ -130,17 +129,6 @@ teardown(){
|
||||
assert_output --partial "0.1.0+1.20.0"
|
||||
}
|
||||
|
||||
@test "force rollback with no available downgrades" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app rollback "$TEST_APP_DOMAIN" \
|
||||
--force --no-input --no-converge-checks
|
||||
assert_success
|
||||
assert_output --partial "0.1.0+1.20.0"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "rollback to a version 2 tags behind" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks
|
||||
@ -186,16 +174,3 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial "not a known version"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "no chaos version label if no chaos" {
|
||||
_deploy_app
|
||||
|
||||
run $ABRA app rollback "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
refute_output --regexp "coop-cloud.abra-test-recipe.$TEST_SERVER.chaos-version"
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ teardown(){
|
||||
}
|
||||
|
||||
@test "rollback writes version to env file" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
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 grep -q "TYPE=abra-test-recipe:0.2.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
@ -35,6 +35,8 @@ teardown(){
|
||||
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks --debug
|
||||
assert_success
|
||||
assert_output --partial "0.1.0+1.20.0"
|
||||
assert_output --partial "overriding env file version"
|
||||
|
||||
run grep -q "TYPE=abra-test-recipe:0.1.0+1.20.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
|
@ -1,102 +0,0 @@
|
||||
#!/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(){
|
||||
_undeploy_app
|
||||
_reset_app
|
||||
_reset_recipe
|
||||
}
|
||||
|
||||
@test "deploy then rollback" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# rollback
|
||||
assert_output --regexp 'VERSION.*' + "0.1.0+1.20.0"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.1.0+1.20.0"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.1.0+1.20.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "force rollback" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# rollback
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.2.0+1.21.0"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.2.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "app rollback no .env version" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
_wipe_env_version
|
||||
|
||||
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# rollback
|
||||
assert_output --regexp 'VERSION.*' + "0.1.0+1.20.0"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*N/A'
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.2.0+1.21.0"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
@ -131,9 +131,6 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -274,9 +271,6 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
@ -325,9 +319,6 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial 'locally unstaged changes'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_equal "$(_git_status)" "?? foo"
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
||||
|
@ -45,4 +45,5 @@ teardown(){
|
||||
|
||||
sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
|
||||
assert_output --partial "$sanitisedDomainName_app"
|
||||
assert_output --partial "nginx"
|
||||
}
|
||||
|
@ -107,10 +107,10 @@ teardown(){
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "undeploy chaos deployment" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --chaos
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
|
||||
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
_undeploy_app
|
||||
|
||||
# NOTE(d1): ensure chaos undeploy
|
||||
assert_output --partial ${_get_current_hash:0:8}
|
||||
|
@ -1,60 +0,0 @@
|
||||
#!/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
|
||||
_ensure_catalogue
|
||||
}
|
||||
|
||||
teardown(){
|
||||
_reset_recipe
|
||||
_undeploy_app
|
||||
_reset_app
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "undeploy version written to env" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.1.0+1.20.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run sed -i "s/TYPE=$TEST_RECIPE:.*/TYPE=$TEST_RECIPE/g" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.1.0+1.20.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "chaos commit written to env" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --chaos
|
||||
|
||||
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${_get_current_hash:0:8}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
#!/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
|
||||
_ensure_catalogue
|
||||
}
|
||||
|
||||
teardown(){
|
||||
_reset_recipe
|
||||
_undeploy_app
|
||||
_reset_app
|
||||
}
|
||||
|
||||
@test "deploy and undeploy" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.1.0+1.20.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "0.1.0+1.20.0"
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.1.0+1.20.0"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.1.0+1.20.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "chaos deploy and undeploy" {
|
||||
headHash=$(_get_head_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${headHash:0:8}"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${headHash:0:8}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "chaos deploy with unstaged commits and undeploy" {
|
||||
headHash=$(_get_head_hash)
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
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 deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --chaos
|
||||
assert_success
|
||||
|
||||
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'CHAOS.*' + "${headHash:0:8}+U"
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "${latestRelease}"
|
||||
assert_output --regexp 'NEW VERSION.*' + "${headHash:0:8}+U"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${headHash:0:8}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo"
|
||||
}
|
@ -15,14 +15,12 @@ teardown_file(){
|
||||
setup(){
|
||||
load "$PWD/tests/integration/helpers/common"
|
||||
_common_setup
|
||||
_ensure_catalogue
|
||||
}
|
||||
|
||||
teardown(){
|
||||
_reset_recipe
|
||||
_reset_app
|
||||
_undeploy_app
|
||||
_reset_app # NOTE(d1): _undeploy_app writes version
|
||||
}
|
||||
|
||||
@test "validate app argument" {
|
||||
@ -33,16 +31,6 @@ teardown(){
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@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 upgrade "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||
assert_failure
|
||||
assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE"
|
||||
}
|
||||
|
||||
# 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
|
||||
@ -128,19 +116,6 @@ teardown(){
|
||||
assert_output --partial '0.2.0+1.21.0'
|
||||
}
|
||||
|
||||
@test "force upgrade with no available upgrades" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" \
|
||||
--force --no-input --no-converge-checks
|
||||
assert_success
|
||||
assert_output --partial "$latestRelease"
|
||||
}
|
||||
|
||||
# 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
|
||||
@ -239,16 +214,3 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial "not a known version"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "no chaos version label if no chaos" {
|
||||
_deploy_app
|
||||
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app labels "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
refute_output --regexp "coop-cloud.abra-test-recipe.$TEST_SERVER.chaos-version"
|
||||
}
|
||||
|
@ -22,19 +22,20 @@ teardown(){
|
||||
_reset_recipe
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "upgrade writes version to env file" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
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 grep -q "TYPE=abra-test-recipe:0.1.0+1.20.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
--no-input --no-converge-checks --debug
|
||||
assert_success
|
||||
assert_output --partial "0.2.0+1.21.0"
|
||||
assert_output --partial "overriding env file version"
|
||||
|
||||
run grep -q "TYPE=abra-test-recipe:0.2.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
|
@ -1,105 +0,0 @@
|
||||
#!/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(){
|
||||
_undeploy_app
|
||||
_reset_recipe
|
||||
}
|
||||
|
||||
@test "deploy then upgrade" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.1.0+1.20.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# upgrade
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "0.1.0+1.20.0"
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.2.0+1.21.0"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.2.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "force upgrade" {
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# upgrade
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.2.0+1.21.0"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.2.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "app upgrade no .env version" {
|
||||
latestRelease=$(_latest_release)
|
||||
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \
|
||||
--no-input --no-converge-checks
|
||||
assert_success
|
||||
|
||||
_wipe_env_version
|
||||
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" \
|
||||
--no-input --no-converge-checks --force
|
||||
assert_success
|
||||
|
||||
# current deployment
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# upgrade
|
||||
assert_output --regexp 'VERSION.*' + "0.2.0+1.21.0"
|
||||
assert_output --regexp 'CHAOS.*false'
|
||||
|
||||
# env version
|
||||
assert_output --regexp 'CURRENT VERSION.*N/A'
|
||||
assert_output --regexp 'NEW VERSION.*' + "0.2.0+1.21.0"
|
||||
|
||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
@ -6,13 +6,9 @@ setup(){
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "generate catalogue" {
|
||||
@test "generate entire catalogue" {
|
||||
run $ABRA catalogue generate
|
||||
assert_success
|
||||
|
||||
for d in $(ls $ABRA_DIR/recipes); do
|
||||
assert_exists "$ABRA_DIR/recipes/$d/.git"
|
||||
done
|
||||
}
|
||||
|
||||
@test "error if unstaged changes" {
|
||||
@ -45,5 +41,4 @@ setup(){
|
||||
@test "generate only specific recipe" {
|
||||
run $ABRA catalogue generate gitea
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/recipes/gitea/.git"
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user