stackup 0.8.4 → 0.9.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e91040410c92ac50cf90f111e189e4a3d945be8
4
- data.tar.gz: bcd8623414938cf9e81a33b5a0f7023fc6ec7a55
3
+ metadata.gz: a8edc4a00c69a1f93ac2549985d3268aecc40c0c
4
+ data.tar.gz: 8d5c670e3c2b3fba4718df0368b363dd129ddf40
5
5
  SHA512:
6
- metadata.gz: 826361e54138687d38e1b8aa90be70a4c1b5f816f73000528c30b92f405b7d054b929d1e6372ae4c37d3cb943da34d9d9e157d74a0ecde7e52b8211dcedb131a
7
- data.tar.gz: 3971d9954a435d33ae91b19210f42068655f4383eaaeb2b44771c1ed360bc8f91f1c04e6bce5d06399f03b92466240f29913b1c191870e711de22f49710fa4c2
6
+ metadata.gz: dbf64ed470760758b6c6976724502d877f40f37a60537d80a82e708c480ffc9768b958c0532997d4f97b5893c7a3a4d3698276106c85d6e5bdecf8210fb1e21a
7
+ data.tar.gz: 120becc33de075bfadc14a5062375d83bdd2138b026eb4926e37256a6a5812b9ee04016069771f96ad785e0963d5052c732922510feb4b4c279a7067910985f6
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.9.2 (2016-08-04)
2
+
3
+ * Make poll_interval (during deployment) configurable.
4
+ * Add `--wait-poll-interval` option.
5
+ * Minimise calls to "DescribeStackEvents" (esp. for older stacks).
6
+ * Add `--no-wait` option.
7
+
1
8
  ## 0.8.4 (2016-07-08)
2
9
 
3
10
  * Ensure stack tag values are strings.
data/bin/stackup CHANGED
@@ -37,6 +37,12 @@ Clamp do
37
37
  end
38
38
  end
39
39
 
40
+ option ["--[no-]wait"], :flag, "wait for stack updates to complete",
41
+ :default => true
42
+
43
+ option ["--wait-poll-interval"], "N", "polling interval while waiting for updates",
44
+ :default => 5, &method(:Integer)
45
+
40
46
  option "--debug", :flag, "enable debugging"
41
47
 
42
48
  option ["--version"], :flag, "display version" do
@@ -79,7 +85,7 @@ Clamp do
79
85
  end
80
86
 
81
87
  def stack
82
- stackup.stack(stack_name)
88
+ stackup.stack(stack_name, :wait => wait?, :wait_poll_interval => wait_poll_interval)
83
89
  end
84
90
 
85
91
  def list_stacks
data/lib/stackup/stack.rb CHANGED
@@ -11,16 +11,27 @@ module Stackup
11
11
  #
12
12
  class Stack
13
13
 
14
+ DEFAULT_WAIT_POLL_INTERVAL = 5 # seconds
15
+
14
16
  def initialize(name, client = {}, options = {})
15
17
  client = Aws::CloudFormation::Client.new(client) if client.is_a?(Hash)
16
18
  @name = name
17
19
  @cf_client = client
20
+ @wait = true
18
21
  options.each do |key, value|
19
22
  public_send("#{key}=", value)
20
23
  end
24
+ @wait_poll_interval ||= DEFAULT_WAIT_POLL_INTERVAL
21
25
  end
22
26
 
23
27
  attr_reader :name
28
+ attr_accessor :wait_poll_interval
29
+
30
+ attr_writer :wait
31
+
32
+ def wait?
33
+ @wait
34
+ end
24
35
 
25
36
  # Register a handler for reporting of stack events.
26
37
  # @param [Proc] event_handler
@@ -141,11 +152,9 @@ module Stackup
141
152
  rescue NoSuchStack
142
153
  return nil
143
154
  end
144
- status = modify_stack do
155
+ modify_stack("DELETE_COMPLETE", "stack delete failed") do
145
156
  cf_stack.delete
146
157
  end
147
- fail StackUpdateError, "stack delete failed" unless status == "DELETE_COMPLETE"
148
- status
149
158
  ensure
150
159
  @stack_id = nil
151
160
  end
@@ -158,11 +167,9 @@ module Stackup
158
167
  # @raise [Stackup::StackUpdateError] if operation fails
159
168
  #
160
169
  def cancel_update
161
- status = modify_stack do
170
+ modify_stack(/_COMPLETE$/, "update cancel failed") do
162
171
  cf_stack.cancel_update
