forecast

package
v1.20.2 Latest Latest
Warning

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

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

README

Rain Forecast

The experimental rain forecast command makes API calls into your account to try to predict things that might fail during stack create, update, and delete operations. This command is not meant to be a substitute for the CloudFormation Linter (cfn-lint), which ideally is already an integral part of your development process.

In order to use this command, supply the -x argument to recognize the fact that this feature is currently experimental could change with minor version upgrades.

rain forecast -x my-template.yaml my-stack-name 

You can also supply a CLI profile with the --profile argument to assume a different role for the checks you make against the template.

Generic checks

This command currently makes a few generic checks for a wide range of resources:

  • FG001: The resource already exists (for stack creation with hard coded resource names)
  • FG002: IAM permissions to interact with the resource. Keep in mind that this is a slow operation and is disabled by default. You can enable it with the --include-iam argument. It is also not guaranteed to be 100% accurate, due to the difficulty with predicting the exact ARNs for all possible resources that are involved with the resource provider.

Specific checks

These can be ignored with the --ignore argument.

Code Description
F0001 For a delete operation, the S3 bucket is not empty
F0002 S3 bucket policy has an invalid principal
F0003 RDS cluster configuration is correct for the chosen engine
F0004 RDS monitoring role arn is correct
F0005 RDS cluster quota is not at limit
F0006 RDS instance configuration is correct for the chosen engine
F0007 EC2 instance and launch template KeyName exists
F0008 EC2 instance and launch template InstanceType exists
F0009 EC2 instance and launch template instance type and AMI match
F0010 Within the same template, are all security groups pointing to the same network
F0011 If there is no default VPC, does each security group have a vpc configured?
F0012 Certificate not found for elastic load balancer
F0013 SNS Topic Key is valid
F0014 ELB target group Port and Protocol match
F0015 ELB target groups must be of type instance if they are used by an ASG
F0016 Lambda function role exists
F0017 Lambda function role can be assumed
F0018 SageMaker Notebook quota limit has not been reached
F0019 Lambda S3Bucket exists
F0020 Lambda S3Key exists
F0021 Lambda zip file has a valid size

Estimates

The forecast command also tries to estimate how long it thinks your stack will take to deploy.

Plugins

You can build a plugin that runs prediction functions that you write yourself. This can be useful if you have internal systems that you want to check to make sure the template is valid before deployment. You can see an example plugin in cmd/sample_plugin/main.go.

package main

import (
	fc "github.com/aws-cloudformation/rain/plugins/forecast"
)

type PluginImpl struct{}

// A sample prediction function.
func predictLambda(input fc.PredictionInput) fc.Forecast {
	forecast := fc.MakeForecast(&input)

	// Implement whatever checks you want to make here
	forecast.Add("CODE", false, "testing plugin", 0)

	return forecast
}

// GetForecasters must be implemented by forecast plugins
// This function returns all predictions functions implemented by the plugin
func (p *PluginImpl) GetForecasters() map[string]func(input fc.PredictionInput) fc.Forecast {
	retval := make(map[string]func(input fc.PredictionInput) fc.Forecast)

	retval["AWS::Lambda::Function"] = predictLambda

	return retval
}

// Leave main empty
func main() {
}

// This variable is required to allow rain to find the implementation
var Plugin = PluginImpl{}

Roadmap

You can view the issues list for the forecast command here.

Please feel free to create an issue here whenever you get a stack failure that you think could have been prevented by one of these checks.

Checks we plan to implement:

  • DynamoDB global table replica region requirements
  • Prefix list does not exist
  • Flow Log format errors
  • SES identity not verified
  • SES sending pool does not exist
  • EIP limit
  • Function version does not exist
  • Warn on resource replacements for active traffic
  • API gateway account trust permission

Documentation

Overview

Package forecast looks at your account and tries to predict things that will go wrong when you attempt to CREATE, UPDATE, or DELETE a stack

Index

Constants

