stack_master 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/stack_master/aws_driver/cloud_formation.rb +1 -0
- data/lib/stack_master/cli.rb +1 -1
- data/lib/stack_master/commands/apply.rb +40 -6
- data/lib/stack_master/stack.rb +1 -1
- data/lib/stack_master/test_driver/cloud_formation.rb +7 -7
- data/lib/stack_master/version.rb +1 -1
- data/spec/stack_master/commands/apply_spec.rb +76 -31
- data/spec/stack_master/test_driver/cloud_formation_spec.rb +14 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5d29ac1458d3d02d58c32fb317667305d2a6ca0
|
4
|
+
data.tar.gz: 093812e92afeca3f64aa0db34999222c85161c7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2695db0d497f24a78f583b14377ff3f88869d055262696eca00c61d2ed111faa83801aaf1c5d9499e412c6c51c1c18ae2621188dd88180c361b27a07176f78c
|
7
|
+
data.tar.gz: 53940ce6b011d5adbf44bf4c7f3ab0b9467101989eea7d23412198eac63caeb474ca1e95bc606b577588f70d114637f8d08749897494749b02a9468ecc159c28
|
data/lib/stack_master/cli.rb
CHANGED
@@ -41,7 +41,7 @@ module StackMaster
|
|
41
41
|
c.summary = 'Creates or updates a stack'
|
42
42
|
c.description = "Creates or updates a stack. Shows a diff of the proposed stack's template and parameters. Tails stack events until CloudFormation has completed."
|
43
43
|
c.example 'update a stack named myapp-vpc in us-east-1', 'stack_master apply us-east-1 myapp-vpc'
|
44
|
-
c.option '--on-failure ACTION', String,
|
44
|
+
c.option '--on-failure ACTION', String, "Action to take on CREATE_FAILURE. Valid Values: [ DO_NOTHING | ROLLBACK | DELETE ]. Default: ROLLBACK\nNote: You cannot use this option with Serverless Application Model (SAM) templates."
|
45
45
|
c.action do |args, options|
|
46
46
|
options.defaults config: default_config_file
|
47
47
|
execute_stacks_command(StackMaster::Commands::Apply, args, options)
|
@@ -12,7 +12,7 @@ module StackMaster
|
|
12
12
|
@stack_definition = stack_definition
|
13
13
|
@from_time = Time.now
|
14
14
|
@options = options
|
15
|
-
@options.on_failure ||=
|
15
|
+
@options.on_failure ||= nil
|
16
16
|
end
|
17
17
|
|
18
18
|
def perform
|
@@ -21,6 +21,7 @@ module StackMaster
|
|
21
21
|
ensure_valid_template_body_size!
|
22
22
|
create_or_update_stack
|
23
23
|
tail_stack_events
|
24
|
+
set_stack_policy
|
24
25
|
end
|
25
26
|
|
26
27
|
private
|
@@ -62,12 +63,32 @@ module StackMaster
|
|
62
63
|
end
|
63
64
|
|
64
65
|
def create_stack
|
66
|
+
upload_files
|
67
|
+
if use_change_set?
|
68
|
+
create_stack_by_change_set
|
69
|
+
else
|
70
|
+
create_stack_directly
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def use_change_set?
|
75
|
+
@options.on_failure.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_stack_by_change_set
|
79
|
+
@change_set = ChangeSet.create(stack_options.merge(change_set_type: 'CREATE'))
|
80
|
+
halt!(@change_set.status_reason) if @change_set.failed?
|
81
|
+
@change_set.display(StackMaster.stdout)
|
65
82
|
unless ask?('Create stack (y/n)? ')
|
66
|
-
|
83
|
+
cf.delete_stack(stack_name: stack_name)
|
84
|
+
halt!('Stack creation aborted')
|
67
85
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
86
|
+
execute_change_set
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_stack_directly
|
90
|
+
failed!('Stack creation aborted') unless ask?('Create stack (y/n)? ')
|
91
|
+
cf.create_stack(stack_options.merge(on_failure: @options.on_failure))
|
71
92
|
end
|
72
93
|
|
73
94
|
def ask_to_cancel_stack_update
|
@@ -121,10 +142,10 @@ module StackMaster
|
|
121
142
|
{
|
122
143
|
stack_name: stack_name,
|
123
144
|
parameters: proposed_stack.aws_parameters,
|
145
|
+
tags: proposed_stack.aws_tags,
|
124
146
|
capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
|
125
147
|
role_arn: proposed_stack.role_arn,
|
126
148
|
notification_arns: proposed_stack.notification_arns,
|
127
|
-
stack_policy_body: proposed_stack.stack_policy_body,
|
128
149
|
template_method => template_value
|
129
150
|
}
|
130
151
|
end
|
@@ -166,6 +187,19 @@ module StackMaster
|
|
166
187
|
end
|
167
188
|
end
|
168
189
|
|
190
|
+
def set_stack_policy
|
191
|
+
current_policy = stack && stack.stack_policy_body
|
192
|
+
proposed_policy = proposed_stack.stack_policy_body
|
193
|
+
# No need to reset a stack policy if it's nil or not changed
|
194
|
+
return if proposed_policy.nil? || proposed_policy == current_policy
|
195
|
+
StackMaster.stdout.print 'Setting a stack policy...'
|
196
|
+
cf.set_stack_policy(
|
197
|
+
stack_name: stack_name,
|
198
|
+
stack_policy_body: proposed_policy
|
199
|
+
)
|
200
|
+
StackMaster.stdout.puts 'done.'
|
201
|
+
end
|
202
|
+
|
169
203
|
extend Forwardable
|
170
204
|
def_delegators :@stack_definition, :stack_name, :region
|
171
205
|
end
|
data/lib/stack_master/stack.rb
CHANGED
@@ -41,7 +41,7 @@ module StackMaster
|
|
41
41
|
params_hash[param_struct.parameter_key] = param_struct.parameter_value
|
42
42
|
params_hash
|
43
43
|
end
|
44
|
-
template_body ||= cf.get_template(stack_name: stack_name).template_body
|
44
|
+
template_body ||= cf.get_template(stack_name: stack_name, template_stage: 'Original').template_body
|
45
45
|
template_format = TemplateUtils.identify_template_format(template_body)
|
46
46
|
stack_policy_body ||= cf.get_stack_policy(stack_name: stack_name).stack_policy_body
|
47
47
|
outputs = cf_stack.outputs
|
@@ -87,6 +87,8 @@ module StackMaster
|
|
87
87
|
options.merge!(change_set_id: id)
|
88
88
|
@change_sets[id] = options
|
89
89
|
@change_sets[options.fetch(:change_set_name)] = options
|
90
|
+
stack_name = options.fetch(:stack_name)
|
91
|
+
add_stack(stack_name: stack_name, stack_status: 'REVIEW_IN_PROGRESS') unless @stacks[stack_name]
|
90
92
|
OpenStruct.new(id: id)
|
91
93
|
end
|
92
94
|
|
@@ -106,7 +108,7 @@ module StackMaster
|
|
106
108
|
def execute_change_set(options)
|
107
109
|
change_set_id = options.fetch(:change_set_name)
|
108
110
|
change_set = @change_sets.fetch(change_set_id)
|
109
|
-
|
111
|
+
@stacks[change_set.fetch(:stack_name)].attributes = change_set
|
110
112
|
end
|
111
113
|
|
112
114
|
def delete_change_set(options)
|
@@ -142,17 +144,15 @@ module StackMaster
|
|
142
144
|
OpenStruct.new(stack_policy_body: @stack_policies[options.fetch(:stack_name)])
|
143
145
|
end
|
144
146
|
|
147
|
+
def set_stack_policy(options)
|
148
|
+
@stack_policies[options.fetch(:stack_name)] = options[:stack_policy_body]
|
149
|
+
end
|
150
|
+
|
145
151
|
def describe_stack_events(options)
|
146
152
|
events = @stack_events[options.fetch(:stack_name)] || []
|
147
153
|
OpenStruct.new(stack_events: events, next_token: nil)
|
148
154
|
end
|
149
155
|
|
150
|
-
def update_stack(options)
|
151
|
-
stack_name = options.fetch(:stack_name)
|
152
|
-
@stacks[stack_name].attributes = options
|
153
|
-
@stack_policies[stack_name] = options[:stack_policy_body]
|
154
|
-
end
|
155
|
-
|
156
156
|
def create_stack(options)
|
157
157
|
stack_name = options.fetch(:stack_name)
|
158
158
|
add_stack(options)
|
data/lib/stack_master/version.rb
CHANGED
@@ -12,6 +12,7 @@ RSpec.describe StackMaster::Commands::Apply do
|
|
12
12
|
let(:parameters) { { 'param_1' => 'hello' } }
|
13
13
|
let(:proposed_stack) { StackMaster::Stack.new(template_body: template_body, template_format: template_format, tags: { 'environment' => 'production' } , parameters: parameters, role_arn: role_arn, notification_arns: [notification_arn], stack_policy_body: stack_policy_body ) }
|
14
14
|
let(:stack_policy_body) { '{}' }
|
15
|
+
let(:change_set) { double(display: true, failed?: false, id: '1') }
|
15
16
|
|
16
17
|
before do
|
17
18
|
allow(StackMaster::Stack).to receive(:find).with(region, stack_name).and_return(stack)
|
@@ -23,6 +24,10 @@ RSpec.describe StackMaster::Commands::Apply do
|
|
23
24
|
allow(StackMaster::StackDiffer).to receive(:new).with(proposed_stack, stack).and_return double.as_null_object
|
24
25
|
allow(StackMaster::StackEvents::Streamer).to receive(:stream)
|
25
26
|
allow(StackMaster).to receive(:interactive?).and_return(false)
|
27
|
+
allow(cf).to receive(:create_change_set).and_return(OpenStruct.new(id: '1'))
|
28
|
+
allow(StackMaster::ChangeSet).to receive(:create).and_return(change_set)
|
29
|
+
allow(cf).to receive(:execute_change_set).and_return(OpenStruct.new(id: '1'))
|
30
|
+
allow(cf).to receive(:set_stack_policy)
|
26
31
|
end
|
27
32
|
|
28
33
|
def apply
|
@@ -31,13 +36,6 @@ RSpec.describe StackMaster::Commands::Apply do
|
|
31
36
|
|
32
37
|
context 'the stack exist' do
|
33
38
|
let(:stack) { StackMaster::Stack.new(stack_id: '1') }
|
34
|
-
let(:change_set) { double(display: true, failed?: false, id: 'id-1') }
|
35
|
-
|
36
|
-
before do
|
37
|
-
allow(cf).to receive(:create_change_set).and_return(OpenStruct.new(id: '1'))
|
38
|
-
allow(StackMaster::ChangeSet).to receive(:create).and_return(change_set)
|
39
|
-
allow(cf).to receive(:execute_change_set).and_return(OpenStruct.new(id: '1'))
|
40
|
-
end
|
41
39
|
|
42
40
|
it 'creates a change set' do
|
43
41
|
apply
|
@@ -47,10 +45,12 @@ RSpec.describe StackMaster::Commands::Apply do
|
|
47
45
|
parameters: [
|
48
46
|
{ parameter_key: 'param_1', parameter_value: 'hello' }
|
49
47
|
],
|
48
|
+
tags: [
|
49
|
+
{ key: 'environment', value: 'production' }
|
50
|
+
],
|
50
51
|
capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
|
51
52
|
role_arn: role_arn,
|
52
|
-
notification_arns: [notification_arn]
|
53
|
-
stack_policy_body: stack_policy_body
|
53
|
+
notification_arns: [notification_arn]
|
54
54
|
)
|
55
55
|
end
|
56
56
|
|
@@ -61,6 +61,23 @@ RSpec.describe StackMaster::Commands::Apply do
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
it 'attaches a stack policy to the stack' do
|
65
|
+
apply
|
66
|
+
expect(cf).to have_received(:set_stack_policy).with(
|
67
|
+
stack_name: stack_name,
|
68
|
+
stack_policy_body: stack_policy_body
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'stack policy is not changed' do
|
73
|
+
let(:stack) { StackMaster::Stack.new(stack_id: '1', stack_policy_body: stack_policy_body) }
|
74
|
+
|
75
|
+
it 'does not set a stack policy' do
|
76
|
+
apply
|
77
|
+
expect(cf).to_not have_received(:set_stack_policy)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
64
81
|
context 'when using s3' do
|
65
82
|
before do
|
66
83
|
stack_definition.s3 = {
|
@@ -122,24 +139,52 @@ RSpec.describe StackMaster::Commands::Apply do
|
|
122
139
|
context 'the stack does not exist' do
|
123
140
|
let(:stack) { nil }
|
124
141
|
|
125
|
-
it '
|
142
|
+
it 'creates a change set for a new stack' do
|
126
143
|
apply
|
127
|
-
expect(
|
144
|
+
expect(StackMaster::ChangeSet).to have_received(:create).with(
|
128
145
|
stack_name: stack_name,
|
129
146
|
template_body: proposed_stack.template_body,
|
130
147
|
parameters: [
|
131
148
|
{ parameter_key: 'param_1', parameter_value: 'hello' }
|
132
149
|
],
|
133
150
|
tags: [
|
134
|
-
{
|
135
|
-
|
136
|
-
value: 'production'
|
137
|
-
}],
|
151
|
+
{ key: 'environment', value: 'production' }
|
152
|
+
],
|
138
153
|
capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
|
139
154
|
role_arn: role_arn,
|
140
155
|
notification_arns: [notification_arn],
|
141
|
-
|
142
|
-
|
156
|
+
change_set_type: 'CREATE'
|
157
|
+
)
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'on_failure option is set' do
|
161
|
+
it 'calls the create stack API method' do
|
162
|
+
options = Commander::Command::Options.new
|
163
|
+
options.on_failure = 'ROLLBACK'
|
164
|
+
StackMaster::Commands::Apply.perform(config, stack_definition, options)
|
165
|
+
apply
|
166
|
+
expect(cf).to have_received(:create_stack).with(
|
167
|
+
stack_name: stack_name,
|
168
|
+
template_body: proposed_stack.template_body,
|
169
|
+
parameters: [
|
170
|
+
{ parameter_key: 'param_1', parameter_value: 'hello' }
|
171
|
+
],
|
172
|
+
tags: [
|
173
|
+
{ key: 'environment', value: 'production' }
|
174
|
+
],
|
175
|
+
capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
|
176
|
+
role_arn: role_arn,
|
177
|
+
notification_arns: [notification_arn],
|
178
|
+
on_failure: 'ROLLBACK'
|
179
|
+
)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'attaches a stack policy to the created stack' do
|
184
|
+
apply
|
185
|
+
expect(cf).to have_received(:set_stack_policy).with(
|
186
|
+
stack_name: stack_name,
|
187
|
+
stack_policy_body: stack_policy_body
|
143
188
|
)
|
144
189
|
end
|
145
190
|
|
@@ -161,21 +206,21 @@ RSpec.describe StackMaster::Commands::Apply do
|
|
161
206
|
end
|
162
207
|
end
|
163
208
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
209
|
+
context 'user decides to not create a stack' do
|
210
|
+
before do
|
211
|
+
allow(StackMaster).to receive(:non_interactive_answer).and_return('n')
|
212
|
+
allow(cf).to receive(:delete_stack)
|
213
|
+
allow(StackMaster::ChangeSet).to receive(:execute)
|
214
|
+
apply
|
215
|
+
end
|
171
216
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
217
|
+
it 'deletes the stack' do
|
218
|
+
expect(cf).to have_received(:delete_stack).with(stack_name: stack_name)
|
219
|
+
end
|
220
|
+
|
221
|
+
it "doesn't execute the change set" do
|
222
|
+
expect(StackMaster::ChangeSet).to_not have_received(:execute).with(change_set.id)
|
223
|
+
end
|
179
224
|
end
|
180
225
|
end
|
181
226
|
|
@@ -8,7 +8,6 @@ RSpec.describe StackMaster::TestDriver::CloudFormation do
|
|
8
8
|
test_cf_driver.create_stack(stack_id: "1", stack_name: 'stack-1')
|
9
9
|
test_cf_driver.create_stack(stack_id: "2", stack_name: 'stack-2')
|
10
10
|
expect(test_cf_driver.describe_stacks.stacks.map(&:stack_id)).to eq(["1", "2"])
|
11
|
-
|
12
11
|
end
|
13
12
|
|
14
13
|
it 'adds and gets stack events' do
|
@@ -22,6 +21,12 @@ RSpec.describe StackMaster::TestDriver::CloudFormation do
|
|
22
21
|
test_cf_driver.set_template('stack-1', 'blah')
|
23
22
|
expect(test_cf_driver.get_template(stack_name: 'stack-1').template_body).to eq 'blah'
|
24
23
|
end
|
24
|
+
|
25
|
+
it 'sets and gets stack policies' do
|
26
|
+
stack_policy_body = '{}'
|
27
|
+
test_cf_driver.set_stack_policy(stack_name: 'stack-1', stack_policy_body: stack_policy_body)
|
28
|
+
expect(test_cf_driver.get_stack_policy(stack_name: 'stack-1').stack_policy_body).to eq(stack_policy_body)
|
29
|
+
end
|
25
30
|
end
|
26
31
|
|
27
32
|
context 'change sets' do
|
@@ -39,6 +44,14 @@ RSpec.describe StackMaster::TestDriver::CloudFormation do
|
|
39
44
|
expect(change_set.change_set_id).to eq change_set_id
|
40
45
|
expect(change_set.change_set_name).to eq 'change-set-1'
|
41
46
|
end
|
47
|
+
|
48
|
+
it 'creates stacks using change sets and describes stacks' do
|
49
|
+
change_set1 = test_cf_driver.create_change_set(change_set_name: 'change-set-1', stack_name: 'stack-1', change_set_type: 'CREATE')
|
50
|
+
change_set2 = test_cf_driver.create_change_set(change_set_name: 'change-set-2', stack_name: 'stack-2', change_set_type: 'CREATE')
|
51
|
+
test_cf_driver.execute_change_set(change_set_name: change_set1.id)
|
52
|
+
test_cf_driver.execute_change_set(change_set_name: change_set2.id)
|
53
|
+
expect(test_cf_driver.describe_stacks.stacks.map(&:stack_name)).to eq(['stack-1', 'stack-2'])
|
54
|
+
end
|
42
55
|
end
|
43
56
|
|
44
57
|
it 'deletes change sets' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stack_master
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Hodgkiss
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-05-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -450,7 +450,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
450
450
|
version: '0'
|
451
451
|
requirements: []
|
452
452
|
rubyforge_project:
|
453
|
-
rubygems_version: 2.6.
|
453
|
+
rubygems_version: 2.6.8
|
454
454
|
signing_key:
|
455
455
|
specification_version: 4
|
456
456
|
summary: StackMaster is a sure-footed way of creating, updating and keeping track
|