forked from toolshed/abra
		
	| @ -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,6 +18,7 @@ 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" | ||||
| ) | ||||
|  | ||||
| @ -27,9 +28,9 @@ var AppDeployCommand = &cobra.Command{ | ||||
| 	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 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 as values for | ||||
| "[version]". Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
| 	Example: `  # standard deployment | ||||
|   abra app deploy 1312.net | ||||
|  | ||||
| @ -61,29 +62,20 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
| 		} | ||||
| 	}, | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		var warnMessages []string | ||||
| 		var ( | ||||
| 			deployWarnMessages   []string | ||||
| 			toDeployVersion      string | ||||
| 			isChaosCommit        bool | ||||
| 			toDeployChaosVersion = config.CHAOS_DEFAULT | ||||
| 		) | ||||
|  | ||||
| 		app := internal.ValidateApp(args) | ||||
| 		stackName := app.StackName() | ||||
|  | ||||
| 		ok, err := validateChaosXORVersion(args) | ||||
| 		if !ok { | ||||
| 			log.Fatalf(err.Error()) | ||||
| 		if err := validateArgsAndFlags(args); err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		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 { | ||||
| 		if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| @ -91,96 +83,66 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
| 			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) | ||||
| 		log.Debugf("checking whether %s is already deployed", app.StackName()) | ||||
|  | ||||
| 		deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		// NOTE(d1): handles "[version] as git hash" use case | ||||
| 		var isChaosCommit bool | ||||
|  | ||||
| 		// 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 | ||||
| 		toDeployVersion := deployMeta.Version | ||||
| 		if specificVersion != "" { | ||||
| 			toDeployVersion = specificVersion | ||||
| 			log.Debugf("choosing %s as version to deploy", toDeployVersion) | ||||
|  | ||||
| 			var err error | ||||
| 			isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion) | ||||
| 			if err != nil { | ||||
| 				log.Fatal(err) | ||||
| 			} | ||||
|  | ||||
| 			if isChaosCommit { | ||||
| 				log.Debugf("assuming '%s' is a chaos commit", toDeployVersion) | ||||
| 				internal.Chaos = true | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		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 && !(internal.Force || internal.Chaos) { | ||||
| 			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(args) == 2 && args[1] != "" { | ||||
| 			toDeployVersion = 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 { | ||||
| 					log.Fatal(err) | ||||
| 				} | ||||
| 			} else { | ||||
| 				head, err := app.Recipe.Head() | ||||
| 				if err != nil { | ||||
| 					log.Fatal(err) | ||||
| 				} | ||||
| 				toDeployVersion = formatter.SmallSHA(head.String()) | ||||
| 		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) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		toDeployChaosVersion := config.CHAOS_DEFAULT | ||||
| 		if internal.Chaos { | ||||
| 			if isChaosCommit { | ||||
| 				toDeployChaosVersion = specificVersion | ||||
| 				versionLabelLocal, err := app.Recipe.GetVersionLabelLocal() | ||||
| 				if err != nil { | ||||
| 					log.Fatal(err) | ||||
| 				} | ||||
| 				toDeployVersion = versionLabelLocal | ||||
| 			} else { | ||||
| 				var err error | ||||
| 				toDeployChaosVersion, err = app.Recipe.ChaosVersion() | ||||
| 				if err != nil { | ||||
| 					log.Fatal(err) | ||||
| 				} | ||||
| 			if err := getChaosVersion(app, &toDeployVersion, &toDeployChaosVersion); err != nil { | ||||
| 				log.Fatal(err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		if isChaosCommit { | ||||
| 			log.Debugf("assuming chaos commit: %s", toDeployVersion) | ||||
|  | ||||
| 			internal.Chaos = true | ||||
| 			toDeployChaosVersion = toDeployVersion | ||||
|  | ||||
| 			toDeployVersion, err = app.Recipe.GetVersionLabelLocal() | ||||
| 			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) | ||||
| @ -194,6 +156,7 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		stackName := app.StackName() | ||||
| 		deployOpts := stack.Deploy{ | ||||
| 			Composefiles: composeFiles, | ||||
| 			Namespace:    stackName, | ||||
| @ -224,23 +187,22 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
|  | ||||
| 		for _, envVar := range envVars { | ||||
| 			if !envVar.Present { | ||||
| 				warnMessages = append(warnMessages, | ||||
| 					fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain), | ||||
| 				deployWarnMessages = append(deployWarnMessages, | ||||
| 					fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain), | ||||
| 				) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if !internal.NoDomainChecks { | ||||
| 			domainName, ok := app.Env["DOMAIN"] | ||||
| 			if ok { | ||||
| 			if domainName, ok := app.Env["DOMAIN"]; ok { | ||||
| 				if _, err = dns.EnsureDomainsResolveSameIPv4(domainName, app.Server); err != nil { | ||||
| 					log.Fatal(err) | ||||
| 				} | ||||
| 			} else { | ||||
| 				log.Debug("skipping domain checks as no DOMAIN=... configured for app") | ||||
| 				log.Debug("skipping domain checks, no DOMAIN=... configured") | ||||
| 			} | ||||
| 		} else { | ||||
| 			log.Debug("skipping domain checks as requested") | ||||
| 			log.Debug("skipping domain checks") | ||||
| 		} | ||||
|  | ||||
| 		deployedVersion := config.NO_VERSION_DEFAULT | ||||
| @ -248,13 +210,20 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
| 			deployedVersion = deployMeta.Version | ||||
| 		} | ||||
|  | ||||
| 		toWriteVersion := toDeployVersion | ||||
| 		if internal.Chaos || isChaosCommit { | ||||
| 			toWriteVersion = toDeployChaosVersion | ||||
| 		} | ||||
|  | ||||
| 		if err := internal.DeployOverview( | ||||
| 			app, | ||||
| 			warnMessages, | ||||
| 			deployWarnMessages, | ||||
| 			deployedVersion, | ||||
| 			deployMeta.ChaosVersion, | ||||
| 			toDeployVersion, | ||||
| 			toDeployChaosVersion); err != nil { | ||||
| 			toDeployChaosVersion, | ||||
| 			toWriteVersion, | ||||
| 		); err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| @ -262,7 +231,8 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 		log.Debugf("set waiting timeout to %d s", stack.WaitTimeout) | ||||
|  | ||||
| 		log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout) | ||||
|  | ||||
| 		if err := stack.RunDeploy(cl, deployOpts, compose, app.Name, internal.DontWaitConverge); err != nil { | ||||
| 			log.Fatal(err) | ||||
| @ -276,30 +246,77 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`, | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		app.Recipe.Version = toDeployVersion | ||||
| 		if toDeployChaosVersion != config.CHAOS_DEFAULT { | ||||
| 			app.Recipe.Version = toDeployChaosVersion | ||||
| 		} | ||||
| 		if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil { | ||||
| 			log.Fatalf("writing new recipe version in env file: %s", err) | ||||
| 		if err := app.WriteRecipeVersion(toWriteVersion, false); err != nil { | ||||
| 			log.Fatalf("writing recipe version failed: %s", 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") | ||||
| func getChaosVersion(app app.App, toDeployVersion, toDeployChaosVersion *string) error { | ||||
| 	var err error | ||||
| 	*toDeployChaosVersion, err = app.Recipe.ChaosVersion() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return true, nil | ||||
|  | ||||
| 	*toDeployVersion, err = app.Recipe.GetVersionLabelLocal() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // getSpecifiedVersion retrieves the specific version if available | ||||
| func getSpecifiedVersion(args []string) string { | ||||
| 	if len(args) >= 2 { | ||||
| 		return args[1] | ||||
| func getLatestVersionOrCommit(app app.App, toDeployVersion *string) error { | ||||
| 	versions, err := app.Recipe.Tags() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return "" | ||||
|  | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| @ -332,6 +349,6 @@ func init() { | ||||
| 		"no-converge-checks", | ||||
| 		"c", | ||||
| 		false, | ||||
| 		"do not wait for converge logic checks", | ||||
| 		"disable converge logic checks", | ||||
| 	) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user