View Source
const (
	FG001 = "FG001"
	FG002 = "FG002"
	F0001 = "F0001"
	F0002 = "F0002"
	F0003 = "F0003"
	F0004 = "F0004"
	F0005 = "F0005"
	F0006 = "F0006"
	F0007 = "F0007"
	F0008 = "F0008"
	F0009 = "F0009"
	F0010 = "F0010"
	F0011 = "F0011"
	F0012 = "F0012"
	F0013 = "F0013"
	F0014 = "F0014"
	F0015 = "F0015"
	F0016 = "F0016"
	F0017 = "F0017"
	F0018 = "F0018"
	F0019 = "F0019"
	F0020 = "F0020"
	F0021 = "F0021"
	F0022 = "F0022"
)
View Source
const (
	ALL    = "all"
	CREATE = "create"
	UPDATE = "update"
	DELETE = "delete"
)

Variables

View Source
var Cmd = &cobra.Command{
	Use:   "forecast --experimental <template> [stackName]",
	Short: "Predict deployment failures",
	Long: `Outputs warnings about potential deployment failures due to constraints in 
the account or misconfigurations in the template related to dependencies in 
the account.

NOTE: This is an experimental feature!

To use this command, add --experimental or -x as an argument.

This command is not a linter! Use cfn-lint for that. The forecast command 
is concerned with things that could go wrong during deployment, after the 
template has been checked to make sure it has a valid syntax.

This command checks for some common issues across all resources, and 
resource-specific checks. See the README for more details.
`,
	Args:                  cobra.RangeArgs(1, 2),
	DisableFlagsInUseLine: true,
	Run: func(cmd *cobra.Command, args []string) {
		fn := args[0]
		base := filepath.Base(fn)
		var suppliedStackName string

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

		if !Experimental {
			panic("Please add the --experimental arg to use this feature")
		}
		pkg.Experimental = Experimental

		config.Debugf("Generating forecast for %v", fn)

		tForLines, err := parse.File(fn)
		if err != nil {
			panic(err)
		}
		resources, err := tForLines.GetSection(cft.Resources)
		if err != nil {
			panic(err)
		}
		for i := 0; i < len(resources.Content); i += 2 {
			logicalId := resources.Content[i].Value
			lineNum := resources.Content[i].Line
			lineNums[logicalId] = lineNum
		}

		source, err := pkg.File(fn)
		if err != nil {
			panic(err)
		}

		content := format.CftToYaml(source)
		source, err = parse.String(content)
		if err != nil {
			panic(err)
		}

		stackName := dc.GetStackName(suppliedStackName, base)

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

		msg := ""
		if stackExists {
			msg = "exists"
		} else {
			msg = "does not exist"
		}
		config.Debugf("Stack %v %v", stackName, msg)

		if action == DELETE || action == UPDATE && !stackExists {
			panic(fmt.Sprintf("stack %v does not exist, action %s is not valid", stackName, action))
		}

		if action == CREATE && stackExists {
			panic(fmt.Sprintf("stack %v already exists, action %s is not valid", stackName, action))
		}

		dc, err := dc.GetDeployConfig(tags, params, configFilePath, base,
			source, stack, stackExists, true, false)
		if err != nil {
			panic(err)
		}

		if pluginPath != "" {
			config.Debugf("pluginPath: %s", pluginPath)
			plg, err := plugin.Open(pluginPath)
			if err != nil {
				panic(err)
			}
			p, err := plg.Lookup("Plugin")
			if err != nil {
				panic(err)
			}
			forecastPlugin, ok := p.(fc.ForecastPlugin)
			if !ok {
				panic("Could not cast to ForecastPlugin")
			}

			pluginForecasters = forecastPlugin.GetForecasters()
		}

		if !Predict(source, stackName, stack, stackExists, dc) {
			os.Exit(1)
		}

	},
}

Cmd is the forecast command's entrypoint

View Source
var Estimates map[string]ResourceEstimate

Estimates is a map of resource type name to ResourceEstimates, which are based on historical averages

View Source
var Experimental bool

Experimental indicates that this is an experimental feature that might break between minor releases

View Source
var IncludeIAM bool

IncludeIAM indicates if we should perform permissions checks or not, to save time

View Source
var ResourceType string

ResourceType is the resource type to check (optional --type to limit checks to one type)

