deploy

package
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 15, 2023 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Example (Deploy_help)
package main

import (
	"os"

	"github.com/aws-cloudformation/rain/internal/cmd/deploy"
)

func main() {
	os.Args = []string{
		os.Args[0],
		"--help",
	}

	deploy.Cmd.Execute()
}
Output:

Creates or updates a CloudFormation stack named <stack> from the template file <template>.
If you don't specify a stack name, rain will use the template filename minus its extension.

If a template needs to be packaged before it can be deployed, rain will package the template first.
Rain will attempt to create an S3 bucket to store artifacts that it packages and deploys.
The bucket's name will be of the format rain-artifacts-<AWS account id>-<AWS region>.

The config flag can be used to programmatically set tags and parameters.
The format is similar to the "Template configuration file" for AWS CodePipeline just without the
'StackPolicy' key. The file can be in YAML or JSON format.

JSON:
  {
    "Parameters" : {
      "NameOfTemplateParameter" : "ValueOfParameter",
      ...
    },
    "Tags" : {
      "TagKey" : "TagValue",
      ...
    }
  }

YAML:
  Parameters:
    NameOfTemplateParameter: ValueOfParameter
    ...
  Tags:
    TagKey: TagValue
    ...

Usage:
  deploy <template> [stack]

Flags:
  -c, --config string            YAML or JSON file to set tags and parameters
  -d, --detach                   once deployment has started, don't wait around for it to finish
  -h, --help                     help for deploy
  -k, --keep                     keep deployed resources after a failure by disabling rollbacks
      --params strings           set parameter values; use the format key1=value1,key2=value2
      --role-arn string          ARN of an IAM role that CloudFormation should assume to deploy the stack
      --tags strings             add tags to the stack; use the format key1=value1,key2=value2
  -t, --termination-protection   enable termination protection on the stack
  -y, --yes                      don't ask questions; just deploy

Index

Examples

Constants

View Source
const MaxStackNameLength = 128

Variables

