Skip to content
This repository was archived by the owner on Oct 9, 2020. It is now read-only.

Commit 6629d8e

Browse files
committed
up can update an existing stack using CloudFormation Changeset
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
1 parent 2afe344 commit 6629d8e

File tree

4 files changed

+78
-13
lines changed

4 files changed

+78
-13
lines changed

pkg/amazon/backend/up.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@ func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error {
2626
return err
2727
}
2828

29-
update, err := b.api.StackExists(ctx, project.Name)
30-
if err != nil {
31-
return err
32-
}
33-
if update {
34-
return fmt.Errorf("we do not (yet) support updating an existing CloudFormation stack")
35-
}
36-
3729
template, err := b.Convert(project)
3830
if err != nil {
3931
return err
@@ -62,17 +54,34 @@ func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error {
6254
ParameterLoadBalancerARN: lb,
6355
}
6456

65-
err = b.api.CreateStack(ctx, project.Name, template, parameters)
57+
update, err := b.api.StackExists(ctx, project.Name)
6658
if err != nil {
6759
return err
6860
}
61+
operation := compose.StackCreate
62+
if update {
63+
operation = compose.StackUpdate
64+
changeset, err := b.api.CreateChangeSet(ctx, project.Name, template, parameters)
65+
if err != nil {
66+
return err
67+
}
68+
err = b.api.UpdateStack(ctx, changeset)
69+
if err != nil {
70+
return err
71+
}
72+
} else {
73+
err = b.api.CreateStack(ctx, project.Name, template, parameters)
74+
if err != nil {
75+
return err
76+
}
77+
}
6978

7079
fmt.Println()
7180
w := console.NewProgressWriter()
7281
for k := range template.Resources {
7382
w.ResourceEvent(k, "PENDING", "")
7483
}
75-
return b.WaitStackCompletion(ctx, project.Name, compose.StackCreate, w)
84+
return b.WaitStackCompletion(ctx, project.Name, operation, w)
7685
}
7786

7887
func (b Backend) GetVPC(ctx context.Context, project *types.Project) (string, error) {

pkg/amazon/sdk/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type API interface {
2323
GetStackID(ctx context.Context, name string) (string, error)
2424
WaitStackComplete(ctx context.Context, name string, operation int) error
2525
DescribeStackEvents(ctx context.Context, stackID string) ([]*cf.StackEvent, error)
26+
CreateChangeSet(ctx context.Context, name string, template *cloudformation.Template, parameters map[string]string) (string, error)
27+
UpdateStack(ctx context.Context, changeset string) error
2628

2729
DescribeServices(ctx context.Context, cluster string, arns []string) ([]compose.ServiceStatus, error)
2830

pkg/amazon/sdk/sdk.go

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,8 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
175175
param := []*cloudformation.Parameter{}
176176
for name, value := range parameters {
177177
param = append(param, &cloudformation.Parameter{
178-
ParameterKey: aws.String(name),
179-
ParameterValue: aws.String(value),
180-
UsePreviousValue: aws.Bool(true),
178+
ParameterKey: aws.String(name),
179+
ParameterValue: aws.String(value),
181180
})
182181
}
183182

@@ -194,6 +193,60 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
194193
return err
195194
}
196195

196+
func (s sdk) CreateChangeSet(ctx context.Context, name string, template *cf.Template, parameters map[string]string) (string, error) {
197+
logrus.Debug("Create CloudFormation Changeset")
198+
json, err := template.JSON()
199+
if err != nil {
200+
return "", err
201+
}
202+
203+
param := []*cloudformation.Parameter{}
204+
for name := range parameters {
205+
param = append(param, &cloudformation.Parameter{
206+
ParameterKey: aws.String(name),
207+
UsePreviousValue: aws.Bool(true),
208+
})
209+
}
210+
211+
update := fmt.Sprintf("Update%s", time.Now().Format("2006-01-02-15-04-05"))
212+
changeset, err := s.CF.CreateChangeSetWithContext(ctx, &cloudformation.CreateChangeSetInput{
213+
ChangeSetName: aws.String(update),
214+
ChangeSetType: aws.String(cloudformation.ChangeSetTypeUpdate),
215+
StackName: aws.String(name),
216+
TemplateBody: aws.String(string(json)),
217+
Parameters: param,
218+
Capabilities: []*string{
219+
aws.String(cloudformation.CapabilityCapabilityIam),
220+
},
221+
})
222+
if err != nil {
223+
return "", err
224+
}
225+
226+
err = s.CF.WaitUntilChangeSetCreateCompleteWithContext(ctx, &cloudformation.DescribeChangeSetInput{
227+
ChangeSetName: changeset.Id,
228+
})
229+
return *changeset.Id, err
230+
}
231+
232+
func (s sdk) UpdateStack(ctx context.Context, changeset string) error {
233+
desc, err := s.CF.DescribeChangeSetWithContext(ctx, &cloudformation.DescribeChangeSetInput{
234+
ChangeSetName: aws.String(changeset),
235+
})
236+
if err != nil {
237+
return err
238+
}
239+
240+
if strings.HasPrefix(aws.StringValue(desc.StatusReason), "The submitted information didn't contain changes.") {
241+
return nil
242+
}
243+
244+
_, err = s.CF.ExecuteChangeSet(&cloudformation.ExecuteChangeSetInput{
245+
ChangeSetName: aws.String(changeset),
246+
})
247+
return err
248+
}
249+
197250
func (s sdk) WaitStackComplete(ctx context.Context, name string, operation int) error {
198251
input := &cloudformation.DescribeStacksInput{
199252
StackName: aws.String(name),

pkg/compose/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type ServiceStatus struct {
1919

2020
const (
2121
StackCreate = iota
22+
StackUpdate
2223
StackDelete
2324
)
2425

0 commit comments

Comments
 (0)