View Source
var RoleArn string

RoleArn is the role name to use for the IAM policy simulator (optional --role)

Functions

func CheckAutoScalingLaunchConfiguration added in v1.14.0

func CheckAutoScalingLaunchConfiguration(input fc.PredictionInput) fc.Forecast

func CheckEC2Instance added in v1.14.0

func CheckEC2Instance(input fc.PredictionInput) fc.Forecast

func CheckEC2LaunchTemplate added in v1.14.0

func CheckEC2LaunchTemplate(input fc.PredictionInput) fc.Forecast

func CheckEC2SecurityGroup added in v1.14.0

func CheckEC2SecurityGroup(input fc.PredictionInput) fc.Forecast

func CheckELBListener added in v1.14.0

func CheckELBListener(input fc.PredictionInput) fc.Forecast

func CheckELBTargetGroup added in v1.14.0

func CheckELBTargetGroup(input fc.PredictionInput) fc.Forecast

func CheckLambdaFunction added in v1.14.0

func CheckLambdaFunction(input fc.PredictionInput) fc.Forecast

checkLambdaFunction checks for potential stack failures related to functions

func CheckRDSDBCluster added in v1.14.0

func CheckRDSDBCluster(input fc.PredictionInput) fc.Forecast

Checks configuration issues with RDS clusters

func CheckS3Bucket added in v1.14.0

func CheckS3Bucket(input fc.PredictionInput) fc.Forecast

Check everything that could go wrong with an AWS::S3::Bucket resource

func CheckS3BucketPolicy added in v1.14.0

func CheckS3BucketPolicy(input fc.PredictionInput) fc.Forecast

Check everything that could go wrong with an AWS::S3::Bucket resource. Returns numFailed, numChecked

func CheckSNSTopic added in v1.14.0

func CheckSNSTopic(input fc.PredictionInput) fc.Forecast

func CheckSageMakerNotebook added in v1.14.0

func CheckSageMakerNotebook(input fc.PredictionInput) fc.Forecast

func FormatEstimate added in v1.7.0

func FormatEstimate(total int) string

FormatEstimate returns a string in human readable format to represent the number of seconds. For example, 61 would return "0h, 1m, 1s"

func GetNode added in v1.13.1

func GetNode(prop *yaml.Node, name string) *yaml.Node

GetNode is a simplified version of s11n.GetMapValue that returns the value only

func GetResourceEstimate added in v1.7.0

func GetResourceEstimate(resourceType string, action StackAction) (int, error)

GetResourceEstimate returns the estimated time an action will take for the given resource type

func InitEstimates added in v1.7.0

func InitEstimates()

init initializes the Estimates map for all AWS resource types

func Predict added in v1.14.0

func Predict(source cft.Template, stackName string, stack types.Stack, stackExists bool, dc *deployconfig.DeployConfig) bool

Query the account to make predictions about deployment failures. Returns true if no failures are predicted.

func PredictTotalEstimate added in v1.7.0

func PredictTotalEstimate(t cft.Template, stackExists bool) int

PredictTotalEstimate returns the total number of seconds expected to deploy the stack. This function takes into account resources that will be deployed in parallel.

Types

type NotebookCode added in v1.13.1

type NotebookCode struct {
	Code          string
	Name          string
	InstanceType  string
	InstanceCount int
}

func ParseNotebookCodes added in v1.13.1

func ParseNotebookCodes(jsonData string) ([]NotebookCode, error)

type ResourceEstimate added in v1.7.0

type ResourceEstimate struct {
	Name   string
	Create int
	Update int
	Delete int
}

ResourceEstimate stores the estimated time, in seconds, to create, update, or delete a specific resource type. The Name is something like "AWS::S3::Bucket"

func NewResourceEstimate added in v1.7.0

func NewResourceEstimate(name string, create int, update int, del int) ResourceEstimate

NewResourceEstimate creates a new instance of ResourceEstimate

type StackAction added in v1.7.0

type StackAction string
const (
	Create StackAction = "create"
	Update StackAction = "update"
	Delete StackAction = "delete"
)

Jump to

Keyboard shortcuts

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