163
172
  end
164
- fail StackUpdateError, "update cancel failed" unless status =~ /_COMPLETE$/
165
- status
166
173
  rescue InvalidStateError
167
174
  nil
168
175
  end
@@ -172,7 +179,7 @@ module Stackup
172
179
  # @return [String] status, once stable
173
180
  #
174
181
  def wait
175
- modify_stack do
182
+ modify_stack(/./, "failed to stabilize") do
176
183
  # nothing
177
184
  end
178
185
  end
@@ -249,22 +256,18 @@ module Stackup
249
256
  options[:stack_name] = name
250
257
  options.delete(:stack_policy_during_update_body)
251
258
  options.delete(:stack_policy_during_update_url)
252
- status = modify_stack do
259
+ modify_stack("CREATE_COMPLETE", "stack creation failed") do
253
260
  cf.create_stack(options)
254
261
  end
255
- fail StackUpdateError, "stack creation failed" unless status == "CREATE_COMPLETE"
256
- status
257
262
  end
258
263
 
259
264
  def update(options)
260
265
  options.delete(:disable_rollback)
261
266
  options.delete(:on_failure)
262
267
  options.delete(:timeout_in_minutes)
263
- status = modify_stack do
268
+ modify_stack("UPDATE_COMPLETE", "stack update failed") do
264
269
  cf_stack.update(options)
265
270
  end
266
- fail StackUpdateError, "stack update failed" unless status == "UPDATE_COMPLETE"
267
- status
268
271
  rescue NoUpdateRequired
269
272
  logger.info "No update required"
270
273
  nil
@@ -283,11 +286,25 @@ module Stackup
283
286
  end
284
287
  end
285
288
 
289
+ # Execute a block, to modify the stack.
290
+ #
291
+ # @return the stack status
292
+ #
293
+ def modify_stack(target_status, failure_message, &block)
294
+ if wait?
295
+ status = modify_stack_synchronously(&block)
296
+ fail StackUpdateError, failure_message unless target_status === status
297
+ status
298
+ else
299
+ modify_stack_asynchronously(&block)
300
+ end
301
+ end
302
+
286
303
  # Execute a block, reporting stack events, until the stack is stable.
287
304
  #
288
305
  # @return the final stack status
289
306
  #
290
- def modify_stack
307
+ def modify_stack_synchronously
291
308
  watch do |watcher|
292
309
  handling_validation_error do
293
310
  yield
@@ -297,11 +314,22 @@ module Stackup
297
314
  status = self.status
298
315
  logger.debug("stack_status=#{status}")
299
316
  return status if status.nil? || status =~ /_(COMPLETE|FAILED)$/
300
- sleep(5)
317
+ sleep(wait_poll_interval)
301
318
  end
302
319
  end
303
320
  end
304
321
 
322
+ # Execute a block to instigate stack modification, but don't wait.
323
+ #
324
+ # @return the stack status
325
+ #
326
+ def modify_stack_asynchronously
327
+ handling_validation_error do
328
+ yield
329
+ end
330
+ self.status
331
+ end
332
+
305
333
  def normalize_tags(tags)
306
334
  if tags.is_a?(Hash)
307
335
  tags.map do |key, value|
@@ -10,7 +10,6 @@ module Stackup
10
10
 
11
11
  def initialize(stack)
12
12
  @stack = stack
13
- @processed_event_ids = Set.new
14
13
  end
15
14
 
16
15
  attr_accessor :stack
@@ -19,14 +18,14 @@ module Stackup
19
18
  #
20
19
  def each_new_event
21
20
  # rubocop:disable Lint/HandleExceptions
22
- buffer = []
21
+ new_events = []
23
22
  stack.events.each do |event|
24
- break if @processed_event_ids.include?(event.event_id)
25
- buffer.unshift(event)
23
+ break if event.id == @last_processed_event_id
24
+ new_events.unshift(event)
26
25
  end
27
- buffer.each do |event|
28
- yield event if block_given?
29
- @processed_event_ids.add(event.event_id)
26
+ new_events.each do |event|
27
+ yield event
28
+ @last_processed_event_id = event.id
30
29
  end
31
30
  rescue Aws::CloudFormation::Errors::ValidationError
32
31
  end
@@ -34,8 +33,11 @@ module Stackup
34
33
  # Consume all new events
35
34
  #
36
35
  def zero