View Source
var Cmd = &cobra.Command{
	Use:   "deploy <template> [stack]",
	Short: "Deploy a CloudFormation stack from a local template",
	Long: `Creates or updates a CloudFormation stack named <stack> from the template file <template>.
If you don't specify a stack name, rain will use the template filename minus its extension.

If a template needs to be packaged before it can be deployed, rain will package the template first.
Rain will attempt to create an S3 bucket to store artifacts that it packages and deploys.
The bucket's name will be of the format rain-artifacts-<AWS account id>-<AWS region>.

The config flag can be used to programmatically set tags and parameters.
The format is similar to the "Template configuration file" for AWS CodePipeline just without the
'StackPolicy' key. The file can be in YAML or JSON format.

JSON:
  {
    "Parameters" : {
      "NameOfTemplateParameter" : "ValueOfParameter",
      ...
    },
    "Tags" : {
      "TagKey" : "TagValue",
      ...
    }
  }

YAML:
  Parameters:
    NameOfTemplateParameter: ValueOfParameter
    ...
  Tags:
    TagKey: TagValue
    ...
`,
	Args:                  cobra.RangeArgs(1, 2),
	DisableFlagsInUseLine: true,
	Run: func(cmd *cobra.Command, args []string) {
		fn := args[0]
		base := filepath.Base(fn)

		var stackName string

		if len(args) == 2 {
			stackName = args[1]
		} else {
			stackName = base[:len(base)-len(filepath.Ext(base))]

			stackName = FixStackNameRe.ReplaceAllString(stackName, "-")

			if len(stackName) > MaxStackNameLength {
				stackName = stackName[:MaxStackNameLength]
			}
		}

		parsedTagFlag := ListToMap("tag", tags)

		parsedParamFlag := ListToMap("param", params)

		var combinedTags map[string]string
		var combinedParameters map[string]string

		if len(configFilePath) != 0 {
			configFileContent, err := os.ReadFile(configFilePath)
			if err != nil {
				panic(ui.Errorf(err, "unable to read config file '%s'", configFilePath))
			}

			var configFile configFileFormat
			err = yaml.Unmarshal([]byte(configFileContent), &configFile)
			if err != nil {
				panic(ui.Errorf(err, "unable to parse yaml in '%s'", configFilePath))
			}

			combinedTags = configFile.Tags
			combinedParameters = configFile.Parameters

			for k, v := range parsedTagFlag {
				if _, ok := combinedTags[k]; ok {
					fmt.Println(console.Yellow(fmt.Sprintf("tags flag overrides tag in config file: %s", k)))
				}
				combinedTags[k] = v
			}

			for k, v := range parsedParamFlag {
				if _, ok := combinedParameters[k]; ok {
					fmt.Println(console.Yellow(fmt.Sprintf("params flag overrides parameter in config file: %s", k)))
				}
				combinedParameters[k] = v
			}
		} else {
			combinedTags = parsedTagFlag
			combinedParameters = parsedParamFlag
		}

		spinner.Push(fmt.Sprintf("Preparing template '%s'", base))
		template := PackageTemplate(fn, yes)
		spinner.Pop()

		spinner.Push(fmt.Sprintf("Checking current status of stack '%s'", stackName))
		stack, stackExists := checkStack(stackName)
		spinner.Pop()

		config.Debugf("Handling parameters")
		parameters := GetParameters(template, combinedParameters, stack.Parameters, stackExists)

		if config.Debug {
			for _, param := range parameters {
				val := ptr.ToString(param.ParameterValue)
				if ptr.ToBool(param.UsePreviousValue) {
					val = "<previous value>"
				}
				config.Debugf("  %s: %s", ptr.ToString(param.ParameterKey), val)
			}
		}

		spinner.Push("Creating change set")
		changeSetName, createErr := cfn.CreateChangeSet(template, parameters, combinedTags, stackName, roleArn)
		if createErr != nil {
			if createErr.Error() == noChangeFoundMsg {
				spinner.Pop()
				fmt.Println(console.Green("Change set was created, but there is no change. Deploy was skipped."))
				return
			} else {
				panic(ui.Errorf(createErr, "error creating changeset"))
			}
		}
		spinner.Pop()

		if !yes {
			spinner.Push("Formatting change set")
			status := formatChangeSet(stackName, changeSetName)
			spinner.Pop()

			fmt.Println("CloudFormation will make the following changes:")
			fmt.Println(status)

			if !console.Confirm(true, "Do you wish to continue?") {
				err := cfn.DeleteChangeSet(stackName, changeSetName)
				if err != nil {
					panic(ui.Errorf(err, "error while deleting changeset '%s'", changeSetName))
				}

				if !stackExists {
					err = cfn.DeleteStack(stackName, "")
					if err != nil {
						panic(ui.Errorf(err, "error deleting empty stack '%s'", stackName))
					}
				}

				panic(errors.New("user cancelled deployment"))
			}
		}

		err := cfn.ExecuteChangeSet(stackName, changeSetName, keep)
		if err != nil {
			panic(ui.Errorf(err, "error while executing changeset '%s'", changeSetName))
		}

		if detach {
			fmt.Printf("Detaching. You can check your stack's status with: rain watch %s\n", stackName)
		} else {
			fmt.Printf("Deploying template '%s' as stack '%s' in %s.\n", filepath.Base(fn), stackName, aws.Config().Region)

			status, messages := ui.WaitForStackToSettle(stackName)
			stack, _ = cfn.GetStack(stackName)
			output := ui.GetStackSummary(stack, false)

			fmt.Println(output)

			if len(messages) > 0 {
				fmt.Println(console.Yellow("Messages:"))
				for _, message := range messages {
					fmt.Printf("  - %s\n", message)
				}
			}

			if status == "CREATE_COMPLETE" {
				fmt.Println(console.Green("Successfully deployed " + stackName))
			} else if status == "UPDATE_COMPLETE" {
				fmt.Println(console.Green("Successfully updated " + stackName))
			} else {
				panic(fmt.Errorf("failed deploying stack '%s'", stackName))
			}
		}

		if terminationProtection {
			err = cfn.SetTerminationProtection(stackName, true)
			if err != nil {
				panic(ui.Errorf(err, "error while enabling termination protection on stack '%s'", stackName))
			}
		}
	},
}

Cmd is the deploy command's entrypoint

View Source
var FixStackNameRe *regexp.Regexp

Functions

func GetParameters added in v1.3.0

func GetParameters(template cft.Template, combinedParameters map[string]string, old []types.Parameter, stackExists bool) []types.Parameter

func ListToMap added in v1.2.0

func ListToMap(name string, in []string) map[string]string

ListToMap converts a pflag parsed StringSlice into a map where values are expected to be presented in the form Foo=bar,Baz=quux,mooz,Xyzzy=garply

func PackageTemplate added in v1.3.0

func PackageTemplate(fn string, yes bool) cft.Template

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL