`up` can update an existing stack using CloudFormation Changeset

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-07-16 16:07:26 +02:00
parent efeded2670
commit 2d931dab9d
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
4 changed files with 78 additions and 13 deletions

View File

@ -26,14 +26,6 @@ func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error {
return err
}
update, err := b.api.StackExists(ctx, project.Name)
if err != nil {
return err
}
if update {
return fmt.Errorf("we do not (yet) support updating an existing CloudFormation stack")
}
template, err := b.Convert(project)
if err != nil {
return err
@ -62,17 +54,34 @@ func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error {
ParameterLoadBalancerARN: lb,
}
err = b.api.CreateStack(ctx, project.Name, template, parameters)
update, err := b.api.StackExists(ctx, project.Name)
if err != nil {
return err
}
operation := compose.StackCreate
if update {
operation = compose.StackUpdate
changeset, err := b.api.CreateChangeSet(ctx, project.Name, template, parameters)
if err != nil {
return err
}
err = b.api.UpdateStack(ctx, changeset)
if err != nil {
return err
}
} else {
err = b.api.CreateStack(ctx, project.Name, template, parameters)
if err != nil {
return err
}
}
fmt.Println()
w := console.NewProgressWriter()
for k := range template.Resources {
w.ResourceEvent(k, "PENDING", "")
}
return b.WaitStackCompletion(ctx, project.Name, compose.StackCreate, w)
return b.WaitStackCompletion(ctx, project.Name, operation, w)
}
func (b Backend) GetVPC(ctx context.Context, project *types.Project) (string, error) {

View File

@ -23,6 +23,8 @@ type API interface {
GetStackID(ctx context.Context, name string) (string, error)
WaitStackComplete(ctx context.Context, name string, operation int) error
DescribeStackEvents(ctx context.Context, stackID string) ([]*cf.StackEvent, error)
CreateChangeSet(ctx context.Context, name string, template *cloudformation.Template, parameters map[string]string) (string, error)
UpdateStack(ctx context.Context, changeset string) error
DescribeServices(ctx context.Context, cluster string, arns []string) ([]compose.ServiceStatus, error)

View File

@ -175,9 +175,8 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
param := []*cloudformation.Parameter{}
for name, value := range parameters {
param = append(param, &cloudformation.Parameter{
ParameterKey: aws.String(name),
ParameterValue: aws.String(value),
UsePreviousValue: aws.Bool(true),
ParameterKey: aws.String(name),
ParameterValue: aws.String(value),
})
}
@ -194,6 +193,60 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
return err
}
func (s sdk) CreateChangeSet(ctx context.Context, name string, template *cf.Template, parameters map[string]string) (string, error) {
logrus.Debug("Create CloudFormation Changeset")
json, err := template.JSON()
if err != nil {
return "", err
}
param := []*cloudformation.Parameter{}
for name := range parameters {
param = append(param, &cloudformation.Parameter{
ParameterKey: aws.String(name),
UsePreviousValue: aws.Bool(true),
})
}
update := fmt.Sprintf("Update%s", time.Now().Format("2006-01-02-15-04-05"))
changeset, err := s.CF.CreateChangeSetWithContext(ctx, &cloudformation.CreateChangeSetInput{
ChangeSetName: aws.String(update),
ChangeSetType: aws.String(cloudformation.ChangeSetTypeUpdate),
StackName: aws.String(name),
TemplateBody: aws.String(string(json)),
Parameters: param,
Capabilities: []*string{
aws.String(cloudformation.CapabilityCapabilityIam),
},
})
if err != nil {
return "", err
}
err = s.CF.WaitUntilChangeSetCreateCompleteWithContext(ctx, &cloudformation.DescribeChangeSetInput{
ChangeSetName: changeset.Id,
})
return *changeset.Id, err
}
func (s sdk) UpdateStack(ctx context.Context, changeset string) error {
desc, err := s.CF.DescribeChangeSetWithContext(ctx, &cloudformation.DescribeChangeSetInput{
ChangeSetName: aws.String(changeset),
})
if err != nil {
return err
}
if strings.HasPrefix(aws.StringValue(desc.StatusReason), "The submitted information didn't contain changes.") {
return nil
}
_, err = s.CF.ExecuteChangeSet(&cloudformation.ExecuteChangeSetInput{
ChangeSetName: aws.String(changeset),
})
return err
}
func (s sdk) WaitStackComplete(ctx context.Context, name string, operation int) error {
input := &cloudformation.DescribeStacksInput{
StackName: aws.String(name),

View File

@ -19,6 +19,7 @@ type ServiceStatus struct {
const (
StackCreate = iota
StackUpdate
StackDelete
)