From e686712df8a86aedb2bf6e14352dc2e9686473d0 Mon Sep 17 00:00:00 2001 From: Taras Postument Date: Tue, 30 Jan 2018 23:11:34 +0200 Subject: [PATCH 1/2] Issue #157. implemet CloudFormation RollbackTriggers. --- aws/doc/paramsdoc.go | 28 +++++++----- aws/spec/setters.go | 3 ++ aws/spec/stack.go | 105 ++++++++++++++++++++++++++++--------------- 3 files changed, 87 insertions(+), 49 deletions(-) diff --git a/aws/doc/paramsdoc.go b/aws/doc/paramsdoc.go index f633121b0..faccc660c 100644 --- a/aws/doc/paramsdoc.go +++ b/aws/doc/paramsdoc.go @@ -292,12 +292,14 @@ var manualParamsDoc = map[string]map[string]string{ "adjustment-scaling": "The amount by which to scale, based on the specified adjustment type (e.g. '-2', '3')", }, "create.stack": { - "capabilities": "A list of values that you must specify before AWS CloudFormation can create certain stacks", - "on-failure": "Determines what action will be taken if stack creation fails", - "parameters": "A list of Parameters that specify input parameters for the stack given using this format: [key1:val1,key2:val2,...]", - "policy-file": "The path to the file containing the stack policy body", - "template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes", - "stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile", + "capabilities": "A list of values that you must specify before AWS CloudFormation can create certain stacks", + "on-failure": "Determines what action will be taken if stack creation fails", + "parameters": "A list of Parameters that specify input parameters for the stack given using this format: [key1:val1,key2:val2,...]", + "policy-file": "The path to the file containing the stack policy body", + "template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes", + "stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile", + "rollback-triggers": "List of ClodwatchAlarm Arns to monitor during and after creation", + "rollback-monitoring-min": "Time to monitor rollback-triggers during and after creation", }, "create.subnet": { "name": "The 'Name' Tag for the subnet to create", @@ -531,12 +533,14 @@ var manualParamsDoc = map[string]map[string]string{ "portrange": "The portrange for the rule to update: any, 80, 22-23...", }, "update.stack": { - "capabilities": "A list of values that you must specify before AWS CloudFormation can update certain stacks", - "parameters": "A list of Parameters that specify input parameters for the stack given using this format: [key1:val1,key2:val2,...]", - "policy-file": "The path to the file containing the stack policy body", - "policy-update-file": "The path to the file containing the temporary overriding stack policy", - "template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes", - "stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile", + "capabilities": "A list of values that you must specify before AWS CloudFormation can update certain stacks", + "parameters": "A list of Parameters that specify input parameters for the stack given using this format: [key1:val1,key2:val2,...]", + "policy-file": "The path to the file containing the stack policy body", + "policy-update-file": "The path to the file containing the temporary overriding stack policy", + "template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes", + "stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile", + "rollback-triggers": "List of ClodwatchAlarm Arns to monitor during and after update", + "rollback-monitoring-min": "Time to monitor rollback-triggers during and after update", }, "update.targetgroup": { "id": "The Amazon Resource Name (ARN) of the target group", diff --git a/aws/spec/setters.go b/aws/spec/setters.go index d6181b7f3..2c4596838 100644 --- a/aws/spec/setters.go +++ b/aws/spec/setters.go @@ -68,6 +68,7 @@ const ( aws6digitsstring = "aws6digitsstring" awsbyteslice = "awsbyteslice" awstagslice = "awstagslice" + awsrollbackconfig = "awsrollbackconfig" ) var ( @@ -359,6 +360,8 @@ func setFieldWithType(v, i interface{}, fieldPath string, destType string, inter } v = tags + case awsrollbackconfig: + // do nothing since we did all job in BeforeRun } awsutil.SetValueAtPath(i, fieldPath, v) return nil diff --git a/aws/spec/stack.go b/aws/spec/stack.go index be8a93047..7fbbc86bf 100644 --- a/aws/spec/stack.go +++ b/aws/spec/stack.go @@ -27,6 +27,7 @@ import ( "github.com/wallix/awless/template/env" "github.com/wallix/awless/template/params" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudformation" "github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface" "github.com/wallix/awless/logger" @@ -34,29 +35,32 @@ import ( ) type CreateStack struct { - _ string `action:"create" entity:"stack" awsAPI:"cloudformation" awsCall:"CreateStack" awsInput:"cloudformation.CreateStackInput" awsOutput:"cloudformation.CreateStackOutput"` - logger *logger.Logger - graph cloud.GraphAPI - api cloudformationiface.CloudFormationAPI - Name *string `awsName:"StackName" awsType:"awsstr" templateName:"name"` - TemplateFile *string `awsName:"TemplateBody" awsType:"awsfiletostring" templateName:"template-file"` - Capabilities []*string `awsName:"Capabilities" awsType:"awsstringslice" templateName:"capabilities"` - DisableRollback *bool `awsName:"DisableRollback" awsType:"awsbool" templateName:"disable-rollback"` - Notifications []*string `awsName:"NotificationARNs" awsType:"awsstringslice" templateName:"notifications"` - OnFailure *string `awsName:"OnFailure" awsType:"awsstr" templateName:"on-failure"` - Parameters []*string `awsName:"Parameters" awsType:"awsparameterslice" templateName:"parameters"` - ResourceTypes []*string `awsName:"ResourceTypes" awsType:"awsstringslice" templateName:"resource-types"` - Role *string `awsName:"RoleARN" awsType:"awsstr" templateName:"role"` - PolicyFile *string `awsName:"StackPolicyBody" awsType:"awsfiletostring" templateName:"policy-file"` - Timeout *int64 `awsName:"TimeoutInMinutes" awsType:"awsint64" templateName:"timeout"` - Tags []*string `awsName:"Tags" awsType:"awstagslice" templateName:"tags"` - PolicyBody *string `awsName:"StackPolicyBody" awsType:"awsstr"` - StackFile *string `templateName:"stack-file"` + _ string `action:"create" entity:"stack" awsAPI:"cloudformation" awsCall:"CreateStack" awsInput:"cloudformation.CreateStackInput" awsOutput:"cloudformation.CreateStackOutput"` + logger *logger.Logger + graph cloud.GraphAPI + api cloudformationiface.CloudFormationAPI + Name *string `awsName:"StackName" awsType:"awsstr" templateName:"name"` + TemplateFile *string `awsName:"TemplateBody" awsType:"awsfiletostring" templateName:"template-file"` + Capabilities []*string `awsName:"Capabilities" awsType:"awsstringslice" templateName:"capabilities"` + DisableRollback *bool `awsName:"DisableRollback" awsType:"awsbool" templateName:"disable-rollback"` + Notifications []*string `awsName:"NotificationARNs" awsType:"awsstringslice" templateName:"notifications"` + OnFailure *string `awsName:"OnFailure" awsType:"awsstr" templateName:"on-failure"` + Parameters []*string `awsName:"Parameters" awsType:"awsparameterslice" templateName:"parameters"` + ResourceTypes []*string `awsName:"ResourceTypes" awsType:"awsstringslice" templateName:"resource-types"` + Role *string `awsName:"RoleARN" awsType:"awsstr" templateName:"role"` + PolicyFile *string `awsName:"StackPolicyBody" awsType:"awsfiletostring" templateName:"policy-file"` + Timeout *int64 `awsName:"TimeoutInMinutes" awsType:"awsint64" templateName:"timeout"` + Tags []*string `awsName:"Tags" awsType:"awstagslice" templateName:"tags"` + PolicyBody *string `awsName:"StackPolicyBody" awsType:"awsstr"` + StackFile *string `templateName:"stack-file"` + RollbackConfigurations *cloudformation.RollbackConfiguration `awsName:"RollbackConfiguration" awsType:"awsrollbackconfig"` + RollbackTriggers []*string `templateName:"rollback-triggers"` + RollbackMonitoringMin *int64 `templateName:"rollback-monitoring-min"` } func (cmd *CreateStack) ParamsSpec() params.Spec { return params.NewSpec( - params.AllOf(params.Key("name"), params.Key("template-file"), params.Opt("capabilities", "disable-rollback", "notifications", "on-failure", "parameters", "policy-file", "resource-types", "role", "stack-file", "tags", "timeout")), + params.AllOf(params.Key("name"), params.Key("template-file"), params.Opt("capabilities", "disable-rollback", "notifications", "on-failure", "parameters", "policy-file", "resource-types", "role", "stack-file", "tags", "timeout", "rollback-triggers", "rollback-monitoring-min")), params.Validators{"template-file": params.IsFilepath}, ) } @@ -71,32 +75,38 @@ func (cmd *CreateStack) ExtractResult(i interface{}) string { func (cmd *CreateStack) BeforeRun(renv env.Running) error { var err error cmd.Parameters, cmd.Tags, cmd.PolicyBody, err = processStackFile(cmd.StackFile, cmd.PolicyFile, cmd.Parameters, cmd.Tags) + if len(cmd.RollbackTriggers) > 0 || cmd.RollbackMonitoringMin != nil { + cmd.RollbackConfigurations = newRollBackConfig(cmd.RollbackTriggers, cmd.RollbackMonitoringMin) + } return err } type UpdateStack struct { - _ string `action:"update" entity:"stack" awsAPI:"cloudformation" awsCall:"UpdateStack" awsInput:"cloudformation.UpdateStackInput" awsOutput:"cloudformation.UpdateStackOutput"` - logger *logger.Logger - graph cloud.GraphAPI - api cloudformationiface.CloudFormationAPI - Name *string `awsName:"StackName" awsType:"awsstr" templateName:"name"` - Capabilities []*string `awsName:"Capabilities" awsType:"awsstringslice" templateName:"capabilities"` - Notifications []*string `awsName:"NotificationARNs" awsType:"awsstringslice" templateName:"notifications"` - Parameters []*string `awsName:"Parameters" awsType:"awsparameterslice" templateName:"parameters"` - ResourceTypes []*string `awsName:"ResourceTypes" awsType:"awsstringslice" templateName:"resource-types"` - Role *string `awsName:"RoleARN" awsType:"awsstr" templateName:"role"` - PolicyFile *string `awsName:"StackPolicyBody" awsType:"awsfiletostring" templateName:"policy-file"` - PolicyUpdateFile *string `awsName:"StackPolicyDuringUpdateBody" awsType:"awsfiletostring" templateName:"policy-update-file"` - TemplateFile *string `awsName:"TemplateBody" awsType:"awsfiletostring" templateName:"template-file"` - UsePreviousTemplate *bool `awsName:"UsePreviousTemplate" awsType:"awsbool" templateName:"use-previous-template"` - Tags []*string `awsName:"Tags" awsType:"awstagslice" templateName:"tags"` - PolicyBody *string `awsName:"StackPolicyBody" awsType:"awsstr"` - StackFile *string `templateName:"stack-file"` + _ string `action:"update" entity:"stack" awsAPI:"cloudformation" awsCall:"UpdateStack" awsInput:"cloudformation.UpdateStackInput" awsOutput:"cloudformation.UpdateStackOutput"` + logger *logger.Logger + graph cloud.GraphAPI + api cloudformationiface.CloudFormationAPI + Name *string `awsName:"StackName" awsType:"awsstr" templateName:"name"` + Capabilities []*string `awsName:"Capabilities" awsType:"awsstringslice" templateName:"capabilities"` + Notifications []*string `awsName:"NotificationARNs" awsType:"awsstringslice" templateName:"notifications"` + Parameters []*string `awsName:"Parameters" awsType:"awsparameterslice" templateName:"parameters"` + ResourceTypes []*string `awsName:"ResourceTypes" awsType:"awsstringslice" templateName:"resource-types"` + Role *string `awsName:"RoleARN" awsType:"awsstr" templateName:"role"` + PolicyFile *string `awsName:"StackPolicyBody" awsType:"awsfiletostring" templateName:"policy-file"` + PolicyUpdateFile *string `awsName:"StackPolicyDuringUpdateBody" awsType:"awsfiletostring" templateName:"policy-update-file"` + TemplateFile *string `awsName:"TemplateBody" awsType:"awsfiletostring" templateName:"template-file"` + UsePreviousTemplate *bool `awsName:"UsePreviousTemplate" awsType:"awsbool" templateName:"use-previous-template"` + Tags []*string `awsName:"Tags" awsType:"awstagslice" templateName:"tags"` + PolicyBody *string `awsName:"StackPolicyBody" awsType:"awsstr"` + StackFile *string `templateName:"stack-file"` + RollbackConfigurations *cloudformation.RollbackConfiguration `awsName:"RollbackConfiguration" awsType:"awsrollbackconfig"` + RollbackTriggers []*string `templateName:"rollback-triggers"` + RollbackMonitoringMin *int64 `templateName:"rollback-monitoring-min"` } func (cmd *UpdateStack) ParamsSpec() params.Spec { return params.NewSpec(params.AllOf(params.Key("name"), - params.Opt("capabilities", "notifications", "parameters", "policy-file", "policy-update-file", "resource-types", "role", "stack-file", "tags", "template-file", "use-previous-template"), + params.Opt("capabilities", "notifications", "parameters", "policy-file", "policy-update-file", "resource-types", "role", "stack-file", "tags", "template-file", "use-previous-template", "rollback-triggers", "rollback-monitoring-min"), )) } @@ -110,9 +120,30 @@ func (cmd *UpdateStack) ExtractResult(i interface{}) string { func (cmd *UpdateStack) BeforeRun(renv env.Running) error { var err error cmd.Parameters, cmd.Tags, cmd.PolicyBody, err = processStackFile(cmd.StackFile, cmd.PolicyFile, cmd.Parameters, cmd.Tags) + if len(cmd.RollbackTriggers) > 0 || cmd.RollbackMonitoringMin != nil { + cmd.RollbackConfigurations = newRollBackConfig(cmd.RollbackTriggers, cmd.RollbackMonitoringMin) + } + return err } +func newRollBackConfig(rollbackTriggers []*string, monitoringTime *int64) *cloudformation.RollbackConfiguration { + rc := &cloudformation.RollbackConfiguration{ + MonitoringTimeInMinutes: monitoringTime, + } + + if len(rollbackTriggers) > 0 { + for _, rt := range rollbackTriggers { + rc.RollbackTriggers = append(rc.RollbackTriggers, &cloudformation.RollbackTrigger{ + Arn: rt, + Type: aws.String("AWS::CloudWatch::Alarm"), + }) + } + } + + return rc +} + type stackFile struct { Parameters map[string]string `yaml:"Parameters"` Tags map[string]string `yaml:"Tags"` From 8f5214dc76b65390c988c54dd646193de75e4d5c Mon Sep 17 00:00:00 2001 From: Taras Postument Date: Thu, 1 Feb 2018 22:01:25 +0200 Subject: [PATCH 2/2] fix the typo --- aws/doc/paramsdoc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/doc/paramsdoc.go b/aws/doc/paramsdoc.go index faccc660c..6a344f68a 100644 --- a/aws/doc/paramsdoc.go +++ b/aws/doc/paramsdoc.go @@ -298,7 +298,7 @@ var manualParamsDoc = map[string]map[string]string{ "policy-file": "The path to the file containing the stack policy body", "template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes", "stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile", - "rollback-triggers": "List of ClodwatchAlarm Arns to monitor during and after creation", + "rollback-triggers": "List of CloudWatch Alarm ARNs to monitor during and after creation", "rollback-monitoring-min": "Time to monitor rollback-triggers during and after creation", }, "create.subnet": { @@ -539,7 +539,7 @@ var manualParamsDoc = map[string]map[string]string{ "policy-update-file": "The path to the file containing the temporary overriding stack policy", "template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes", "stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile", - "rollback-triggers": "List of ClodwatchAlarm Arns to monitor during and after update", + "rollback-triggers": "List of CloudWatch Alarm ARNs to monitor during and after update", "rollback-monitoring-min": "Time to monitor rollback-triggers during and after update", }, "update.targetgroup": {