Add images & configs to deploy overview #657
@ -6,14 +6,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/app"
|
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/envfile"
|
|
||||||
"coopcloud.tech/abra/pkg/secret"
|
"coopcloud.tech/abra/pkg/secret"
|
||||||
|
|
||||||
appPkg "coopcloud.tech/abra/pkg/app"
|
appPkg "coopcloud.tech/abra/pkg/app"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
|
"coopcloud.tech/abra/pkg/deploy"
|
||||||
"coopcloud.tech/abra/pkg/dns"
|
"coopcloud.tech/abra/pkg/dns"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/i18n"
|
"coopcloud.tech/abra/pkg/i18n"
|
||||||
@ -129,13 +128,9 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
|
if err := deploy.MergeAbraShEnv(app.Recipe, app.Env); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
for k, v := range abraShEnv {
|
|
||||||
app.Env[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
|
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -194,12 +189,34 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
|||||||
deployedVersion = deployMeta.Version
|
deployedVersion = deployMeta.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gather secrets
|
||||||
|
secretInfo, err := deploy.GatherSecretsForDeploy(cl, app)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather configs
|
||||||
|
configInfo, err := deploy.GatherConfigsForDeploy(cl, app, compose, app.Env, internal.ShowUnchanged)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather images
|
||||||
|
imageInfo, err := deploy.GatherImagesForDeploy(cl, app, compose, internal.ShowUnchanged)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show deploy overview
|
||||||
if err := internal.DeployOverview(
|
if err := internal.DeployOverview(
|
||||||
app,
|
app,
|
||||||
deployedVersion,
|
deployedVersion,
|
||||||
toDeployVersion,
|
toDeployVersion,
|
||||||
"",
|
"",
|
||||||
deployWarnMessages,
|
deployWarnMessages,
|
||||||
|
secretInfo,
|
||||||
|
configInfo,
|
||||||
|
imageInfo,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -245,7 +262,7 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLatestVersionOrCommit(app app.App) (string, error) {
|
func getLatestVersionOrCommit(app appPkg.App) (string, error) {
|
||||||
versions, err := app.Recipe.Tags()
|
versions, err := app.Recipe.Tags()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -280,7 +297,7 @@ func validateArgsAndFlags(args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSecrets(cl *dockerClient.Client, app app.App) error {
|
func validateSecrets(cl *dockerClient.Client, app appPkg.App) error {
|
||||||
secStats, err := secret.PollSecretsStatus(cl, app)
|
secStats, err := secret.PollSecretsStatus(cl, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -295,7 +312,7 @@ func validateSecrets(cl *dockerClient.Client, app app.App) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDeployVersion(cliArgs []string, deployMeta stack.DeployMeta, app app.App) (string, error) {
|
func getDeployVersion(cliArgs []string, deployMeta stack.DeployMeta, app appPkg.App) (string, error) {
|
||||||
// Chaos mode overrides everything
|
// Chaos mode overrides everything
|
||||||
if internal.Chaos {
|
if internal.Chaos {
|
||||||
v, err := app.Recipe.ChaosVersion()
|
v, err := app.Recipe.ChaosVersion()
|
||||||
@ -375,4 +392,12 @@ func init() {
|
|||||||
false,
|
false,
|
||||||
i18n.G("deploy latest recipe version"),
|
i18n.G("deploy latest recipe version"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AppDeployCommand.Flags().BoolVarP(
|
||||||
|
&internal.ShowUnchanged,
|
||||||
|
i18n.G("show-unchanged"),
|
||||||
|
i18n.G("U"),
|
||||||
|
false,
|
||||||
|
i18n.G("show all configs & images, including unchanged ones"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/app"
|
|
||||||
appPkg "coopcloud.tech/abra/pkg/app"
|
appPkg "coopcloud.tech/abra/pkg/app"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/envfile"
|
"coopcloud.tech/abra/pkg/deploy"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/i18n"
|
"coopcloud.tech/abra/pkg/i18n"
|
||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
@ -155,13 +154,9 @@ beforehand. See "abra app backup" for more.`),
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
|
if err := deploy.MergeAbraShEnv(app.Recipe, app.Env); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
for k, v := range abraShEnv {
|
|
||||||
app.Env[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
|
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -190,6 +185,24 @@ beforehand. See "abra app backup" for more.`),
|
|||||||
}
|
}
|
||||||
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
||||||
|
|
||||||
|
// Gather secrets
|
||||||
|
secretInfo, err := deploy.GatherSecretsForDeploy(cl, app)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather configs
|
||||||
|
configInfo, err := deploy.GatherConfigsForDeploy(cl, app, compose, app.Env, internal.ShowUnchanged)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather images
|
||||||
|
imageInfo, err := deploy.GatherImagesForDeploy(cl, app, compose, internal.ShowUnchanged)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE(d1): no release notes implemeneted for rolling back
|
// NOTE(d1): no release notes implemeneted for rolling back
|
||||||
if err := internal.DeployOverview(
|
if err := internal.DeployOverview(
|
||||||
app,
|
app,
|
||||||
@ -197,6 +210,9 @@ beforehand. See "abra app backup" for more.`),
|
|||||||
chosenDowngrade,
|
chosenDowngrade,
|
||||||
"",
|
"",
|
||||||
downgradeWarnMessages,
|
downgradeWarnMessages,
|
||||||
|
secretInfo,
|
||||||
|
configInfo,
|
||||||
|
imageInfo,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -267,7 +283,7 @@ func chooseDowngrade(
|
|||||||
// validateDownpgradeVersionArg validates the specific version.
|
// validateDownpgradeVersionArg validates the specific version.
|
||||||
func validateDowngradeVersionArg(
|
func validateDowngradeVersionArg(
|
||||||
specificVersion string,
|
specificVersion string,
|
||||||
app app.App,
|
app appPkg.App,
|
||||||
deployMeta stack.DeployMeta,
|
deployMeta stack.DeployMeta,
|
||||||
) error {
|
) error {
|
||||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||||
@ -346,4 +362,12 @@ func init() {
|
|||||||
false,
|
false,
|
||||||
i18n.G("disable converge logic checks"),
|
i18n.G("disable converge logic checks"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AppRollbackCommand.Flags().BoolVarP(
|
||||||
|
&internal.ShowUnchanged,
|
||||||
|
i18n.G("show-unchanged"),
|
||||||
|
i18n.G("U"),
|
||||||
|
false,
|
||||||
|
i18n.G("show all configs & images, including unchanged ones"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,9 @@ Passing "--prune/-p" does not remove those volumes.`),
|
|||||||
config.NO_DOMAIN_DEFAULT,
|
config.NO_DOMAIN_DEFAULT,
|
||||||
"",
|
"",
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/app"
|
|
||||||
appPkg "coopcloud.tech/abra/pkg/app"
|
appPkg "coopcloud.tech/abra/pkg/app"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/envfile"
|
"coopcloud.tech/abra/pkg/deploy"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/i18n"
|
"coopcloud.tech/abra/pkg/i18n"
|
||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
@ -168,13 +167,9 @@ beforehand. See "abra app backup" for more.`),
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
|
if err := deploy.MergeAbraShEnv(app.Recipe, app.Env); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
for k, v := range abraShEnv {
|
|
||||||
app.Env[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
|
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -216,6 +211,24 @@ beforehand. See "abra app backup" for more.`),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gather secrets
|
||||||
|
secretInfo, err := deploy.GatherSecretsForDeploy(cl, app)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather configs
|
||||||
|
configInfo, err := deploy.GatherConfigsForDeploy(cl, app, compose, app.Env, internal.ShowUnchanged)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather images
|
||||||
|
imageInfo, err := deploy.GatherImagesForDeploy(cl, app, compose, internal.ShowUnchanged)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if showReleaseNotes {
|
if showReleaseNotes {
|
||||||
fmt.Print(upgradeReleaseNotes)
|
fmt.Print(upgradeReleaseNotes)
|
||||||
return
|
return
|
||||||
@ -234,6 +247,9 @@ beforehand. See "abra app backup" for more.`),
|
|||||||
chosenUpgrade,
|
chosenUpgrade,
|
||||||
upgradeReleaseNotes,
|
upgradeReleaseNotes,
|
||||||
upgradeWarnMessages,
|
upgradeWarnMessages,
|
||||||
|
secretInfo,
|
||||||
|
configInfo,
|
||||||
|
imageInfo,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -311,7 +327,7 @@ func chooseUpgrade(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getReleaseNotes(
|
func getReleaseNotes(
|
||||||
app app.App,
|
app appPkg.App,
|
||||||
versions []string,
|
versions []string,
|
||||||
chosenUpgrade string,
|
chosenUpgrade string,
|
||||||
deployMeta stack.DeployMeta,
|
deployMeta stack.DeployMeta,
|
||||||
@ -356,7 +372,7 @@ func getReleaseNotes(
|
|||||||
|
|
||||||
// ensureUpgradesAvailable ensures that there are available upgrades.
|
// ensureUpgradesAvailable ensures that there are available upgrades.
|
||||||
func ensureUpgradesAvailable(
|
func ensureUpgradesAvailable(
|
||||||
app app.App,
|
app appPkg.App,
|
||||||
versions []string,
|
versions []string,
|
||||||
availableUpgrades *[]string,
|
availableUpgrades *[]string,
|
||||||
deployMeta stack.DeployMeta,
|
deployMeta stack.DeployMeta,
|
||||||
@ -388,7 +404,7 @@ func ensureUpgradesAvailable(
|
|||||||
// validateUpgradeVersionArg validates the specific version.
|
// validateUpgradeVersionArg validates the specific version.
|
||||||
func validateUpgradeVersionArg(
|
func validateUpgradeVersionArg(
|
||||||
specificVersion string,
|
specificVersion string,
|
||||||
app app.App,
|
app appPkg.App,
|
||||||
deployMeta stack.DeployMeta,
|
deployMeta stack.DeployMeta,
|
||||||
) error {
|
) error {
|
||||||
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
parsedSpecificVersion, err := tagcmp.Parse(specificVersion)
|
||||||
@ -415,7 +431,7 @@ func validateUpgradeVersionArg(
|
|||||||
|
|
||||||
// ensureDeployed ensures the app is deployed and if so, returns deployment
|
// ensureDeployed ensures the app is deployed and if so, returns deployment
|
||||||
// meta info.
|
// meta info.
|
||||||
func ensureDeployed(cl *dockerClient.Client, app app.App) (stack.DeployMeta, error) {
|
func ensureDeployed(cl *dockerClient.Client, app appPkg.App) (stack.DeployMeta, error) {
|
||||||
log.Debug(i18n.G("checking whether %s is already deployed", app.StackName()))
|
log.Debug(i18n.G("checking whether %s is already deployed", app.StackName()))
|
||||||
|
|
||||||
deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName())
|
deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName())
|
||||||
@ -464,4 +480,12 @@ func init() {
|
|||||||
false,
|
false,
|
||||||
i18n.G("only show release notes"),
|
i18n.G("only show release notes"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AppUpgradeCommand.Flags().BoolVarP(
|
||||||
|
&internal.ShowUnchanged,
|
||||||
|
i18n.G("show-unchanged"),
|
||||||
|
i18n.G("U"),
|
||||||
|
false,
|
||||||
|
i18n.G("show all configs & images, including unchanged ones"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -19,4 +19,5 @@ var (
|
|||||||
Minor bool
|
Minor bool
|
||||||
NoDomainChecks bool
|
NoDomainChecks bool
|
||||||
Patch bool
|
Patch bool
|
||||||
|
ShowUnchanged bool
|
||||||
)
|
)
|
||||||
|
@ -50,6 +50,9 @@ func DeployOverview(
|
|||||||
toDeployVersion string,
|
toDeployVersion string,
|
||||||
releaseNotes string,
|
releaseNotes string,
|
||||||
warnMessages []string,
|
warnMessages []string,
|
||||||
|
secrets []string,
|
||||||
|
configs []string,
|
||||||
|
images []string,
|
||||||
) error {
|
) error {
|
||||||
deployConfig := "compose.yml"
|
deployConfig := "compose.yml"
|
||||||
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
||||||
@ -80,6 +83,24 @@ func DeployOverview(
|
|||||||
{i18n.G("CURRENT DEPLOYMENT"), formatter.BoldDirtyDefault(deployedVersion)},
|
{i18n.G("CURRENT DEPLOYMENT"), formatter.BoldDirtyDefault(deployedVersion)},
|
||||||
{i18n.G("ENV VERSION"), formatter.BoldDirtyDefault(envVersion)},
|
{i18n.G("ENV VERSION"), formatter.BoldDirtyDefault(envVersion)},
|
||||||
{i18n.G("NEW DEPLOYMENT"), formatter.BoldDirtyDefault(toDeployVersion)},
|
{i18n.G("NEW DEPLOYMENT"), formatter.BoldDirtyDefault(toDeployVersion)},
|
||||||
|
{"", ""},
|
||||||
|
{i18n.G("IMAGES"), strings.Join(images, "\n")},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(secrets) > 0 {
|
||||||
|
secretsRows := [][]string{
|
||||||
|
{"", ""},
|
||||||
|
{i18n.G("SECRETS"), strings.Join(secrets, "\n")},
|
||||||
|
}
|
||||||
|
rows = append(rows, secretsRows...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(configs) > 0 {
|
||||||
|
configsRows := [][]string{
|
||||||
|
{"", ""},
|
||||||
|
{i18n.G("CONFIGS"), strings.Join(configs, "\n")},
|
||||||
|
}
|
||||||
|
rows = append(rows, configsRows...)
|
||||||
}
|
}
|
||||||
|
|
||||||
deployType := getDeployType(deployedVersion, toDeployVersion)
|
deployType := getDeployType(deployedVersion, toDeployVersion)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
appPkg "coopcloud.tech/abra/pkg/app"
|
appPkg "coopcloud.tech/abra/pkg/app"
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
|
"coopcloud.tech/abra/pkg/deploy"
|
||||||
"coopcloud.tech/abra/pkg/envfile"
|
"coopcloud.tech/abra/pkg/envfile"
|
||||||
"coopcloud.tech/abra/pkg/i18n"
|
"coopcloud.tech/abra/pkg/i18n"
|
||||||
"coopcloud.tech/abra/pkg/lint"
|
"coopcloud.tech/abra/pkg/lint"
|
||||||
@ -335,21 +336,6 @@ func processRecipeRepoVersion(r recipe.Recipe, version string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeAbraShEnv merges abra.sh env vars into the app env vars.
|
|
||||||
func mergeAbraShEnv(recipe recipe.Recipe, env envfile.AppEnv) error {
|
|
||||||
abraShEnv, err := envfile.ReadAbraShEnvVars(recipe.AbraShPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range abraShEnv {
|
|
||||||
log.Debugf("read v:%s k: %s", v, k)
|
|
||||||
env[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// createDeployConfig merges and enriches the compose config for the deployment.
|
// createDeployConfig merges and enriches the compose config for the deployment.
|
||||||
func createDeployConfig(r recipe.Recipe, stackName string, env envfile.AppEnv) (*composetypes.Config, stack.Deploy, error) {
|
func createDeployConfig(r recipe.Recipe, stackName string, env envfile.AppEnv) (*composetypes.Config, stack.Deploy, error) {
|
||||||
env["STACK_NAME"] = stackName
|
env["STACK_NAME"] = stackName
|
||||||
@ -444,7 +430,7 @@ func upgrade(cl *dockerclient.Client, stackName, recipeName, upgradeVersion stri
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mergeAbraShEnv(app.Recipe, app.Env); err != nil {
|
if err = deploy.MergeAbraShEnv(app.Recipe, app.Env); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/i18n"
|
"coopcloud.tech/abra/pkg/i18n"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
@ -37,3 +38,12 @@ func RemoveConfigs(cl *client.Client, ctx context.Context, configNames []string,
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetConfigNameAndVersion parses a full config name like `app_example_com_someconf_v1` to extract name and version, ("someconf", "v1")
|
||||||
|
func GetConfigNameAndVersion(fullName string, stackName string) (string, string, error) {
|
||||||
|
name := strings.TrimPrefix(fullName, stackName+"_")
|
||||||
|
if lastUnderscore := strings.LastIndex(name, "_"); lastUnderscore != -1 {
|
||||||
|
return name[0:lastUnderscore], name[lastUnderscore+1:], nil
|
||||||
3wordchant marked this conversation as resolved
|
|||||||
|
}
|
||||||
|
return "", "", errors.New(i18n.G("can't parse version from config '%s'", fullName))
|
||||||
|
}
|
||||||
|
89
pkg/client/configs_test.go
Normal file
89
pkg/client/configs_test.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetConfigNameAndVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fullName string
|
||||||
|
stackName string
|
||||||
|
expected string
|
||||||
|
expectedVer string
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid config with version",
|
||||||
|
fullName: "myapp_database_v2",
|
||||||
|
stackName: "myapp",
|
||||||
|
expected: "database",
|
||||||
|
expectedVer: "v2",
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid config with numeric version",
|
||||||
|
fullName: "myapp_redis_1",
|
||||||
|
stackName: "myapp",
|
||||||
|
expected: "redis",
|
||||||
|
expectedVer: "1",
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "config without underscore in name",
|
||||||
|
fullName: "myapp_db_v1",
|
||||||
|
stackName: "myapp",
|
||||||
|
expected: "db",
|
||||||
|
expectedVer: "v1",
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "config with multiple underscores",
|
||||||
|
fullName: "myapp_my_database_v3",
|
||||||
|
stackName: "myapp",
|
||||||
|
expected: "my_database",
|
||||||
|
expectedVer: "v3",
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid config - no version",
|
||||||
|
fullName: "myapp_database",
|
||||||
|
stackName: "myapp",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty config name",
|
||||||
|
fullName: "myapp__v1",
|
||||||
|
stackName: "myapp",
|
||||||
|
expected: "",
|
||||||
|
expectedVer: "v1",
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong stack prefix",
|
||||||
|
fullName: "otherapp_database_v1",
|
||||||
|
stackName: "myapp",
|
||||||
|
expected: "otherapp_database",
|
||||||
|
expectedVer: "v1",
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
name, version, err := GetConfigNameAndVersion(tt.fullName, tt.stackName)
|
||||||
|
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, name)
|
||||||
|
assert.Empty(t, version)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.expected, name)
|
||||||
|
assert.Equal(t, tt.expectedVer, version)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -17,3 +17,11 @@ func StoreSecret(cl *client.Client, secretName, secretValue string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSecretNames(secrets []swarm.Secret) []string {
|
||||||
|
var secretNames []string
|
||||||
|
for _, secret := range secrets {
|
||||||
|
secretNames = append(secretNames, secret.Spec.Name)
|
||||||
|
}
|
||||||
|
return secretNames
|
||||||
|
}
|
||||||
|
58
pkg/client/secret_test.go
Normal file
58
pkg/client/secret_test.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetSecretNames(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
secrets []swarm.Secret
|
||||||
|
expected []string
|
||||||
|
description string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty secrets list",
|
||||||
|
secrets: []swarm.Secret{},
|
||||||
|
expected: nil,
|
||||||
|
description: "should return nil for empty input",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single secret",
|
||||||
|
secrets: []swarm.Secret{
|
||||||
|
{Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "database_password"}}},
|
||||||
|
},
|
||||||
|
expected: []string{"database_password"},
|
||||||
|
description: "should return single secret name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple secrets",
|
||||||
|
secrets: []swarm.Secret{
|
||||||
|
{Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "db_password"}}},
|
||||||
|
{Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "api_key"}}},
|
||||||
|
{Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "ssl_cert"}}},
|
||||||
|
},
|
||||||
|
expected: []string{"db_password", "api_key", "ssl_cert"},
|
||||||
|
description: "should return all secret names in order",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "secrets with empty names",
|
||||||
|
secrets: []swarm.Secret{
|
||||||
|
{Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: ""}}},
|
||||||
|
{Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "valid_name"}}},
|
||||||
|
},
|
||||||
|
expected: []string{"", "valid_name"},
|
||||||
|
description: "should include empty names if present",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := GetSecretNames(tt.secrets)
|
||||||
|
assert.Equal(t, tt.expected, result, tt.description)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
239
pkg/deploy/utils.go
Normal file
239
pkg/deploy/utils.go
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
package deploy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
appPkg "coopcloud.tech/abra/pkg/app"
|
||||||
|
"coopcloud.tech/abra/pkg/client"
|
||||||
|
"coopcloud.tech/abra/pkg/envfile"
|
||||||
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
|
"coopcloud.tech/abra/pkg/i18n"
|
||||||
|
"coopcloud.tech/abra/pkg/log"
|
||||||
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
|
"coopcloud.tech/abra/pkg/secret"
|
||||||
|
|
||||||
|
"github.com/distribution/reference"
|
||||||
|
composetypes "github.com/docker/cli/cli/compose/types"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
dockerClient "github.com/docker/docker/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MergeAbraShEnv merges abra.sh env vars into the app env vars.
|
||||||
|
func MergeAbraShEnv(recipe recipe.Recipe, env envfile.AppEnv) error {
|
||||||
|
abraShEnv, err := envfile.ReadAbraShEnvVars(recipe.AbraShPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range abraShEnv {
|
||||||
|
log.Debugf("read v:%s k: %s", v, k)
|
||||||
|
env[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfigsForStack retrieves all Docker configs attached to services in a given stack.
|
||||||
|
func GetConfigsForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) {
|
||||||
|
filters, err := app.Filters(false, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all services in the stack
|
||||||
|
services, err := cl.ServiceList(context.Background(), swarm.ServiceListOptions{
|
||||||
|
Filters: filters,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect unique config names with versions
|
||||||
|
configs := make(map[string]string)
|
||||||
|
for _, service := range services {
|
||||||
|
if service.Spec.TaskTemplate.ContainerSpec != nil {
|
||||||
|
for _, configRef := range service.Spec.TaskTemplate.ContainerSpec.Configs {
|
||||||
|
configName := configRef.ConfigName
|
||||||
|
if configName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
configBaseName, configVersion, err := client.GetConfigNameAndVersion(configName, app.StackName())
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
existingConfigVersion, ok := configs[configBaseName]
|
||||||
|
if !ok {
|
||||||
|
// First time seeing this, add to map
|
||||||
|
configs[configBaseName] = configVersion
|
||||||
|
} else {
|
||||||
|
// Just make sure the versions are the same..
|
||||||
|
if existingConfigVersion != configVersion {
|
||||||
3wordchant marked this conversation as resolved
decentral1se
commented
`log.Warn(i18n.G("..."))`
|
|||||||
|
log.Warnf(i18n.G("different versions for config '%s', '%s' and %s'", configBaseName, existingConfigVersion, configVersion))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return configs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImagesForStack retrieves all Docker images for services in a given stack.
|
||||||
|
func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]string, error) {
|
||||||
|
filters, err := app.Filters(false, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all services in the stack
|
||||||
|
services, err := cl.ServiceList(context.Background(), swarm.ServiceListOptions{
|
||||||
|
Filters: filters,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect unique image names with versions
|
||||||
|
images := make(map[string]string)
|
||||||
|
for _, service := range services {
|
||||||
|
if service.Spec.TaskTemplate.ContainerSpec != nil {
|
||||||
|
imageName := service.Spec.TaskTemplate.ContainerSpec.Image
|
||||||
|
|
||||||
|
imageParsed, err := reference.ParseNormalizedNamed(imageName)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
imageBaseName := reference.Path(imageParsed)
|
||||||
|
imageTag := imageParsed.(reference.NamedTagged).Tag()
|
||||||
|
|
||||||
|
existingImageVersion, ok := images[imageBaseName]
|
||||||
|
if !ok {
|
||||||
|
// First time seeing this, add to map
|
||||||
|
images[imageBaseName] = imageTag
|
||||||
|
} else {
|
||||||
|
// Just make sure the versions are the same..
|
||||||
|
if existingImageVersion != imageTag {
|
||||||
3wordchant marked this conversation as resolved
decentral1se
commented
```log.Warn(i18n.G("..."))```
|
|||||||
|
log.Warnf(i18n.G("different versions for image '%s', '%s' and %s'", imageBaseName, existingImageVersion, imageTag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GatherSecretsForDeploy(cl *dockerClient.Client, app appPkg.App) ([]string, error) {
|
||||||
|
secStats, err := secret.PollSecretsStatus(cl, app)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var secretInfo []string
|
||||||
|
|
||||||
|
// Sort secrets to ensure reproducible output
|
||||||
|
sort.Slice(secStats, func(i, j int) bool {
|
||||||
|
return secStats[i].LocalName < secStats[j].LocalName
|
||||||
|
})
|
||||||
|
for _, secStat := range secStats {
|
||||||
|
secretInfo = append(secretInfo, fmt.Sprintf("%s: %s", secStat.LocalName, secStat.Version))
|
||||||
|
}
|
||||||
|
return secretInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GatherConfigsForDeploy(cl *dockerClient.Client, app appPkg.App, compose *composetypes.Config, abraShEnv map[string]string, showUnchanged bool) ([]string, error) {
|
||||||
|
// Get current configs from existing deployment
|
||||||
|
currentConfigs, err := GetConfigsForStack(cl, app)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf(i18n.G("deployed config names: %v", currentConfigs))
|
||||||
|
|
||||||
|
// Get new configs from the compose specification
|
||||||
|
newConfigs := compose.Configs
|
||||||
|
|
||||||
|
var configInfo []string
|
||||||
|
for configName := range newConfigs {
|
||||||
|
log.Debugf(i18n.G("searching abra.sh for version for %s", configName))
|
||||||
|
versionKey := strings.ToUpper(configName) + "_VERSION"
|
||||||
|
newVersion, exists := abraShEnv[versionKey]
|
||||||
|
if !exists {
|
||||||
|
log.Warnf(i18n.G("no version found for config %s", configName))
|
||||||
|
configInfo = append(configInfo, i18n.G("%s: ? (missing version)", configName))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentVersion, exists := currentConfigs[configName]; exists {
|
||||||
|
if currentVersion == newVersion {
|
||||||
|
if showUnchanged {
|
||||||
|
configInfo = append(configInfo, i18n.G("%s: %s (unchanged)", configName, newVersion))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configInfo = append(configInfo, i18n.G("%s: %s → %s", configName, currentVersion, newVersion))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configInfo = append(configInfo, i18n.G("%s: %s (new)", configName, newVersion))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return configInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *composetypes.Config, showUnchanged bool) ([]string, error) {
|
||||||
|
// Get current images from existing deployment
|
||||||
3wordchant marked this conversation as resolved
decentral1se
commented
Rogue newline? Rogue newline?
|
|||||||
|
currentImages, err := GetImagesForStack(cl, app)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf(i18n.G("deployed images: %v", currentImages))
|
||||||
|
|
||||||
|
// Proposed new images from the compose files
|
||||||
|
newImages := make(map[string]string)
|
||||||
|
|
||||||
|
for _, service := range compose.Services {
|
||||||
|
imageParsed, err := reference.ParseNormalizedNamed(service.Image)
|
||||||
|
imageBaseName := reference.Path(imageParsed)
|
||||||
|
imageTag := imageParsed.(reference.NamedTagged).Tag()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existingImageVersion, ok := newImages[imageBaseName]
|
||||||
|
if !ok {
|
||||||
|
// First time seeing this, add to map
|
||||||
|
newImages[imageBaseName] = imageTag
|
||||||
|
} else {
|
||||||
|
// Just make sure the versions are the same..
|
||||||
|
if existingImageVersion != imageTag {
|
||||||
|
log.Warnf(i18n.G("different versions for image '%s', '%s' and %s'", imageBaseName, existingImageVersion, imageTag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf(i18n.G("proposed images: %v", newImages))
|
||||||
|
|
||||||
|
var imageInfo []string
|
||||||
|
for newImageName, newImageVersion := range newImages {
|
||||||
|
if currentVersion, exists := currentImages[newImageName]; exists {
|
||||||
|
if currentVersion == newImageVersion {
|
||||||
|
if showUnchanged {
|
||||||
|
imageInfo = append(imageInfo, i18n.G("%s: %s (unchanged)", formatter.StripTagMeta(newImageName), newImageVersion))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageInfo = append(imageInfo, i18n.G("%s: %s → %s", formatter.StripTagMeta(newImageName), currentVersion, newImageVersion))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageInfo = append(imageInfo, i18n.G("%s: %s (new)", formatter.StripTagMeta(newImageName), newImageVersion))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageInfo, nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -280,7 +280,7 @@ type secretStatus struct {
|
|||||||
type secretStatuses []secretStatus
|
type secretStatuses []secretStatus
|
||||||
|
|
||||||
// PollSecretsStatus checks status of secrets by comparing the local recipe
|
// PollSecretsStatus checks status of secrets by comparing the local recipe
|
||||||
// config and deploymend server state.
|
// config and deployed server state.
|
||||||
func PollSecretsStatus(cl *dockerClient.Client, app appPkg.App) (secretStatuses, error) {
|
func PollSecretsStatus(cl *dockerClient.Client, app appPkg.App) (secretStatuses, error) {
|
||||||
var secStats secretStatuses
|
var secStats secretStatuses
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ func PollSecretsStatus(cl *dockerClient.Client, app appPkg.App) (secretStatuses,
|
|||||||
|
|
||||||
remoteSecretNames := make(map[string]bool)
|
remoteSecretNames := make(map[string]bool)
|
||||||
for _, cont := range secretList {
|
for _, cont := range secretList {
|
||||||
remoteSecretNames[cont.Spec.Annotations.Name] = true
|
remoteSecretNames[cont.Spec.Name] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for secretName, val := range secretsConfig {
|
for secretName, val := range secretsConfig {
|
||||||
|
@ -41,6 +41,8 @@ teardown(){
|
|||||||
assert_output --partial 'CURRENT DEPLOYMENT N/A'
|
assert_output --partial 'CURRENT DEPLOYMENT N/A'
|
||||||
assert_output --partial 'ENV VERSION N/A'
|
assert_output --partial 'ENV VERSION N/A'
|
||||||
assert_output --partial "NEW DEPLOYMENT ${latestRelease}"
|
assert_output --partial "NEW DEPLOYMENT ${latestRelease}"
|
||||||
|
assert_output --partial "IMAGES nginx: ${latestRelease##*+} (new)"
|
||||||
|
assert_output --partial "CONFIGS test_conf: v1 (new)"
|
||||||
|
|
||||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
@ -58,12 +60,35 @@ teardown(){
|
|||||||
assert_output --partial "CURRENT DEPLOYMENT N/A"
|
assert_output --partial "CURRENT DEPLOYMENT N/A"
|
||||||
assert_output --partial "ENV VERSION ${latestRelease}"
|
assert_output --partial "ENV VERSION ${latestRelease}"
|
||||||
assert_output --partial "NEW DEPLOYMENT ${latestRelease}"
|
assert_output --partial "NEW DEPLOYMENT ${latestRelease}"
|
||||||
|
assert_output --partial "IMAGES nginx: ${latestRelease##*+} (new)"
|
||||||
|
assert_output --partial "CONFIGS test_conf: v1 (new)"
|
||||||
|
|
||||||
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
run grep -q "TYPE=$TEST_RECIPE:${latestRelease}" \
|
||||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
assert_success
|
assert_success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "show changed config version on re-deploy" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
|
--no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run sed -i 's/TEST_CONF_VERSION=v1/TEST_CONF_VERSION=v2/' \
|
||||||
|
"$ABRA_DIR/recipes/$TEST_RECIPE/abra.sh" \
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
cat "$ABRA_DIR/recipes/$TEST_RECIPE/abra.sh"
|
||||||
|
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
|
--no-input --no-converge-checks --force --chaos
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
assert_output --partial "CONFIGS test_conf: v1 → v2"
|
||||||
|
|
||||||
|
_checkout_recipe
|
||||||
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@test "deploy, re-deploy, choose env version" {
|
@test "deploy, re-deploy, choose env version" {
|
||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" \
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" \
|
||||||
|
Reference in New Issue
Block a user
Can drop the
else
and justreturn ...
.