diff --git a/cli/app/deploy.go b/cli/app/deploy.go index 259fba73..24c175fe 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -6,6 +6,7 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" + "coopcloud.tech/abra/pkg/secret" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" @@ -91,6 +92,17 @@ recipes. logrus.Fatal(err) } + secStats, err := secret.PollSecretsStatus(app) + if err != nil { + logrus.Fatal(err) + } + + for _, secStat := range secStats { + if !secStat.CreatedOnRemote { + logrus.Fatalf("unable to deploy, secrets not generated (%s)?", secStat.LocalName) + } + } + if isDeployed { if internal.Force || internal.Chaos { logrus.Warnf("%s is already deployed but continuing (--force/--chaos)", app.Name) diff --git a/cli/app/secret.go b/cli/app/secret.go index 31b86c23..1fd0678e 100644 --- a/cli/app/secret.go +++ b/cli/app/secret.go @@ -383,50 +383,21 @@ var appSecretLsCommand = cli.Command{ } } - composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env) - if err != nil { - logrus.Fatal(err) - } - - secretsConfig, err := secret.ReadSecretsConfig(app.Env, composeFiles, app.Recipe) - if err != nil { - logrus.Fatal(err) - } - tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"} table := formatter.CreateTable(tableCol) - cl, err := client.New(app.Server) + secStats, err := secret.PollSecretsStatus(app) if err != nil { logrus.Fatal(err) } - filters, err := app.Filters(false, false) - if err != nil { - logrus.Fatal(err) - } - - secretList, err := cl.SecretList(context.Background(), types.SecretListOptions{Filters: filters}) - if err != nil { - logrus.Fatal(err) - } - - remoteSecretNames := make(map[string]bool) - for _, cont := range secretList { - remoteSecretNames[cont.Spec.Annotations.Name] = true - } - - for secretName, secretValue := range secretsConfig { - createdRemote := false - val, err := secret.ParseSecretValue(secretValue) - if err != nil { - logrus.Fatal(err) + for _, secStat := range secStats { + tableRow := []string{ + secStat.LocalName, + secStat.Version, + secStat.RemoteName, + strconv.FormatBool(secStat.CreatedOnRemote), } - secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, val.Version) - if _, ok := remoteSecretNames[secretRemoteName]; ok { - createdRemote = true - } - tableRow := []string{secretName, val.Version, secretRemoteName, strconv.FormatBool(createdRemote)} table.Append(tableRow) } diff --git a/pkg/secret/secret.go b/pkg/secret/secret.go index aea8391a..4ded6b1f 100644 --- a/pkg/secret/secret.go +++ b/pkg/secret/secret.go @@ -4,6 +4,7 @@ package secret import ( + "context" "fmt" "slices" "strconv" @@ -11,9 +12,11 @@ import ( "sync" "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/upstream/stack" loader "coopcloud.tech/abra/pkg/upstream/stack" "github.com/decentral1se/passgen" + "github.com/docker/docker/api/types" dockerClient "github.com/docker/docker/client" "github.com/sirupsen/logrus" ) @@ -209,3 +212,71 @@ func GenerateSecrets(cl *dockerClient.Client, secretsFromConfig map[string]strin return secrets, nil } + +type secretStatus struct { + LocalName string + RemoteName string + Version string + CreatedOnRemote bool +} + +type secretStatuses []secretStatus + +// PollSecretsStatus checks status of secrest by comparing the local recipe +// config and deploymend server state. +func PollSecretsStatus(app config.App) (secretStatuses, error) { + var secStats secretStatuses + + composeFiles, err := config.GetComposeFiles(app.Recipe, app.Env) + if err != nil { + return secStats, err + } + + secretsConfig, err := ReadSecretsConfig(app.Env, composeFiles, app.Recipe) + if err != nil { + return secStats, err + } + + cl, err := client.New(app.Server) + if err != nil { + return secStats, err + } + + filters, err := app.Filters(false, false) + if err != nil { + return secStats, err + } + + secretList, err := cl.SecretList(context.Background(), types.SecretListOptions{Filters: filters}) + if err != nil { + return secStats, err + } + + remoteSecretNames := make(map[string]bool) + for _, cont := range secretList { + remoteSecretNames[cont.Spec.Annotations.Name] = true + } + + for secretName, secretValue := range secretsConfig { + createdRemote := false + + val, err := ParseSecretValue(secretValue) + if err != nil { + return secStats, err + } + + secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, val.Version) + if _, ok := remoteSecretNames[secretRemoteName]; ok { + createdRemote = true + } + + secStats = append(secStats, secretStatus{ + LocalName: secretName, + RemoteName: secretRemoteName, + Version: val.Version, + CreatedOnRemote: createdRemote, + }) + } + + return secStats, nil +} diff --git a/tests/integration/app_deploy.bats b/tests/integration/app_deploy.bats index 071c2728..a3a5ebdd 100644 --- a/tests/integration/app_deploy.bats +++ b/tests/integration/app_deploy.bats @@ -328,3 +328,19 @@ teardown(){ _undeploy_app _reset_app } + +@test "error if no secrets generated" { + run sed -i 's/COMPOSE_FILE="compose.yml"/COMPOSE_FILE="compose.yml:compose.extra_secret.yml"/g' \ + "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" + assert_success + + run sed -i 's/#SECRET_EXTRA_PASS_VERSION=v1/SECRET_EXTRA_PASS_VERSION=v1/g' \ + "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" + assert_success + + run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks + assert_failure + assert_output --partial 'unable to deploy, secrets not generated' + + _reset_app +}