diff --git a/cmd/deploy_execute.go b/cmd/deploy_execute.go index 74c1821..c37c116 100644 --- a/cmd/deploy_execute.go +++ b/cmd/deploy_execute.go @@ -28,7 +28,7 @@ func init() { } -var _ = addCommand(deployCmd, &cobra.Command{ +var deployExecuteCmd = addCommand(deployCmd, &cobra.Command{ Use: "execute {path | {release|stable|unstable}} [apps...]", Args: cobra.MinimumNArgs(1), Short: "Executes a deployment against the current environment.", @@ -92,10 +92,12 @@ var _ = addCommand(deployCmd, &cobra.Command{ return err }, -}, func(cmd *cobra.Command) { +},applyDeployExecuteCmdFlags) + +func applyDeployExecuteCmdFlags(cmd *cobra.Command) { cmd.Flags().Bool(argDeployExecuteSkipValidate, false, "Skip validation") cmd.Flags().Bool(argDeployExecuteValuesOnly, false, "Display the values which would be used for the deploy, but do not actually execute.") -}) +} const ( argDeployExecuteSkipValidate = "skip-validation" diff --git a/cmd/deploy_plan.go b/cmd/deploy_plan.go index 7917214..329c05f 100644 --- a/cmd/deploy_plan.go +++ b/cmd/deploy_plan.go @@ -134,7 +134,9 @@ var deployPlanCmd = addCommand(deployCmd, &cobra.Command{ return err }, -}, func(cmd *cobra.Command) { +}, applyDeployPlanFlags) + +func applyDeployPlanFlags(cmd *cobra.Command) { cmd.Flags().String(argDeployPlanPath, "", "Dir where plan should be stored.") cmd.Flags().String(argDeployPlanProviderPriority, "", "Provider to use to deploy apps (current, stable, unstable, or workspace).") cmd.Flags().StringSlice(argDeployPlanApps, []string{}, "AppDeploymentProgress to include.") @@ -142,7 +144,7 @@ var deployPlanCmd = addCommand(deployCmd, &cobra.Command{ cmd.Flags().Bool(argDeployPlanIgnoreDeps, false, "Don't validate dependencies.") cmd.Flags().Bool(argDeployPlanAutoDeps, false, "Automatically include dependencies.") cmd.Flags().Bool(argDeployPlanUpdate, false, "Update an existing plan rather than creating a new one.") -}) +} const ( argDeployPlanPath = "path" diff --git a/cmd/deploy_validate.go b/cmd/deploy_validate.go index bef9463..837815c 100644 --- a/cmd/deploy_validate.go +++ b/cmd/deploy_validate.go @@ -30,7 +30,7 @@ func init() { } -var _ = addCommand(deployCmd, &cobra.Command{ +var deployValidateCmd = addCommand(deployCmd, &cobra.Command{ Use: "validate {path | release}", Args: cobra.ExactArgs(1), Short: "Validates a deployment plan.", diff --git a/cmd/release.go b/cmd/release.go index 9fd7428..ccff230 100644 --- a/cmd/release.go +++ b/cmd/release.go @@ -28,6 +28,36 @@ var releaseCmd = addCommand(rootCmd, &cobra.Command{ Use: "release", Aliases: []string{"rel", "r"}, Short: "Contains sub-commands for releases.", + Long: strings.ReplaceAll(`The release command is the entry-point for interacting with releases, which are +snapshots of apps that will be deployed together as an upgrade (or initial install) to a cluster. +There are two release "slots", stable and unstable. These are both stored (by default) in the /releases +folder in the folder where the platform file is located. The unstable slot is intended for sharing the latest +development versions of apps, to facilitate deployment of those apps without needing to clone their repos. +The stable slot contains the versions of apps which are (or will shortly be) deployed to production. The +files under the stable slot should never be modified except on a release branch of the platform repo. + +The lifecycle of a release is as follows: +- 'bosun release create' will create a new release. You be asked which release should be used as a base, and what +bump to apply to the release. Use a patch bump for hotfixes and a minor bump for releases, unless it's a major change. +When you run this command bosun will create and push a new branch for that release. The newly created release will +contain the same app versions as the release it was based on. +- 'bosun release add {app}' will create a release branch for the given app and add it to the release. You can specify +which branch to add the app from and what version bump to apply. +- 'bosun release update {app}' will update the release by copying the manifest from the release branch for that app. +This is used to pull changes made during RC testing into the release. +- 'bosun release deploy plan' creates a deploy plan for the current release. This copies the app manifests which have +been specifically added to the release to a deployment plan location. You can include additional apps that aren't owned +by the release in the deployment plan by using flags. You should always run this command after you run +'bosun release update'; bosun will remind you if you forget. +- 'bosun release deploy validate' will check that the correct images exist for the release +- 'bosun release deploy execute [apps...]' will deploy the apps listed to the current cluster, or all apps if you don't +list any +- 'bosun release deploy show' will show the deploy progress to the current cluster +- 'bosun release commit plan' should be used after the release is fully deployed. It will prepare a plan for merging +all the release branches back to develop and master, as well as tagging them. +- 'bosun release commit execute' will execute the commit plan. If you need to abort the commit plan to do a complicated +merge resolution or something you can run this command again to pick up where you left off. +`, "'", "`"), }) var originalCurrentRelease *string @@ -53,6 +83,47 @@ var _ = addCommand(releaseCmd, &cobra.Command{ }, }) + +var releaseDeployCmd = addCommand(releaseCmd, &cobra.Command{ + Use: "deploy", + Short: "Deployment commands for releases.", + SilenceUsage: true, +}) + +var _ = addCommand(releaseDeployCmd, &cobra.Command{ + Use: "plan", + Short: "Plans the deployment of a release", + RunE: func(cmd *cobra.Command, args []string) error { + return deployPlanCmd.RunE(cmd, []string{"release"}) + }, +}, applyDeployPlanFlags) + +var _ = addCommand(releaseDeployCmd, &cobra.Command{ + Use: "validate", + Short: "Validates the deployment of a release", + RunE: func(cmd *cobra.Command, args []string) error { + return deployValidateCmd.RunE(cmd, []string{"release"}) + }, +}) +var _ = addCommand(releaseDeployCmd, &cobra.Command{ + Use: "show", + Short: "Shows the progress made in deploying the release", + RunE: func(cmd *cobra.Command, args []string) error { + return deployShowCmd.RunE(cmd, []string{"release"}) + }, +}) + +var _ = addCommand(releaseDeployCmd, &cobra.Command{ + Use: "execute [apps...]", + Short: "Executes the deployment of a release", + RunE: func(cmd *cobra.Command, args []string) error { + + args = append([]string{"release"}, args...) + return deployExecuteCmd.RunE(cmd, args) + }, +}, applyDeployExecuteCmdFlags) + + var releaseListCmd = addCommand(releaseCmd, &cobra.Command{ Use: "list", Aliases: []string{"ls"}, @@ -377,78 +448,6 @@ var releaseTestCmd = addCommand(releaseCmd, &cobra.Command{ }, }, withFilteringFlags) -var releaseDeployCmd = addCommand(releaseCmd, &cobra.Command{ - Use: "deploy [apps...]", - Short: "Deploys the release.", - Long: `Deploys the current release to the current environment. If apps are provided, -only those apps will be deployed. Otherwise, all apps in the release will be deployed (subject to --include flags).`, - SilenceErrors: true, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) error { - viper.BindPFlags(cmd.Flags()) - - b := MustGetBosun() - release := mustGetActiveRelease(b) - ctx := b.NewContext() - if err := b.ConfirmEnvironment(); err != nil { - return err - } - - valueSets, err := getValueSetSlice(b, b.GetCurrentEnvironment()) - if err != nil { - return err - } - - deploySettings := bosun.DeploySettings{ - SharedDeploySettings: bosun.SharedDeploySettings{ - Environment: ctx.Environment(), - Recycle: viper.GetBool(ArgReleaseRecycle), - }, - ValueSets: valueSets, - Manifest: release, - ForceDeployApps: map[string]bool{}, - } - for _, appName := range args { - deploySettings.ForceDeployApps[appName] = true - } - - getFilterParams(b, args).ApplyToDeploySettings(&deploySettings) - - deploy, err := bosun.NewDeploy(ctx, deploySettings) - if err != nil { - return err - } - - color.Yellow("About to deploy the following apps:") - for _, app := range deploy.AppDeploys { - fmt.Printf("- %s: %s (tag %s) => namespace:%s \n", app.Name, app.AppConfig.Version, deploySettings.GetImageTag(app.AppManifest.AppMetadata), app.Namespace) - } - - if !confirm("Is this what you expected") { - return errors.New("Deploy cancelled.") - } - - if viper.GetBool(ArgReleaseSkipValidate) { - ctx.Log().Warn("Validation disabled.") - } else { - ctx.Log().Info("Validating...") - err = validateDeploy(b, ctx, deploy) - if err != nil { - return err - } - } - - err = deploy.Deploy(ctx) - - return err - }, -}, func(cmd *cobra.Command) { - cmd.Flags().Bool(ArgReleaseSkipValidate, false, "Skips running validation before deploying the release.") - cmd.Flags().Bool(ArgReleaseRecycle, false, "Recycles apps after they are deployed.") -}, - withFilteringFlags, - withValueSetFlags) - var releaseUpdateCmd = addCommand(releaseCmd, &cobra.Command{ Use: "update [apps...]", Short: "Updates the current release by pulling in the manifests from the app repos.", diff --git a/cmd/release_create.go b/cmd/release_create.go index 6524290..b9518d3 100644 --- a/cmd/release_create.go +++ b/cmd/release_create.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/naveego/bosun/pkg/bosun" + "github.com/naveego/bosun/pkg/cli" "github.com/naveego/bosun/pkg/semver" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -10,37 +11,45 @@ import ( const ( argReleaseCreateBase = "base" - argReleaseCreateVersion = "version" + argReleaseBump = "bump" ) // releaseCmd represents the release command var releaseCreateCmd = addCommand(releaseCmd, &cobra.Command{ - Use: "create {release version}", + Use: "create", Short: "Create a release.", - Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { b, p := getReleaseCmdDeps() - version, err := semver.Parse(args[0]) - - if err != nil { - return err - } req := bosun.ReleaseCreateSettings{ - Version: version, } + var err error var baseVersion semver.Version baseVersionFlag := viper.GetString(argReleaseCreateBase) if baseVersionFlag != "" { - baseVersion, err = semver.Parse(baseVersionFlag) if err != nil { return err } req.Base = &baseVersion + } else { + currentRelease, releaseErr := p.GetStableRelease() + if releaseErr != nil { + return releaseErr + } + req.Base = ¤tRelease.Version + } + + bump := viper.GetString(argReleaseBump) + + if bump == "" { + _, v := cli.RequestBump("What bump should be applied to the release version?", *req.Base) + req.Version = *v + } else { + req.Version, err = req.Base.Bump(bump) } r, err := p.CreateRelease(b.NewContext(), req) @@ -60,5 +69,5 @@ var releaseCreateCmd = addCommand(releaseCmd, &cobra.Command{ }, }, func(cmd *cobra.Command) { cmd.Flags().String(argReleaseCreateBase, "", "The release to base the release on. Defaults to the current release.") - cmd.Flags().String(argReleaseCreateVersion, "", "The version for the new release.") + cmd.Flags().String(argReleaseBump, "", "The version for the new release.") }) diff --git a/docs/bosun_release.md b/docs/bosun_release.md index de3de13..8caa68f 100644 --- a/docs/bosun_release.md +++ b/docs/bosun_release.md @@ -4,7 +4,36 @@ Contains sub-commands for releases. ### Synopsis -Contains sub-commands for releases. +The release command is the entry-point for interacting with releases, which are +snapshots of apps that will be deployed together as an upgrade (or initial install) to a cluster. +There are two release "slots", stable and unstable. These are both stored (by default) in the /releases +folder in the folder where the platform file is located. The unstable slot is intended for sharing the latest +development versions of apps, to facilitate deployment of those apps without needing to clone their repos. +The stable slot contains the versions of apps which are (or will shortly be) deployed to production. The +files under the stable slot should never be modified except on a release branch of the platform repo. + +The lifecycle of a release is as follows: +- `bosun release create` will create a new release. You be asked which release should be used as a base, and what +bump to apply to the release. Use a patch bump for hotfixes and a minor bump for releases, unless it`s a major change. +When you run this command bosun will create and push a new branch for that release. The newly created release will +contain the same app versions as the release it was based on. +- `bosun release add {app}` will create a release branch for the given app and add it to the release. You can specify +which branch to add the app from and what version bump to apply. +- `bosun release update {app}` will update the release by copying the manifest from the release branch for that app. +This is used to pull changes made during RC testing into the release. +- `bosun release deploy plan` creates a deploy plan for the current release. This copies the app manifests which have +been specifically added to the release to a deployment plan location. You can include additional apps that aren`t owned +by the release in the deployment plan by using flags. You should always run this command after you run +`bosun release update`; bosun will remind you if you forget. +- `bosun release deploy validate` will check that the correct images exist for the release +- `bosun release deploy execute [apps...]` will deploy the apps listed to the current cluster, or all apps if you don`t +list any +- `bosun release deploy show` will show the deploy progress to the current cluster +- `bosun release commit plan` should be used after the release is fully deployed. It will prepare a plan for merging +all the release branches back to develop and master, as well as tagging them. +- `bosun release commit execute` will execute the commit plan. If you need to abort the commit plan to do a complicated +merge resolution or something you can run this command again to pick up where you left off. + ### Options @@ -33,7 +62,7 @@ Contains sub-commands for releases. * [bosun release add](bosun_release_add.md) - Adds an app to the current release. * [bosun release commit](bosun_release_commit.md) - Commands for merging a release branch back to develop and master. * [bosun release create](bosun_release_create.md) - Create a release. -* [bosun release deploy](bosun_release_deploy.md) - Deploys the release. +* [bosun release deploy](bosun_release_deploy.md) - Deployment commands for releases. * [bosun release dot](bosun_release_dot.md) - Prints a dot diagram of the release. * [bosun release list](bosun_release_list.md) - Lists known releases. * [bosun release show](bosun_release_show.md) - Lists the apps in the current release. diff --git a/docs/bosun_release_create.md b/docs/bosun_release_create.md index 91425a7..456f941 100644 --- a/docs/bosun_release_create.md +++ b/docs/bosun_release_create.md @@ -7,15 +7,15 @@ Create a release. Create a release. ``` -bosun release create {release version} [flags] +bosun release create [flags] ``` ### Options ``` - --base string The release to base the release on. Defaults to the current release. - -h, --help help for create - --version string The version for the new release. + --base string The release to base the release on. Defaults to the current release. + --bump string The version for the new release. + -h, --help help for create ``` ### Options inherited from parent commands diff --git a/docs/bosun_release_deploy.md b/docs/bosun_release_deploy.md index da937ac..7e41ea5 100644 --- a/docs/bosun_release_deploy.md +++ b/docs/bosun_release_deploy.md @@ -1,28 +1,15 @@ ## bosun release deploy -Deploys the release. +Deployment commands for releases. ### Synopsis -Deploys the current release to the current environment. If apps are provided, -only those apps will be deployed. Otherwise, all apps in the release will be deployed (subject to --include flags). - -``` -bosun release deploy [apps...] [flags] -``` +Deployment commands for releases. ### Options ``` - --all Will include all items. - --exclude strings Will exclude items with labels matching filter (like x==y or x?=prefix-.*). - -h, --help help for deploy - --include strings Will include items with labels matching filter (like x==y or x?=prefix-.*). - --labels strings Will include any items where a label with that key is present. - --recycle Recycles apps after they are deployed. - -s, --set strings Value overrides to set in this deploy, as path.to.key=value pairs. - --skip-validation Skips running validation before deploying the release. - -v, --value-sets strings Additional value sets to include in this deploy. + -h, --help help for deploy ``` ### Options inherited from parent commands @@ -43,5 +30,9 @@ bosun release deploy [apps...] [flags] ### SEE ALSO * [bosun release](bosun_release.md) - Contains sub-commands for releases. +* [bosun release deploy execute](bosun_release_deploy_execute.md) - Executes the deployment of a release +* [bosun release deploy plan](bosun_release_deploy_plan.md) - Plans the deployment of a release +* [bosun release deploy show](bosun_release_deploy_show.md) - Shows the progress made in deploying the release +* [bosun release deploy validate](bosun_release_deploy_validate.md) - Validates the deployment of a release ###### Auto generated by spf13/cobra on 23-Sep-2021 diff --git a/docs/bosun_release_deploy_execute.md b/docs/bosun_release_deploy_execute.md new file mode 100644 index 0000000..bb52e93 --- /dev/null +++ b/docs/bosun_release_deploy_execute.md @@ -0,0 +1,40 @@ +## bosun release deploy execute + +Executes the deployment of a release + +### Synopsis + +Executes the deployment of a release + +``` +bosun release deploy execute [apps...] [flags] +``` + +### Options + +``` + -h, --help help for execute + --skip-validation Skip validation + --values-only Display the values which would be used for the deploy, but do not actually execute. +``` + +### Options inherited from parent commands + +``` + --cluster string Set to target a specific cluster. + --config-file string Config file for Bosun. You can also set BOSUN_CONFIG. (default "/home/steve/.bosun/bosun.yaml") + --confirm-env string Set to confirm that the environment is correct when targeting a protected environment. + --dry-run Display rendered plans, but do not actually execute (not supported by all commands). + --force Force the requested command to be executed even if heuristics indicate it should not be. + --no-report Disable reporting of deploys to github. + -o, --output table Output format. Options are table, `json`, or `yaml`. Only respected by a some commands. + --sudo Use sudo when running commands like docker. + --verbose Enable verbose logging. + -V, --verbose-errors Enable verbose errors with stack traces. +``` + +### SEE ALSO + +* [bosun release deploy](bosun_release_deploy.md) - Deployment commands for releases. + +###### Auto generated by spf13/cobra on 23-Sep-2021 diff --git a/docs/bosun_release_deploy_plan.md b/docs/bosun_release_deploy_plan.md new file mode 100644 index 0000000..b8860d0 --- /dev/null +++ b/docs/bosun_release_deploy_plan.md @@ -0,0 +1,45 @@ +## bosun release deploy plan + +Plans the deployment of a release + +### Synopsis + +Plans the deployment of a release + +``` +bosun release deploy plan [flags] +``` + +### Options + +``` + --all Deploy all apps which target the current environment. + --apps strings AppDeploymentProgress to include. + --auto-deps Automatically include dependencies. + -h, --help help for plan + --ignore-deps Don't validate dependencies. + --path string Dir where plan should be stored. + --providers string Provider to use to deploy apps (current, stable, unstable, or workspace). + --update Update an existing plan rather than creating a new one. +``` + +### Options inherited from parent commands + +``` + --cluster string Set to target a specific cluster. + --config-file string Config file for Bosun. You can also set BOSUN_CONFIG. (default "/home/steve/.bosun/bosun.yaml") + --confirm-env string Set to confirm that the environment is correct when targeting a protected environment. + --dry-run Display rendered plans, but do not actually execute (not supported by all commands). + --force Force the requested command to be executed even if heuristics indicate it should not be. + --no-report Disable reporting of deploys to github. + -o, --output table Output format. Options are table, `json`, or `yaml`. Only respected by a some commands. + --sudo Use sudo when running commands like docker. + --verbose Enable verbose logging. + -V, --verbose-errors Enable verbose errors with stack traces. +``` + +### SEE ALSO + +* [bosun release deploy](bosun_release_deploy.md) - Deployment commands for releases. + +###### Auto generated by spf13/cobra on 23-Sep-2021 diff --git a/docs/bosun_release_deploy_show.md b/docs/bosun_release_deploy_show.md new file mode 100644 index 0000000..c8bc5ea --- /dev/null +++ b/docs/bosun_release_deploy_show.md @@ -0,0 +1,38 @@ +## bosun release deploy show + +Shows the progress made in deploying the release + +### Synopsis + +Shows the progress made in deploying the release + +``` +bosun release deploy show [flags] +``` + +### Options + +``` + -h, --help help for show +``` + +### Options inherited from parent commands + +``` + --cluster string Set to target a specific cluster. + --config-file string Config file for Bosun. You can also set BOSUN_CONFIG. (default "/home/steve/.bosun/bosun.yaml") + --confirm-env string Set to confirm that the environment is correct when targeting a protected environment. + --dry-run Display rendered plans, but do not actually execute (not supported by all commands). + --force Force the requested command to be executed even if heuristics indicate it should not be. + --no-report Disable reporting of deploys to github. + -o, --output table Output format. Options are table, `json`, or `yaml`. Only respected by a some commands. + --sudo Use sudo when running commands like docker. + --verbose Enable verbose logging. + -V, --verbose-errors Enable verbose errors with stack traces. +``` + +### SEE ALSO + +* [bosun release deploy](bosun_release_deploy.md) - Deployment commands for releases. + +###### Auto generated by spf13/cobra on 23-Sep-2021 diff --git a/docs/bosun_release_deploy_validate.md b/docs/bosun_release_deploy_validate.md new file mode 100644 index 0000000..8b65bb8 --- /dev/null +++ b/docs/bosun_release_deploy_validate.md @@ -0,0 +1,38 @@ +## bosun release deploy validate + +Validates the deployment of a release + +### Synopsis + +Validates the deployment of a release + +``` +bosun release deploy validate [flags] +``` + +### Options + +``` + -h, --help help for validate +``` + +### Options inherited from parent commands + +``` + --cluster string Set to target a specific cluster. + --config-file string Config file for Bosun. You can also set BOSUN_CONFIG. (default "/home/steve/.bosun/bosun.yaml") + --confirm-env string Set to confirm that the environment is correct when targeting a protected environment. + --dry-run Display rendered plans, but do not actually execute (not supported by all commands). + --force Force the requested command to be executed even if heuristics indicate it should not be. + --no-report Disable reporting of deploys to github. + -o, --output table Output format. Options are table, `json`, or `yaml`. Only respected by a some commands. + --sudo Use sudo when running commands like docker. + --verbose Enable verbose logging. + -V, --verbose-errors Enable verbose errors with stack traces. +``` + +### SEE ALSO + +* [bosun release deploy](bosun_release_deploy.md) - Deployment commands for releases. + +###### Auto generated by spf13/cobra on 23-Sep-2021 diff --git a/pkg/cli/interact.go b/pkg/cli/interact.go index 5b16b56..40a50b3 100644 --- a/pkg/cli/interact.go +++ b/pkg/cli/interact.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2" "github.com/manifoldco/promptui" + "github.com/naveego/bosun/pkg/semver" "github.com/pkg/errors" "golang.org/x/crypto/ssh/terminal" "os" @@ -108,6 +109,65 @@ func RequestChoice(message string, options ...string) string { } +func RequestBump(message string, currentVersion semver.Version) (semver.Bump, *semver.Version) { + + if !IsInteractive() { + panic(fmt.Sprintf("Requested bump from user but no terminal is attached: %q, %v", message)) + } + + var bump semver.Bump + var version *semver.Version + var tempVersion semver.Version + var response string + + message = fmt.Sprintf("%s (current version is %s)", message, currentVersion) + + prompt := &survey.Select{ + Message: message, + Options: []string { + string(semver.BumpMajor), + string(semver.BumpMinor), + string(semver.BumpPatch), + string(semver.BumpNone), + "custom", + }, + } + err := survey.AskOne(prompt, &response) + + if err != nil { + fmt.Printf("User quit %s\n", err) + os.Exit(0) + } + switch response { + case "custom": + versionPrompt := &survey.Input{ + Message: "Enter the version number you want", + } + err = survey.AskOne(versionPrompt, &response) + if err != nil { + fmt.Printf("User quit %s\n", err) + os.Exit(0) + } + tempVersion, err = semver.Parse(response) + if err != nil { + fmt.Printf("Invalid version %s: %s\n", response, err) + os.Exit(1) + } + version = &tempVersion + bump = semver.Unknown + default: + tempVersion, err = currentVersion.Bump(response) + if err != nil { + fmt.Printf("Invalid bump %s: %s\n", response, err) + os.Exit(1) + } + version = &tempVersion + bump = semver.Bump(response) + } + + return bump, version +} + func RequestMultiChoiceIfEmpty(defaultValue []string, message string, options []string) []string { if len(defaultValue) > 0 { return defaultValue