fix!: chaos consistency (deploy/undeploy/rollback/upgrade)
See coop-cloud/organising#559 --chaos for rollback/upgrade goes away.
This commit is contained in:
		| @ -71,7 +71,7 @@ recipes. | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
| @ -80,7 +80,7 @@ recipes. | |||||||
| 		// is because we need to deal with GetComposeFiles under the hood and these | 		// is because we need to deal with GetComposeFiles under the hood and these | ||||||
| 		// files change from version to version which therefore affects which | 		// files change from version to version which therefore affects which | ||||||
| 		// secrets might be generated | 		// secrets might be generated | ||||||
| 		version := deployedVersion | 		version := deployMeta.Version | ||||||
| 		if specificVersion != "" { | 		if specificVersion != "" { | ||||||
| 			version = specificVersion | 			version = specificVersion | ||||||
| 			log.Debugf("choosing %s as version to deploy", version) | 			log.Debugf("choosing %s as version to deploy", version) | ||||||
| @ -100,7 +100,7 @@ recipes. | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if isDeployed { | 		if deployMeta.IsDeployed { | ||||||
| 			if internal.Force || internal.Chaos { | 			if internal.Force || internal.Chaos { | ||||||
| 				log.Warnf("%s is already deployed but continuing (--force/--chaos)", app.Name) | 				log.Warnf("%s is already deployed but continuing (--force/--chaos)", app.Name) | ||||||
| 			} else { | 			} else { | ||||||
| @ -147,10 +147,11 @@ recipes. | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		chaosVersion := "false" | ||||||
| 		if internal.Chaos { | 		if internal.Chaos { | ||||||
| 			log.Warnf("chaos mode engaged") | 			log.Warnf("chaos mode engaged") | ||||||
| 			var err error | 			var err error | ||||||
| 			version, err = app.Recipe.ChaosVersion() | 			chaosVersion, err = app.Recipe.ChaosVersion() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Fatal(err) | 				log.Fatal(err) | ||||||
| 			} | 			} | ||||||
| @ -184,7 +185,7 @@ recipes. | |||||||
| 		appPkg.ExposeAllEnv(stackName, compose, app.Env) | 		appPkg.ExposeAllEnv(stackName, compose, app.Env) | ||||||
| 		appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name) | 		appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name) | ||||||
| 		appPkg.SetChaosLabel(compose, stackName, internal.Chaos) | 		appPkg.SetChaosLabel(compose, stackName, internal.Chaos) | ||||||
| 		appPkg.SetChaosVersionLabel(compose, stackName, version) | 		appPkg.SetChaosVersionLabel(compose, stackName, chaosVersion) | ||||||
| 		appPkg.SetUpdateLabel(compose, stackName, app.Env) | 		appPkg.SetUpdateLabel(compose, stackName, app.Env) | ||||||
|  |  | ||||||
| 		envVars, err := appPkg.CheckEnv(app) | 		envVars, err := appPkg.CheckEnv(app) | ||||||
| @ -198,7 +199,7 @@ recipes. | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err := internal.DeployOverview(app, version, "continue with deployment?"); err != nil { | 		if err := internal.DeployOverview(app, version, chaosVersion, "continue with deployment?"); err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -47,12 +47,12 @@ var appLogsCommand = cli.Command{ | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, _, err := stack.IsDeployed(context.Background(), cl, stackName) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !isDeployed { | 		if !deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is not deployed?", app.Name) | 			log.Fatalf("%s is not deployed?", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -41,12 +41,12 @@ var appPsCommand = cli.Command{ | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !isDeployed { | 		if !deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is not deployed?", app.Name) | 			log.Fatalf("%s is not deployed?", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @ -55,7 +55,7 @@ var appPsCommand = cli.Command{ | |||||||
| 		if statusMeta, ok := statuses[app.StackName()]; ok { | 		if statusMeta, ok := statuses[app.StackName()]; ok { | ||||||
| 			isChaos, exists := statusMeta["chaos"] | 			isChaos, exists := statusMeta["chaos"] | ||||||
| 			if exists && isChaos == "false" { | 			if exists && isChaos == "false" { | ||||||
| 				if err := app.Recipe.EnsureVersion(deployedVersion); err != nil { | 				if err := app.Recipe.EnsureVersion(deployMeta.Version); err != nil { | ||||||
| 					log.Fatal(err) | 					log.Fatal(err) | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| @ -66,7 +66,7 @@ var appPsCommand = cli.Command{ | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		showPSOutput(app, cl, deployedVersion, chaosVersion) | 		showPSOutput(app, cl, deployMeta.Version, chaosVersion) | ||||||
|  |  | ||||||
| 		return nil | 		return nil | ||||||
| 	}, | 	}, | ||||||
|  | |||||||
| @ -66,11 +66,11 @@ flag. | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
| 		if isDeployed { | 		if deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name) | 			log.Fatalf("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -67,12 +67,12 @@ Example: | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !isDeployed { | 		if !deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is not deployed?", app.Name) | 			log.Fatalf("%s is not deployed?", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -28,7 +28,6 @@ var appRollbackCommand = cli.Command{ | |||||||
| 		internal.DebugFlag, | 		internal.DebugFlag, | ||||||
| 		internal.NoInputFlag, | 		internal.NoInputFlag, | ||||||
| 		internal.ForceFlag, | 		internal.ForceFlag, | ||||||
| 		internal.ChaosFlag, |  | ||||||
| 		internal.NoDomainChecksFlag, | 		internal.NoDomainChecksFlag, | ||||||
| 		internal.DontWaitConvergeFlag, | 		internal.DontWaitConvergeFlag, | ||||||
| 		internal.OfflineFlag, | 		internal.OfflineFlag, | ||||||
| @ -42,21 +41,12 @@ useful if the container runtime has gotten into a weird state. | |||||||
|  |  | ||||||
| This action could be destructive, please ensure you have a copy of your app | This action could be destructive, please ensure you have a copy of your app | ||||||
| data beforehand. | data beforehand. | ||||||
|  |  | ||||||
| Chaos mode ("--chaos") will deploy your local checkout of a recipe as-is, |  | ||||||
| including unstaged changes and can be useful for live hacking and testing new |  | ||||||
| recipes. |  | ||||||
| `, | `, | ||||||
| 	BashComplete: autocomplete.AppNameComplete, | 	BashComplete: autocomplete.AppNameComplete, | ||||||
| 	Action: func(c *cli.Context) error { | 	Action: func(c *cli.Context) error { | ||||||
| 		app := internal.ValidateApp(c) | 		app := internal.ValidateApp(c) | ||||||
| 		stackName := app.StackName() | 		stackName := app.StackName() | ||||||
|  |  | ||||||
| 		specificVersion := c.Args().Get(1) |  | ||||||
| 		if specificVersion != "" && internal.Chaos { |  | ||||||
| 			log.Fatal("cannot use <version> and --chaos together") |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { | 		if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
| @ -72,12 +62,12 @@ recipes. | |||||||
|  |  | ||||||
| 		log.Debugf("checking whether %s is already deployed", stackName) | 		log.Debugf("checking whether %s is already deployed", stackName) | ||||||
|  |  | ||||||
| 		isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !isDeployed { | 		if !deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is not deployed?", app.Name) | 			log.Fatalf("%s is not deployed?", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @ -91,12 +81,13 @@ recipes. | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if len(versions) == 0 && !internal.Chaos { | 		if len(versions) == 0 { | ||||||
| 			log.Warn("no published versions in catalogue, trying local recipe repository") | 			log.Warn("no published versions in catalogue, trying local recipe repository") | ||||||
| 			recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline) | 			recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Warn(err) | 				log.Warn(err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			for _, recipeVersion := range recipeVersions { | 			for _, recipeVersion := range recipeVersions { | ||||||
| 				for version := range recipeVersion { | 				for version := range recipeVersion { | ||||||
| 					versions = append(versions, version) | 					versions = append(versions, version) | ||||||
| @ -105,76 +96,82 @@ recipes. | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		var availableDowngrades []string | 		var availableDowngrades []string | ||||||
| 		if deployedVersion == "unknown" { | 		if deployMeta.Version == "unknown" { | ||||||
| 			availableDowngrades = versions | 			availableDowngrades = versions | ||||||
| 			log.Warnf("failed to determine deployed version of %s", app.Name) | 			log.Warnf("failed to determine deployed version of %s", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		specificVersion := c.Args().Get(1) | ||||||
| 		if specificVersion != "" { | 		if specificVersion != "" { | ||||||
| 			parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) | 			parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Fatal(err) | 				log.Fatal(err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			parsedSpecificVersion, err := tagcmp.Parse(specificVersion) | 			parsedSpecificVersion, err := tagcmp.Parse(specificVersion) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Fatal(err) | 				log.Fatal(err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) { | 			if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) { | ||||||
| 				log.Fatalf("%s is not a downgrade for %s?", deployedVersion, specificVersion) | 				log.Fatalf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			availableDowngrades = append(availableDowngrades, specificVersion) | 			availableDowngrades = append(availableDowngrades, specificVersion) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if deployedVersion != "unknown" && !internal.Chaos && specificVersion == "" { | 		if deployMeta.Version != "unknown" && specificVersion == "" { | ||||||
|  | 			if deployMeta.IsChaos == "true" { | ||||||
|  | 				log.Warn("attempting to rollback a chaos deployment") | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			for _, version := range versions { | 			for _, version := range versions { | ||||||
| 				parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) | 				parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Fatal(err) | 					log.Fatal(err) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				parsedVersion, err := tagcmp.Parse(version) | 				parsedVersion, err := tagcmp.Parse(version) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Fatal(err) | 					log.Fatal(err) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if parsedVersion.IsLessThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) { | 				if parsedVersion.IsLessThan(parsedDeployedVersion) && !(parsedVersion.Equals(parsedDeployedVersion)) { | ||||||
| 					availableDowngrades = append(availableDowngrades, version) | 					availableDowngrades = append(availableDowngrades, version) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if len(availableDowngrades) == 0 && !internal.Force { | 			if len(availableDowngrades) == 0 && !internal.Force { | ||||||
| 				log.Info("no available downgrades, you're on oldest ✌️") | 				log.Info("no available downgrades") | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		var chosenDowngrade string | 		var chosenDowngrade string | ||||||
| 		if len(availableDowngrades) > 0 && !internal.Chaos { | 		if len(availableDowngrades) > 0 { | ||||||
| 			if internal.Force || internal.NoInput || specificVersion != "" { | 			if internal.Force || internal.NoInput || specificVersion != "" { | ||||||
| 				chosenDowngrade = availableDowngrades[len(availableDowngrades)-1] | 				chosenDowngrade = availableDowngrades[len(availableDowngrades)-1] | ||||||
| 				log.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade) | 				log.Debugf("choosing %s as version to downgrade to (--force/--no-input)", chosenDowngrade) | ||||||
| 			} else { | 			} else { | ||||||
|  | 				msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version) | ||||||
|  | 				if deployMeta.IsChaos == "true" { | ||||||
|  | 					msg = fmt.Sprintf("please select a downgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion) | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 				prompt := &survey.Select{ | 				prompt := &survey.Select{ | ||||||
| 					Message: fmt.Sprintf("Please select a downgrade (current version: %s):", deployedVersion), | 					Message: msg, | ||||||
| 					Options: internal.ReverseStringList(availableDowngrades), | 					Options: internal.ReverseStringList(availableDowngrades), | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if err := survey.AskOne(prompt, &chosenDowngrade); err != nil { | 				if err := survey.AskOne(prompt, &chosenDowngrade); err != nil { | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !internal.Chaos { | 		log.Debugf("choosing %s as version to rollback", chosenDowngrade) | ||||||
| 			if err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil { | 		if err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil { | ||||||
| 				log.Fatal(err) | 			log.Fatal(err) | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if internal.Chaos { |  | ||||||
| 			log.Warn("chaos mode engaged") |  | ||||||
| 			var err error |  | ||||||
| 			chosenDowngrade, err = app.Recipe.ChaosVersion() |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath) | 		abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath) | ||||||
| @ -189,6 +186,7 @@ recipes. | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		deployOpts := stack.Deploy{ | 		deployOpts := stack.Deploy{ | ||||||
| 			Composefiles: composeFiles, | 			Composefiles: composeFiles, | ||||||
| 			Namespace:    stackName, | 			Namespace:    stackName, | ||||||
| @ -196,18 +194,25 @@ recipes. | |||||||
| 			ResolveImage: stack.ResolveImageAlways, | 			ResolveImage: stack.ResolveImageAlways, | ||||||
| 			Detach:       false, | 			Detach:       false, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env) | 		compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		appPkg.ExposeAllEnv(stackName, compose, app.Env) | 		appPkg.ExposeAllEnv(stackName, compose, app.Env) | ||||||
| 		appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name) | 		appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name) | ||||||
| 		appPkg.SetChaosLabel(compose, stackName, internal.Chaos) | 		appPkg.SetChaosLabel(compose, stackName, internal.Chaos) | ||||||
| 		appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade) | 		appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade) | ||||||
| 		appPkg.SetUpdateLabel(compose, stackName, app.Env) | 		appPkg.SetUpdateLabel(compose, stackName, app.Env) | ||||||
|  |  | ||||||
|  | 		chaosVersion := deployMeta.IsChaos | ||||||
|  | 		if deployMeta.IsChaos == "true" { | ||||||
|  | 			chaosVersion = deployMeta.ChaosVersion | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// NOTE(d1): no release notes implemeneted for rolling back | 		// NOTE(d1): no release notes implemeneted for rolling back | ||||||
| 		if err := internal.NewVersionOverview(app, deployedVersion, chosenDowngrade, ""); err != nil { | 		if err := internal.NewVersionOverview(app, deployMeta.Version, chaosVersion, chosenDowngrade, ""); err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -34,12 +34,12 @@ var appServicesCommand = cli.Command{ | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !isDeployed { | 		if !deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is not deployed?", app.Name) | 			log.Fatalf("%s is not deployed?", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ package app | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"coopcloud.tech/abra/cli/internal" | 	"coopcloud.tech/abra/cli/internal" | ||||||
| 	appPkg "coopcloud.tech/abra/pkg/app" | 	appPkg "coopcloud.tech/abra/pkg/app" | ||||||
| @ -28,27 +27,10 @@ var pruneFlag = &cli.BoolFlag{ | |||||||
| // pruneApp runs the equivalent of a "docker system prune" but only filtering | // pruneApp runs the equivalent of a "docker system prune" but only filtering | ||||||
| // against resources connected with the app deployment. It is not a system wide | // against resources connected with the app deployment. It is not a system wide | ||||||
| // prune. Volumes are not pruned to avoid unwated data loss. | // prune. Volumes are not pruned to avoid unwated data loss. | ||||||
| func pruneApp(c *cli.Context, cl *dockerClient.Client, app appPkg.App) error { | func pruneApp(cl *dockerClient.Client, app appPkg.App) error { | ||||||
| 	stackName := app.StackName() | 	stackName := app.StackName() | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		log.Debugf("polling for %s stack, waiting to be undeployed...", stackName) |  | ||||||
|  |  | ||||||
| 		services, err := stack.GetStackServices(ctx, cl, stackName) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if len(services) == 0 { |  | ||||||
| 			log.Debugf("%s undeployed, moving on with pruning logic", stackName) |  | ||||||
| 			time.Sleep(time.Second) // give runtime more time to tear down related state |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		time.Sleep(time.Second) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	pruneFilters := filters.NewArgs() | 	pruneFilters := filters.NewArgs() | ||||||
| 	stackSearch := fmt.Sprintf("%s*", stackName) | 	stackSearch := fmt.Sprintf("%s*", stackName) | ||||||
| 	pruneFilters.Add("label", stackSearch) | 	pruneFilters.Add("label", stackSearch) | ||||||
| @ -109,16 +91,21 @@ Passing "-p/--prune" does not remove those volumes. | |||||||
|  |  | ||||||
| 		log.Debugf("checking whether %s is already deployed", stackName) | 		log.Debugf("checking whether %s is already deployed", stackName) | ||||||
|  |  | ||||||
| 		isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !isDeployed { | 		if !deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is not deployed?", app.Name) | 			log.Fatalf("%s is not deployed?", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err := internal.DeployOverview(app, deployedVersion, "continue with undeploy?"); err != nil { | 		chaosVersion := deployMeta.IsChaos | ||||||
|  | 		if deployMeta.IsChaos == "true" { | ||||||
|  | 			chaosVersion = deployMeta.ChaosVersion | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err := internal.DeployOverview(app, deployMeta.Version, chaosVersion, "continue with undeploy?"); err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @ -131,7 +118,7 @@ Passing "-p/--prune" does not remove those volumes. | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if prune { | 		if prune { | ||||||
| 			if err := pruneApp(c, cl, app); err != nil { | 			if err := pruneApp(cl, app); err != nil { | ||||||
| 				log.Fatal(err) | 				log.Fatal(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -27,7 +27,6 @@ var appUpgradeCommand = cli.Command{ | |||||||
| 		internal.DebugFlag, | 		internal.DebugFlag, | ||||||
| 		internal.NoInputFlag, | 		internal.NoInputFlag, | ||||||
| 		internal.ForceFlag, | 		internal.ForceFlag, | ||||||
| 		internal.ChaosFlag, |  | ||||||
| 		internal.NoDomainChecksFlag, | 		internal.NoDomainChecksFlag, | ||||||
| 		internal.DontWaitConvergeFlag, | 		internal.DontWaitConvergeFlag, | ||||||
| 		internal.OfflineFlag, | 		internal.OfflineFlag, | ||||||
| @ -35,11 +34,7 @@ var appUpgradeCommand = cli.Command{ | |||||||
| 	}, | 	}, | ||||||
| 	Before: internal.SubCommandBefore, | 	Before: internal.SubCommandBefore, | ||||||
| 	Description: ` | 	Description: ` | ||||||
| Upgrade an app. You can use it to choose and roll out a new upgrade to an | Upgrade an app. You can use it to choose and roll out a new upgrade to a | ||||||
| existing app. |  | ||||||
|  |  | ||||||
| This command specifically supports incrementing the version of running apps, as |  | ||||||
| opposed to "abra app deploy <domain>" which will not change the version of a |  | ||||||
| deployed app. | deployed app. | ||||||
|  |  | ||||||
| You may pass "--force/-f" to upgrade to the same version again. This can be | You may pass "--force/-f" to upgrade to the same version again. This can be | ||||||
| @ -47,21 +42,12 @@ useful if the container runtime has gotten into a weird state. | |||||||
|  |  | ||||||
| This action could be destructive, please ensure you have a copy of your app | This action could be destructive, please ensure you have a copy of your app | ||||||
| data beforehand. | data beforehand. | ||||||
|  |  | ||||||
| Chaos mode ("--chaos") will deploy your local checkout of a recipe as-is, |  | ||||||
| including unstaged changes and can be useful for live hacking and testing new |  | ||||||
| recipes. |  | ||||||
| `, | `, | ||||||
| 	BashComplete: autocomplete.AppNameComplete, | 	BashComplete: autocomplete.AppNameComplete, | ||||||
| 	Action: func(c *cli.Context) error { | 	Action: func(c *cli.Context) error { | ||||||
| 		app := internal.ValidateApp(c) | 		app := internal.ValidateApp(c) | ||||||
| 		stackName := app.StackName() | 		stackName := app.StackName() | ||||||
|  |  | ||||||
| 		specificVersion := c.Args().Get(1) |  | ||||||
| 		if specificVersion != "" && internal.Chaos { |  | ||||||
| 			log.Fatal("cannot use <version> and --chaos together") |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { | 		if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
| @ -77,12 +63,12 @@ recipes. | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !isDeployed { | 		if !deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is not deployed?", app.Name) | 			log.Fatalf("%s is not deployed?", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @ -96,7 +82,7 @@ recipes. | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if len(versions) == 0 && !internal.Chaos { | 		if len(versions) == 0 { | ||||||
| 			log.Warn("no published versions in catalogue, trying local recipe repository") | 			log.Warn("no published versions in catalogue, trying local recipe repository") | ||||||
| 			recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline) | 			recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| @ -110,13 +96,14 @@ recipes. | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		var availableUpgrades []string | 		var availableUpgrades []string | ||||||
| 		if deployedVersion == "unknown" { | 		if deployMeta.Version == "unknown" { | ||||||
| 			availableUpgrades = versions | 			availableUpgrades = versions | ||||||
| 			log.Warnf("failed to determine deployed version of %s", app.Name) | 			log.Warnf("failed to determine deployed version of %s", app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		specificVersion := c.Args().Get(1) | ||||||
| 		if specificVersion != "" { | 		if specificVersion != "" { | ||||||
| 			parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) | 			parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Fatal(err) | 				log.Fatal(err) | ||||||
| 			} | 			} | ||||||
| @ -125,17 +112,21 @@ recipes. | |||||||
| 				log.Fatal(err) | 				log.Fatal(err) | ||||||
| 			} | 			} | ||||||
| 			if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) { | 			if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) || parsedSpecificVersion.Equals(parsedDeployedVersion) { | ||||||
| 				log.Fatalf("%s is not an upgrade for %s?", deployedVersion, specificVersion) | 				log.Fatalf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion) | ||||||
| 			} | 			} | ||||||
| 			availableUpgrades = append(availableUpgrades, specificVersion) | 			availableUpgrades = append(availableUpgrades, specificVersion) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		parsedDeployedVersion, err := tagcmp.Parse(deployedVersion) | 		parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if deployedVersion != "unknown" && !internal.Chaos && specificVersion == "" { | 		if deployMeta.Version != "unknown" && specificVersion == "" { | ||||||
|  | 			if deployMeta.IsChaos == "true" { | ||||||
|  | 				log.Warn("attempting to upgrade a chaos deployment") | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			for _, version := range versions { | 			for _, version := range versions { | ||||||
| 				parsedVersion, err := tagcmp.Parse(version) | 				parsedVersion, err := tagcmp.Parse(version) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| @ -147,21 +138,27 @@ recipes. | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if len(availableUpgrades) == 0 && !internal.Force { | 			if len(availableUpgrades) == 0 && !internal.Force { | ||||||
| 				log.Infof("no available upgrades, you're on latest (%s) ✌️", deployedVersion) | 				log.Info("no available upgrades") | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		var chosenUpgrade string | 		var chosenUpgrade string | ||||||
| 		if len(availableUpgrades) > 0 && !internal.Chaos { | 		if len(availableUpgrades) > 0 { | ||||||
| 			if internal.Force || internal.NoInput || specificVersion != "" { | 			if internal.Force || internal.NoInput || specificVersion != "" { | ||||||
| 				chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] | 				chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] | ||||||
| 				log.Debugf("choosing %s as version to upgrade to", chosenUpgrade) | 				log.Debugf("choosing %s as version to upgrade to", chosenUpgrade) | ||||||
| 			} else { | 			} else { | ||||||
|  | 				msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version) | ||||||
|  | 				if deployMeta.IsChaos == "true" { | ||||||
|  | 					msg = fmt.Sprintf("please select an upgrade (version: %s, chaosVersion: %s):", deployMeta.Version, deployMeta.ChaosVersion) | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 				prompt := &survey.Select{ | 				prompt := &survey.Select{ | ||||||
| 					Message: fmt.Sprintf("Please select an upgrade (current version: %s):", deployedVersion), | 					Message: msg, | ||||||
| 					Options: internal.ReverseStringList(availableUpgrades), | 					Options: internal.ReverseStringList(availableUpgrades), | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if err := survey.AskOne(prompt, &chosenUpgrade); err != nil { | 				if err := survey.AskOne(prompt, &chosenUpgrade); err != nil { | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| @ -169,8 +166,8 @@ recipes. | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if internal.Force && chosenUpgrade == "" { | 		if internal.Force && chosenUpgrade == "" { | ||||||
| 			log.Warnf("%s is already upgraded to latest but continuing (--force/--chaos)", app.Name) | 			log.Warnf("%s is already upgraded to latest but continuing (--force)", app.Name) | ||||||
| 			chosenUpgrade = deployedVersion | 			chosenUpgrade = deployMeta.Version | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// if release notes written after git tag published, read them before we | 		// if release notes written after git tag published, read them before we | ||||||
| @ -199,19 +196,9 @@ recipes. | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !internal.Chaos { | 		log.Debugf("choosing %s as version to upgrade", chosenUpgrade) | ||||||
| 			if err := app.Recipe.EnsureVersion(chosenUpgrade); err != nil { | 		if err := app.Recipe.EnsureVersion(chosenUpgrade); err != nil { | ||||||
| 				log.Fatal(err) | 			log.Fatal(err) | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if internal.Chaos { |  | ||||||
| 			log.Warn("chaos mode engaged") |  | ||||||
| 			var err error |  | ||||||
| 			chosenUpgrade, err = app.Recipe.ChaosVersion() |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath) | 		abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath) | ||||||
| @ -226,6 +213,7 @@ recipes. | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		deployOpts := stack.Deploy{ | 		deployOpts := stack.Deploy{ | ||||||
| 			Composefiles: composeFiles, | 			Composefiles: composeFiles, | ||||||
| 			Namespace:    stackName, | 			Namespace:    stackName, | ||||||
| @ -233,10 +221,12 @@ recipes. | |||||||
| 			ResolveImage: stack.ResolveImageAlways, | 			ResolveImage: stack.ResolveImageAlways, | ||||||
| 			Detach:       false, | 			Detach:       false, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env) | 		compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		appPkg.ExposeAllEnv(stackName, compose, app.Env) | 		appPkg.ExposeAllEnv(stackName, compose, app.Env) | ||||||
| 		appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name) | 		appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name) | ||||||
| 		appPkg.SetChaosLabel(compose, stackName, internal.Chaos) | 		appPkg.SetChaosLabel(compose, stackName, internal.Chaos) | ||||||
| @ -260,7 +250,12 @@ recipes. | |||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err := internal.NewVersionOverview(app, deployedVersion, chosenUpgrade, releaseNotes); err != nil { | 		chaosVersion := deployMeta.IsChaos | ||||||
|  | 		if deployMeta.IsChaos == "true" { | ||||||
|  | 			chaosVersion = deployMeta.ChaosVersion | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err := internal.NewVersionOverview(app, deployMeta.Version, chaosVersion, chosenUpgrade, releaseNotes); err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -92,12 +92,12 @@ Passing "--force/-f" will select all volumes for removal. Be careful. | |||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | 		deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if isDeployed { | 		if deployMeta.IsDeployed { | ||||||
| 			log.Fatalf("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name) | 			log.Fatalf("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | |||||||
| @ -13,8 +13,8 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // NewVersionOverview shows an upgrade or downgrade overview | // NewVersionOverview shows an upgrade or downgrade overview | ||||||
| func NewVersionOverview(app appPkg.App, currentVersion, newVersion, releaseNotes string) error { | func NewVersionOverview(app appPkg.App, currentVersion, chaosVersion, newVersion, releaseNotes string) error { | ||||||
| 	tableCol := []string{"server", "recipe", "config", "domain", "current version", "to be deployed"} | 	tableCol := []string{"server", "recipe", "config", "domain", "version", "chaos", "to deploy"} | ||||||
| 	table := formatter.CreateTable(tableCol) | 	table := formatter.CreateTable(tableCol) | ||||||
|  |  | ||||||
| 	deployConfig := "compose.yml" | 	deployConfig := "compose.yml" | ||||||
| @ -27,7 +27,15 @@ func NewVersionOverview(app appPkg.App, currentVersion, newVersion, releaseNotes | |||||||
| 		server = "local" | 		server = "local" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	table.Append([]string{server, app.Recipe.Name, deployConfig, app.Domain, currentVersion, newVersion}) | 	table.Append([]string{ | ||||||
|  | 		server, | ||||||
|  | 		app.Recipe.Name, | ||||||
|  | 		deployConfig, | ||||||
|  | 		app.Domain, | ||||||
|  | 		currentVersion, | ||||||
|  | 		chaosVersion, | ||||||
|  | 		newVersion, | ||||||
|  | 	}) | ||||||
| 	table.Render() | 	table.Render() | ||||||
|  |  | ||||||
| 	if releaseNotes != "" && newVersion != "" { | 	if releaseNotes != "" && newVersion != "" { | ||||||
| @ -112,8 +120,8 @@ func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| // DeployOverview shows a deployment overview | // DeployOverview shows a deployment overview | ||||||
| func DeployOverview(app appPkg.App, version, message string) error { | func DeployOverview(app appPkg.App, version, chaosVersion, message string) error { | ||||||
| 	tableCol := []string{"server", "recipe", "config", "domain", "version"} | 	tableCol := []string{"server", "recipe", "config", "domain", "version", "chaos"} | ||||||
| 	table := formatter.CreateTable(tableCol) | 	table := formatter.CreateTable(tableCol) | ||||||
|  |  | ||||||
| 	deployConfig := "compose.yml" | 	deployConfig := "compose.yml" | ||||||
| @ -126,7 +134,14 @@ func DeployOverview(app appPkg.App, version, message string) error { | |||||||
| 		server = "local" | 		server = "local" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	table.Append([]string{server, app.Recipe.Name, deployConfig, app.Domain, version}) | 	table.Append([]string{ | ||||||
|  | 		server, | ||||||
|  | 		app.Recipe.Name, | ||||||
|  | 		deployConfig, | ||||||
|  | 		app.Domain, | ||||||
|  | 		version, | ||||||
|  | 		chaosVersion, | ||||||
|  | 	}) | ||||||
| 	table.Render() | 	table.Render() | ||||||
|  |  | ||||||
| 	if NoInput { | 	if NoInput { | ||||||
|  | |||||||
| @ -253,20 +253,20 @@ func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName stri | |||||||
| func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) (string, error) { | func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) (string, error) { | ||||||
| 	log.Debugf("retrieve deployed version whether %s is already deployed", stackName) | 	log.Debugf("retrieve deployed version whether %s is already deployed", stackName) | ||||||
|  |  | ||||||
| 	isDeployed, deployedVersion, err := stack.IsDeployed(context.Background(), cl, stackName) | 	deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !isDeployed { | 	if !deployMeta.IsDeployed { | ||||||
| 		return "", fmt.Errorf("%s is not deployed?", stackName) | 		return "", fmt.Errorf("%s is not deployed?", stackName) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if deployedVersion == "unknown" { | 	if deployMeta.Version == "unknown" { | ||||||
| 		return "", fmt.Errorf("failed to determine deployed version of %s", stackName) | 		return "", fmt.Errorf("failed to determine deployed version of %s", stackName) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return deployedVersion, nil | 	return deployMeta.Version, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // getAvailableUpgrades returns all available versions of an app that are newer | // getAvailableUpgrades returns all available versions of an app that are newer | ||||||
|  | |||||||
| @ -494,13 +494,13 @@ func GetAppComposeConfig(recipe string, opts stack.Deploy, appEnv envfile.AppEnv | |||||||
| func ExposeAllEnv(stackName string, compose *composetypes.Config, appEnv envfile.AppEnv) { | func ExposeAllEnv(stackName string, compose *composetypes.Config, appEnv envfile.AppEnv) { | ||||||
| 	for _, service := range compose.Services { | 	for _, service := range compose.Services { | ||||||
| 		if service.Name == "app" { | 		if service.Name == "app" { | ||||||
| 			log.Debugf("Add the following environment to the app service config of %s:", stackName) | 			log.Debugf("add the following environment to the app service config of %s:", stackName) | ||||||
| 			for k, v := range appEnv { | 			for k, v := range appEnv { | ||||||
| 				_, exists := service.Environment[k] | 				_, exists := service.Environment[k] | ||||||
| 				if !exists { | 				if !exists { | ||||||
| 					value := v | 					value := v | ||||||
| 					service.Environment[k] = &value | 					service.Environment[k] = &value | ||||||
| 					log.Debugf("Add Key: %s Value: %s to %s", k, value, stackName) | 					log.Debugf("add env var: %s value: %s to %s", k, value, stackName) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import ( | |||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
|  | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	stdlibErr "errors" | 	stdlibErr "errors" | ||||||
| @ -35,7 +36,7 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // Timeout to wait until docker services converge, default is 50s (random choice) | // Timeout to wait until docker services converge, default is 50s (random choice) | ||||||
| var WaitTimeout int = 50 | var WaitTimeout = 50 | ||||||
|  |  | ||||||
| type StackStatus struct { | type StackStatus struct { | ||||||
| 	Services []swarm.Service | 	Services []swarm.Service | ||||||
| @ -96,35 +97,64 @@ func GetDeployedServicesByName(ctx context.Context, cl *dockerClient.Client, sta | |||||||
| 	return StackStatus{services, nil} | 	return StackStatus{services, nil} | ||||||
| } | } | ||||||
|  |  | ||||||
| // IsDeployed chekcks whether an appp is deployed or not. | // DeployMeta is runtime metadata about an app deployment. | ||||||
| func IsDeployed(ctx context.Context, cl *dockerClient.Client, stackName string) (bool, string, error) { | type DeployMeta struct { | ||||||
| 	version := "unknown" | 	IsDeployed   bool   // whether the app is deployed or not | ||||||
| 	isDeployed := false | 	Version      string // the deployed version | ||||||
|  | 	IsChaos      string // whether or not the deployment is --chaos | ||||||
|  | 	ChaosVersion string // the --chaos deployment version | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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", // NOTE(d1): match string type used on label | ||||||
|  | 		ChaosVersion: "false", // NOTE(d1): match string type used on label | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	filter := filters.NewArgs() | 	filter := filters.NewArgs() | ||||||
| 	filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) | 	filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName)) | ||||||
|  |  | ||||||
| 	services, err := cl.ServiceList(ctx, types.ServiceListOptions{Filters: filter}) | 	services, err := cl.ServiceList(ctx, types.ServiceListOptions{Filters: filter}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return false, version, err | 		return deployMeta, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(services) > 0 { | 	if len(services) > 0 { | ||||||
|  | 		deployMeta.IsDeployed = true | ||||||
|  |  | ||||||
| 		for _, service := range services { | 		for _, service := range services { | ||||||
| 			labelKey := fmt.Sprintf("coop-cloud.%s.version", stackName) | 			splitter := fmt.Sprintf("%s_", stackName) | ||||||
| 			if deployedVersion, ok := service.Spec.Labels[labelKey]; ok { | 			serviceName := strings.Split(service.Spec.Name, splitter)[1] | ||||||
| 				version = deployedVersion |  | ||||||
| 				break | 			if serviceName == "app" { | ||||||
|  | 				labelKey := fmt.Sprintf("coop-cloud.%s.version", stackName) | ||||||
|  | 				if deployedVersion, ok := service.Spec.Labels[labelKey]; ok { | ||||||
|  | 					deployMeta.Version = deployedVersion | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				labelKey = fmt.Sprintf("coop-cloud.%s.chaos", stackName) | ||||||
|  | 				if isChaos, ok := service.Spec.Labels[labelKey]; ok { | ||||||
|  | 					deployMeta.IsChaos = isChaos | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				labelKey = fmt.Sprintf("coop-cloud.%s.chaos-version", stackName) | ||||||
|  | 				if chaosVersion, ok := service.Spec.Labels[labelKey]; ok { | ||||||
|  | 					deployMeta.ChaosVersion = chaosVersion | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		log.Debugf("%s has been detected as deployed with version %s", stackName, version) | 		log.Debugf("%s has been detected as deployed: %v", stackName, deployMeta) | ||||||
|  |  | ||||||
| 		return true, version, nil | 		return deployMeta, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debugf("%s has been detected as not deployed", stackName) | 	log.Debugf("%s has been detected as not deployed", stackName) | ||||||
| 	return isDeployed, version, nil |  | ||||||
|  | 	return deployMeta, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // pruneServices removes services that are no longer referenced in the source | // pruneServices removes services that are no longer referenced in the source | ||||||
|  | |||||||
| @ -72,80 +72,6 @@ teardown(){ | |||||||
|   assert_equal $(_get_current_hash) $wantHash |   assert_equal $(_get_current_hash) $wantHash | ||||||
| } | } | ||||||
|  |  | ||||||
| @test "bail if unstaged changes and no --chaos" { |  | ||||||
|   run bash -c "echo foo >> $ABRA_DIR/recipes/$TEST_RECIPE/foo" |  | ||||||
|   assert_success |  | ||||||
|   assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo" |  | ||||||
|  |  | ||||||
|   run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status |  | ||||||
|   assert_success |  | ||||||
|   assert_output --partial 'foo' |  | ||||||
|  |  | ||||||
|   run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input --no-converge-checks |  | ||||||
|   assert_failure |  | ||||||
|   assert_output --partial 'locally unstaged changes' |  | ||||||
|   refute_output --partial 'chaos' |  | ||||||
|  |  | ||||||
|   run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo" |  | ||||||
|   assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| # bats test_tags=slow |  | ||||||
| @test "do not bail if unstaged changes and --chaos" { |  | ||||||
|   run bash -c 'echo "unstaged changes" >> "$ABRA_DIR/recipes/$TEST_RECIPE/foo"' |  | ||||||
|   assert_success |  | ||||||
|   assert_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo" |  | ||||||
|  |  | ||||||
|   run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" status |  | ||||||
|   assert_success |  | ||||||
|   assert_output --partial 'foo' |  | ||||||
|  |  | ||||||
|   run $ABRA app deploy "$TEST_APP_DOMAIN" \ |  | ||||||
|     --chaos --no-input --no-converge-checks |  | ||||||
|   assert_success |  | ||||||
|   assert_output --partial 'chaos' |  | ||||||
|  |  | ||||||
|   run $ABRA app rollback "$TEST_APP_DOMAIN" \ |  | ||||||
|     --chaos --no-input --no-converge-checks |  | ||||||
|   assert_success |  | ||||||
|   assert_output --partial 'chaos' |  | ||||||
|  |  | ||||||
|   _undeploy_app |  | ||||||
|  |  | ||||||
|   run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE/foo" |  | ||||||
|   assert_not_exists "$ABRA_DIR/recipes/$TEST_RECIPE/foo" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| # bats test_tags=slow |  | ||||||
| @test "ensure same commit if --chaos" { |  | ||||||
|   latestCommit="$(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --short HEAD)" |  | ||||||
|  |  | ||||||
|   run $ABRA app deploy "$TEST_APP_DOMAIN" \ |  | ||||||
|     --no-input --chaos |  | ||||||
|   assert_success |  | ||||||
|   assert_output --partial "${latestCommit}" |  | ||||||
|   assert_output --partial 'chaos' |  | ||||||
|  |  | ||||||
|   run $ABRA app rollback "$TEST_APP_DOMAIN" \ |  | ||||||
|     --chaos --no-input --no-converge-checks |  | ||||||
|   assert_success |  | ||||||
|   assert_output --partial "$latestCommit" |  | ||||||
|   assert_output --partial 'chaos' |  | ||||||
| } |  | ||||||
|  |  | ||||||
| # bats test_tags=slow |  | ||||||
| @test "no rollback if lint error" { |  | ||||||
|   _deploy_app |  | ||||||
|  |  | ||||||
|   run sed -i '/traefik.enable=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml" |  | ||||||
|   assert_success |  | ||||||
|  |  | ||||||
|   run $ABRA app rollback "$TEST_APP_DOMAIN" \ |  | ||||||
|     --no-input --chaos --chaos --no-converge-checks |  | ||||||
|   assert_failure |  | ||||||
|   assert_output --partial 'failed lint checks' |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @test "error if not already deployed" { | @test "error if not already deployed" { | ||||||
|   run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input |   run $ABRA app rollback "$TEST_APP_DOMAIN" --no-input | ||||||
|   assert_failure |   assert_failure | ||||||
| @ -174,13 +100,6 @@ teardown(){ | |||||||
|   assert_output --partial 'is not a downgrade' |   assert_output --partial 'is not a downgrade' | ||||||
| } | } | ||||||
|  |  | ||||||
| @test "bail out if specific version and chaos" { |  | ||||||
|   run $ABRA app rollback "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \ |  | ||||||
|     --chaos --no-input --no-converge-checks |  | ||||||
|   assert_failure |  | ||||||
|   assert_output --partial 'cannot use' |  | ||||||
| } |  | ||||||
|  |  | ||||||
| # bats test_tags=slow | # bats test_tags=slow | ||||||
| @test "rollback to previous version" { | @test "rollback to previous version" { | ||||||
|   run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks |   run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks | ||||||
| @ -202,3 +121,27 @@ teardown(){ | |||||||
|   assert_success |   assert_success | ||||||
|   assert_output --partial "0.1.0+1.20.0" |   assert_output --partial "0.1.0+1.20.0" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | # bats test_tags=slow | ||||||
|  | @test "rollback chaos deployment" { | ||||||
|  |   tagHash=$(_get_tag_hash "0.2.0+1.21.0") | ||||||
|  |   run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$tagHash" | ||||||
|  |   assert_success | ||||||
|  |  | ||||||
|  |   run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks --chaos | ||||||
|  |   assert_success | ||||||
|  |   assert_output --partial "${tagHash:0:8}" | ||||||
|  |  | ||||||
|  |   run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks | ||||||
|  |   assert_success | ||||||
|  |   assert_output --partial "0.1.1+1.20.2" | ||||||
|  |   assert_output --partial "${tagHash:0:8}" | ||||||
|  |  | ||||||
|  |   run $ABRA app rollback "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input --no-converge-checks | ||||||
|  |   assert_success | ||||||
|  |   assert_output --partial "0.1.0+1.20.0" | ||||||
|  |  | ||||||
|  |   tagHash=$(_get_tag_hash "0.1.1+1.20.2") | ||||||
|  |   refute_output --partial "${tagHash:0:8}" | ||||||
|  |   assert_output --partial "false" | ||||||
|  | } | ||||||
|  | |||||||
| @ -41,6 +41,9 @@ teardown(){ | |||||||
| @test "undeploy app" { | @test "undeploy app" { | ||||||
|   _deploy_app |   _deploy_app | ||||||
|   _undeploy_app |   _undeploy_app | ||||||
|  |  | ||||||
|  |   # NOTE(d1): ensure not chaos undeploy | ||||||
|  |   assert_output --partial 'false' | ||||||
| } | } | ||||||
|  |  | ||||||
| # bats test_tags=slow | # bats test_tags=slow | ||||||
| @ -50,3 +53,15 @@ teardown(){ | |||||||
|   run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input --prune |   run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input --prune | ||||||
|   assert_success |   assert_success | ||||||
| } | } | ||||||
|  |  | ||||||
|  | # bats test_tags=slow | ||||||
|  | @test "undeploy chaos deployment" { | ||||||
|  |   run $ABRA app deploy "$TEST_APP_DOMAIN" \ | ||||||
|  |     --no-input --no-converge-checks --chaos | ||||||
|  |  | ||||||
|  |   _undeploy_app | ||||||
|  |    | ||||||
|  |   # NOTE(d1): ensure chaos undeploy | ||||||
|  |   assert_output --partial ${_get_current_hash:0:8} | ||||||
|  |   refute_output --partial 'false' | ||||||
|  | } | ||||||
|  | |||||||
| @ -43,26 +43,6 @@ teardown(){ | |||||||
|   assert_output --partial 'is not an upgrade' |   assert_output --partial 'is not an upgrade' | ||||||
| } | } | ||||||
|  |  | ||||||
| @test "bail out if specific version and chaos" { |  | ||||||
|   run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" \ |  | ||||||
|     --chaos --no-input --no-converge-checks |  | ||||||
|   assert_failure |  | ||||||
|   assert_output --partial 'cannot use' |  | ||||||
| } |  | ||||||
|  |  | ||||||
| # bats test_tags=slow |  | ||||||
| @test "no upgrade if lint error" { |  | ||||||
|   _deploy_app |  | ||||||
|  |  | ||||||
|   run sed -i '/traefik.enable=.*/d' "$ABRA_DIR/recipes/$TEST_RECIPE/compose.yml" |  | ||||||
|   assert_success |  | ||||||
|  |  | ||||||
|   run $ABRA app upgrade "$TEST_APP_DOMAIN" \ |  | ||||||
|     --no-input --no-converge-checks --chaos |  | ||||||
|   assert_failure |  | ||||||
|   assert_output --partial 'failed lint checks' |  | ||||||
| } |  | ||||||
|  |  | ||||||
| # bats test_tags=slow | # bats test_tags=slow | ||||||
| @test "no upgrade if on latest version" { | @test "no upgrade if on latest version" { | ||||||
|   _deploy_app |   _deploy_app | ||||||
| @ -185,3 +165,27 @@ teardown(){ | |||||||
|   assert_output --partial 'release notes bar' # 0.1.1+1.20.2 |   assert_output --partial 'release notes bar' # 0.1.1+1.20.2 | ||||||
|   assert_output --partial 'release notes baz' # 0.2.0+1.21.0 |   assert_output --partial 'release notes baz' # 0.2.0+1.21.0 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | # bats test_tags=slow | ||||||
|  | @test "upgrade chaos deployment" { | ||||||
|  |   tagHash=$(_get_tag_hash "0.1.0+1.20.0") | ||||||
|  |   run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$tagHash" | ||||||
|  |   assert_success | ||||||
|  |  | ||||||
|  |   run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks --chaos | ||||||
|  |   assert_success | ||||||
|  |   assert_output --partial "${tagHash:0:8}" | ||||||
|  |  | ||||||
|  |   run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.1.1+1.20.2" --no-input --no-converge-checks | ||||||
|  |   assert_success | ||||||
|  |   assert_output --partial "0.1.1+1.20.2" | ||||||
|  |   assert_output --partial "${tagHash:0:8}" | ||||||
|  |  | ||||||
|  |   run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input --no-converge-checks | ||||||
|  |   assert_success | ||||||
|  |   assert_output --partial "0.2.0+1.21.0" | ||||||
|  |  | ||||||
|  |   tagHash=$(_get_tag_hash "0.1.1+1.20.2") | ||||||
|  |   refute_output --partial "${tagHash:0:8}" | ||||||
|  |   assert_output --partial "false" | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user