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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44ae5dfa519051496de9c2707df2441f8a0a9115
4
- data.tar.gz: 8f2065b8c59a2a2399f0398a0dc6a8541d864287
3
+ metadata.gz: d5d29ac1458d3d02d58c32fb317667305d2a6ca0
4
+ data.tar.gz: 093812e92afeca3f64aa0db34999222c85161c7b
5
5
  SHA512:
6
- metadata.gz: 50f3472de66c642581c80398f5430c9fcb596f5b973582834a65bfbca589265be7e2951a584e2459f42f2aa9fcdf1fd2c9fd9bfd07dca1397e8007d67641c6f4
7
- data.tar.gz: 2a2e5221add3e4aa4c3910de36f73d26eb551d63b3272526e5329124d386feccc8566bcbe593a4e49833e8996c7835ab1d577b4e72d70326176fbc5242f45c1e
6
+ metadata.gz: a2695db0d497f24a78f583b14377ff3f88869d055262696eca00c61d2ed111faa83801aaf1c5d9499e412c6c51c1c18ae2621188dd88180c361b27a07176f78c
7
+ data.tar.gz: 53940ce6b011d5adbf44bf4c7f3ab0b9467101989eea7d23412198eac63caeb474ca1e95bc606b577588f70d114637f8d08749897494749b02a9468ecc159c28
@@ -23,6 +23,7 @@ module StackMaster
23
23
  :describe_stack_resources,
24
24
  :get_template,
25
25
  :get_stack_policy,
26
+ :set_stack_policy,
26
27
  :describe_stack_events,
27
28
  :update_stack,
28
29
  :create_stack,
@@ -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, 'Action to take on CREATE_FAILURE. Valid Values: [ DO_NOTHING | ROLLBACK | DELETE ]. Default: ROLLBACK'
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 ||= "ROLLBACK"
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
- failed!("Stack creation aborted")
83
+ cf.delete_stack(stack_name: stack_name)
84
+ halt!('Stack creation aborted')
67
85
  end
68
- upload_files
69
- on_failure = @config.stack_defaults['on_failure'] || @options.on_failure
70
- cf.create_stack(stack_options.merge({tags: proposed_stack.aws_tags, on_failure: on_failure}))
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
@@ -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
- update_stack(change_set)
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)
@@ -1,3 +1,3 @@
1
1
  module StackMaster
2
- VERSION = "0.15.0"
2
+ VERSION = "0.16.0"
3
3
  end
@@ -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 'calls the create stack API method' do
142
+ it 'creates a change set for a new stack' do
126
143
  apply
127
- expect(cf).to have_received(:create_stack).with(
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
- key: 'environment',
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
- stack_policy_body: stack_policy_body,
142
- on_failure: 'ROLLBACK'
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
- it 'on_failure can be set to a custom value' do
165
- config.stack_defaults['on_failure'] = 'DELETE'
166
- apply
167
- expect(cf).to have_received(:create_stack).with(
168
- hash_including(on_failure: 'DELETE')
169
- )
170
- end
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
- it 'on_failure can be passed in options' do
173
- options = Commander::Command::Options.new
174
- options.on_failure = 'DELETE'
175
- StackMaster::Commands::Apply.perform(config, stack_definition, options)
176
- expect(cf).to have_received(:create_stack).with(
177
- hash_including(on_failure: 'DELETE')
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.15.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-04-27 00:00:00.000000000 Z
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.11
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