stack_master 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|