Skip to content
14 changes: 10 additions & 4 deletions internal/pkg/aws/cloudformation/changeset.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,10 @@ func (cs *changeSet) String() string {

// create creates a ChangeSet, waits until it's created, and returns the ChangeSet ID on success.
func (cs *changeSet) create(conf *stackConfig) error {
out, err := cs.client.CreateChangeSet(&cloudformation.CreateChangeSetInput{
input := &cloudformation.CreateChangeSetInput{
ChangeSetName: aws.String(cs.name),
StackName: aws.String(cs.stackName),
ChangeSetType: aws.String(cs.csType.String()),
TemplateBody: aws.String(conf.Template),
Parameters: conf.Parameters,
Tags: conf.Tags,
RoleARN: conf.RoleARN,
Expand All @@ -104,7 +103,15 @@ func (cs *changeSet) create(conf *stackConfig) error {
cloudformation.CapabilityCapabilityNamedIam,
cloudformation.CapabilityCapabilityAutoExpand,
}),
})
}
if conf.TemplateBody != "" {
input.TemplateBody = aws.String(conf.TemplateBody)
}
if conf.TemplateURL != "" {
input.TemplateURL = aws.String(conf.TemplateURL)
}

out, err := cs.client.CreateChangeSet(input)
if err != nil {
return fmt.Errorf("create %s: %w", cs, err)
}
Expand All @@ -114,7 +121,6 @@ func (cs *changeSet) create(conf *stackConfig) error {
if err != nil {
return fmt.Errorf("wait for creation of %s: %w", cs, err)
}

// Since the ChangeSet creation succeeded, use the full ARN instead of the name.
// Using the full ID is essential in case the ChangeSet execution status is obsolete.
// If we call DescribeChangeSet using the ChangeSet name and Stack name on an obsolete changeset, the results is empty.
Expand Down
10 changes: 9 additions & 1 deletion internal/pkg/aws/cloudformation/cloudformation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ func TestCloudFormation_Create(t *testing.T) {
return m
},
},
"creates the stack with templateURL": {
createMock: func(ctrl *gomock.Controller) client {
m := mocks.NewMockclient(ctrl)
m.EXPECT().DescribeStacks(gomock.Any()).Return(nil, errDoesNotExist)
addCreateDeployCalls(m)
return m
},
},
"creates the stack after cleaning the previously failed execution": {
createMock: func(ctrl *gomock.Controller) client {
m := mocks.NewMockclient(ctrl)
Expand Down Expand Up @@ -1184,7 +1192,7 @@ func addDeployCalls(m *mocks.Mockclient, changeSetType string) {
ChangeSetName: aws.String(mockChangeSetName),
StackName: aws.String(mockStack.Name),
ChangeSetType: aws.String(changeSetType),
TemplateBody: aws.String(mockStack.Template),
TemplateBody: aws.String(mockStack.TemplateBody),
Parameters: nil,
Tags: nil,
RoleARN: nil,
Expand Down
25 changes: 20 additions & 5 deletions internal/pkg/aws/cloudformation/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ type Stack struct {
}

type stackConfig struct {
Template string
Parameters []*cloudformation.Parameter
Tags []*cloudformation.Tag
RoleARN *string
TemplateBody string
TemplateURL string
Parameters []*cloudformation.Parameter
Tags []*cloudformation.Tag
RoleARN *string
}

// StackOption allows you to initialize a Stack with additional properties.
Expand All @@ -29,7 +30,21 @@ func NewStack(name, template string, opts ...StackOption) *Stack {
s := &Stack{
Name: name,
stackConfig: &stackConfig{
Template: template,
TemplateBody: template,
},
}
for _, opt := range opts {
opt(s)
}
return s
}

// NewStackWithURL creates a stack with a URL to the template.
func NewStackWithURL(name, templateURL string, opts ...StackOption) *Stack {
s := &Stack{
Name: name,
stackConfig: &stackConfig{
TemplateURL: templateURL,
},
}
for _, opt := range opts {
Expand Down
31 changes: 30 additions & 1 deletion internal/pkg/aws/cloudformation/stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,36 @@ func TestNewStack(t *testing.T) {

// THEN
require.Equal(t, "hello", s.Name)
require.Equal(t, "world", s.Template)
require.Equal(t, "world", s.TemplateBody)
require.Equal(t, []*cloudformation.Parameter{
{
ParameterKey: aws.String("Port"),
ParameterValue: aws.String("80"),
},
}, s.Parameters)
require.Equal(t, []*cloudformation.Tag{
{
Key: aws.String("copilot-application"),
Value: aws.String("phonetool"),
},
}, s.Tags)
require.Equal(t, aws.String("arn"), s.RoleARN)
}

func TestNewStackWithURL(t *testing.T) {
// WHEN
s := NewStackWithURL("hello", "worldlyURL",
WithParameters(map[string]string{
"Port": "80",
}),
WithTags(map[string]string{
"copilot-application": "phonetool",
}),
WithRoleARN("arn"))

// THEN
require.Equal(t, "hello", s.Name)
require.Equal(t, "worldlyURL", s.TemplateURL)
require.Equal(t, []*cloudformation.Parameter{
{
ParameterKey: aws.String("Port"),
Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/cli/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,8 @@ type imageRemover interface {
}

type pipelineDeployer interface {
CreatePipeline(env *deploy.CreatePipelineInput) error
UpdatePipeline(env *deploy.CreatePipelineInput) error
CreatePipeline(env *deploy.CreatePipelineInput, bucketName string) error
UpdatePipeline(env *deploy.CreatePipelineInput, bucketName string) error
PipelineExists(env *deploy.CreatePipelineInput) (bool, error)
DeletePipeline(pipelineName string) error
AddPipelineResourcesToApp(app *config.Application, region string) error
Expand Down
32 changes: 16 additions & 16 deletions internal/pkg/cli/mocks/mock_interfaces.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading