deploy

package
v1.20.0 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2024 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Cmd = &cobra.Command{
	Use:   "deploy <template> [stack]",
	Short: "Deploy a CloudFormation stack or changeset from a local template",
	Long: `Creates or updates a CloudFormation stack named <stack> from the template file <template>. 
You can also create and execute changesets with this command.
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
    ...

To create a changeset (with optional stackName and changeSetName):

rain deploy --no-exec <template> [stackName] [changeSetName]

To execute a changeset:

rain deploy --changeset <stackName> <changeSetName>

To list and delete changesets, use the ls and rm commands.
`,
	Args:                  cobra.RangeArgs(1, 3),
	DisableFlagsInUseLine: true,
	Run: func(cmd *cobra.Command, args []string) {

		var stackName, changeSetName, fn string
		var err error
		var stack types.Stack
		var templateNode *yaml.Node

		if changeset {

			if len(args) != 2 {
				panic("expected 2 args: rain deploy --changeset <stackName> <changeSetName>")
			}

			stackName = args[0]
			changeSetName = args[1]

		} else {

			fn = args[0]
			base := filepath.Base(fn)

			var suppliedStackName string

			if len(args) >= 2 {
				suppliedStackName = args[1]
			}

			if len(args) == 3 {
				changeSetName = args[2]
			}

			if experimental {
				cftpkg.Experimental = true
			}
			spinner.Push(fmt.Sprintf("Preparing template '%s'", base))
			template := PackageTemplate(fn, yes)
			templateNode = template.Node
			spinner.Pop()

			if HasRainMetadata(template) && !experimental {
				panic("metadata commands require the --experimental flag")
			}

			if !changeset {
				err := processMetadataBefore(cft.Template{Node: templateNode},
					stackName, filepath.Dir(fn))
				if err != nil {
					panic(err)
				}
			}

			stackName = dc.GetStackName(suppliedStackName, base)

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

			dc, err := dc.GetDeployConfig(tags, params, configFilePath, base,
				template, stack, stackExists, yes, ignoreUnknownParams)
			if err != nil {
				panic(err)
			}

			spinner.Push("Creating change set")
			var createErr error
			ctx := cfn.ChangeSetContext{
				Template:      template,
				Params:        dc.Params,
				Tags:          dc.Tags,
				StackName:     stackName,
				ChangeSetName: changeSetName,
				RoleArn:       roleArn,
				IncludeNested: includeNested,
			}
			config.Debugf("ChangeSetContext: %+v", ctx)
			changeSetName, createErr = cfn.CreateChangeSet(&ctx)
			if createErr != nil {
				if changeSetHasNoChanges(createErr.Error()) {
					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 noexec {
				spinner.Push("Formatting change set")
				status := formatChangeSet(stackName, changeSetName)
				spinner.Pop()

				fmt.Println("Changeset contains the following changes:")
				fmt.Println(status)

				fmt.Println("changeset created but not executed:", changeSetName)
				return
			}

			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 {
			if changeset {
				fmt.Printf("Executing changeset '%s' as stack '%s' in %s.\n",
					changeSetName, stackName, aws.Config().Region)
			} else {
				fmt.Printf("Deploying template '%s' as stack '%s' in %s.\n",
					filepath.Base(fn), stackName, aws.Config().Region)
			}
			status, messages := cfn.WaitForStackToSettle(stackName)
			stack, _ = cfn.GetStack(stackName)
			output := cfn.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))
			}
		}

		if !changeset {
			err := processMetadataAfter(cft.Template{Node: templateNode},
				stackName, filepath.Dir(fn))
			if err != nil {
				panic(err)
			}
		}
	},
	PostRun: func(cmd *cobra.Command, args []string) {
		params = nil
	},
}

Cmd is the deploy command's entrypoint

Functions

func CheckStack added in v1.4.0

func CheckStack(stackName string) (types.Stack, bool)

func HasRainMetadata added in v1.16.0

func HasRainMetadata(template cft.Template) bool

hasRainMetadata returns true if the template has a resource with a Metadata section with a Rain node

func PackageTemplate added in v1.3.0

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

func Run added in v1.16.0

func Run(n *yaml.Node, key string, stackName string, rootDir string) error

Run checks to ses if either RunBefore or RunAfter is defined in the Rain metadata sections and runs and external command, like a build script. Args to the command can be literal strings or stack output lookups, if this is "RunAfter".

Example:

Metadata:
  Rain:
    Content: site/dist
    RunBefore:
      Command: buildsite.sh
    RunAfter: buildsite.sh
      Command: buildsite.sh
      Args:
        - Rain::OutputValue RestApiInvokeURL
        - Rain::OutputValue RedirectURI
        - Rain::OutputValue AppName
        - Rain::OutputValue AppClientId

Types

This section is empty.

Jump to

Keyboard shortcuts

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