37
- each_new_event
36
+ # rubocop:disable Lint/HandleExceptions
37
+ last_event = stack.events.first
38
+ @last_processed_event_id = last_event.id unless last_event.nil?
38
39
  nil
40
+ rescue Aws::CloudFormation::Errors::ValidationError
39
41
  end
40
42
 
41
43
  private
@@ -1,5 +1,5 @@
1
1
  module Stackup
2
2
 
3
- VERSION = "0.8.4"
3
+ VERSION = "0.9.2"
4
4
 
5
5
  end
@@ -8,8 +8,9 @@ describe Stackup::Stack do
8
8
 
9
9
  let(:stack_name) { "stack_name" }
10
10
  let(:unique_stack_id) { "ID:#{stack_name}" }
11
+ let(:stack_options) { {} }
11
12
 
12
- subject(:stack) { described_class.new(stack_name, cf_client) }
13
+ subject(:stack) { described_class.new(stack_name, cf_client, stack_options) }
13
14
 
14
15
  before do
15
16
  cf_client.stub_responses(:describe_stacks, *describe_stacks_responses)
@@ -93,7 +94,28 @@ describe Stackup::Stack do
93
94
 
94
95
  let(:final_status) { "CREATE_COMPLETE" }
95
96
 
96
- context "successful" do
97
+ it "calls :create_stack" do
98
+ expected_args = {
99
+ :stack_name => stack_name,
100
+ :template_body => template
101
+ }
102
+ create_or_update
103
+ expect(cf_client).to have_received(:create_stack)
104
+ .with(hash_including(expected_args))
105
+ end
106
+
107
+ it "it sleeps" do
108
+ create_or_update
109
+ expect(stack).to have_received(:sleep).with(5)
110
+ end
111
+
112
+ it "returns status" do
113
+ expect(create_or_update).to eq("CREATE_COMPLETE")
114
+ end
115
+
116
+ context "with :wait = false" do
117
+
118
+ let(:stack_options) { { :wait => false } }
97
119
 
98
120
  it "calls :create_stack" do
99
121
  expected_args = {
@@ -105,8 +127,13 @@ describe Stackup::Stack do
105
127
  .with(hash_including(expected_args))
106
128
  end
107
129
 
108
- it "returns status" do
109
- expect(create_or_update).to eq("CREATE_COMPLETE")
130
+ it "it does not sleep" do
131
+ create_or_update
132
+ expect(stack).not_to have_received(:sleep)
133
+ end
134
+
135
+ it "returns initial status" do
136
+ expect(create_or_update).to eq("CREATE_IN_PROGRESS")
110
137
  end
111
138
 
112
139
  end
@@ -122,6 +149,19 @@ describe Stackup::Stack do
122
149
 
123
150
  end
124
151
 
152
+ context "with a custom wait_poll_interval" do
153
+
154
+ let(:stack_options) do
155
+ { :wait_poll_interval => 12.3 }
156
+ end
157
+
158
+ it "it sleeps for the specified interval" do
159
+ create_or_update
160
+ expect(stack).to have_received(:sleep).with(12.3)
161
+ end
162
+
163
+ end
164
+
125
165
  context "with :template as data" do
126
166
 
127
167
  let(:options) do
@@ -1,6 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  require "aws-sdk-resources"
4
+ require "securerandom"
4
5
  require "stackup/stack_watcher"
5
6
 
6
7
  describe Stackup::StackWatcher do
@@ -11,13 +12,14 @@ describe Stackup::StackWatcher do
11
12
  subject(:monitor) { described_class.new(stack) }
12
13
 
13
14
  def add_event(description)
14
- @event_id ||= 0
15
+ event_id = SecureRandom.uuid
15
16
  event = instance_double(
16
17
  Aws::CloudFormation::Event,
17
- :event_id => @event_id, :resource_status_reason => description
18
+ :event_id => event_id,
19
+ :id => event_id,
20
+ :resource_status_reason => description
18
21
  )
19
22
  events.unshift(event)
20
- @event_id += 1
21
23
  end
22
24
 
23
25
  def new_event_reasons
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stackup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.4
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Williams
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-07-08 00:00:00.000000000 Z
12
+ date: 2016-08-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk-resources
@@ -129,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
129
  version: '0'
130
130
  requirements: []
131
131
  rubyforge_project:
132
- rubygems_version: 2.5.1
132
+ rubygems_version: 2.6.6
133
133
  signing_key:
134
134
  specification_version: 4
135
135
  summary: Manage CloudFormation stacks