stackup 0.8.4 → 0.9.2

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: 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