knife-cloudformation 0.1.22 → 0.2.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +56 -2
  4. data/knife-cloudformation.gemspec +4 -7
  5. data/lib/chef/knife/cloudformation_create.rb +105 -245
  6. data/lib/chef/knife/cloudformation_describe.rb +50 -26
  7. data/lib/chef/knife/cloudformation_destroy.rb +17 -18
  8. data/lib/chef/knife/cloudformation_events.rb +48 -14
  9. data/lib/chef/knife/cloudformation_export.rb +117 -34
  10. data/lib/chef/knife/cloudformation_import.rb +124 -18
  11. data/lib/chef/knife/cloudformation_inspect.rb +159 -71
  12. data/lib/chef/knife/cloudformation_list.rb +20 -24
  13. data/lib/chef/knife/cloudformation_promote.rb +40 -0
  14. data/lib/chef/knife/cloudformation_update.rb +132 -15
  15. data/lib/chef/knife/cloudformation_validate.rb +35 -0
  16. data/lib/knife-cloudformation.rb +28 -0
  17. data/lib/knife-cloudformation/cache.rb +213 -35
  18. data/lib/knife-cloudformation/knife.rb +9 -0
  19. data/lib/knife-cloudformation/knife/base.rb +179 -0
  20. data/lib/knife-cloudformation/knife/stack.rb +94 -0
  21. data/lib/knife-cloudformation/knife/template.rb +174 -0
  22. data/lib/knife-cloudformation/monkey_patch.rb +8 -0
  23. data/lib/knife-cloudformation/monkey_patch/stack.rb +195 -0
  24. data/lib/knife-cloudformation/provider.rb +225 -0
  25. data/lib/knife-cloudformation/utils.rb +18 -98
  26. data/lib/knife-cloudformation/utils/animal_strings.rb +28 -0
  27. data/lib/knife-cloudformation/utils/debug.rb +31 -0
  28. data/lib/knife-cloudformation/utils/json.rb +64 -0
  29. data/lib/knife-cloudformation/utils/object_storage.rb +28 -0
  30. data/lib/knife-cloudformation/utils/output.rb +79 -0
  31. data/lib/knife-cloudformation/utils/path_selector.rb +99 -0
  32. data/lib/knife-cloudformation/utils/ssher.rb +29 -0
  33. data/lib/knife-cloudformation/utils/stack_exporter.rb +271 -0
  34. data/lib/knife-cloudformation/utils/stack_parameter_scrubber.rb +35 -0
  35. data/lib/knife-cloudformation/utils/stack_parameter_validator.rb +124 -0
  36. data/lib/knife-cloudformation/version.rb +2 -4
  37. metadata +47 -94
  38. data/Gemfile +0 -3
  39. data/Gemfile.lock +0 -90
  40. data/knife-cloudformation-0.1.20.gem +0 -0
  41. data/lib/knife-cloudformation/aws_commons.rb +0 -267
  42. data/lib/knife-cloudformation/aws_commons/stack.rb +0 -435
  43. data/lib/knife-cloudformation/aws_commons/stack_parameter_validator.rb +0 -79
  44. data/lib/knife-cloudformation/cloudformation_base.rb +0 -168
  45. data/lib/knife-cloudformation/export.rb +0 -174
Binary file
@@ -1,267 +0,0 @@
1
- require 'fog'
2
- require 'knife-cloudformation/utils'
3
- require 'knife-cloudformation/cache'
4
-
5
- Dir.glob(File.join(File.dirname(__FILE__), 'aws_commons/*.rb')).each do |item|
6
- require "knife-cloudformation/aws_commons/#{File.basename(item).sub('.rb', '')}"
7
- end
8
-
9
- module KnifeCloudformation
10
- class AwsCommons
11
-
12
- class << self
13
- def logger=(l)
14
- @logger = l
15
- end
16
-
17
- def logger
18
- unless(@logger)
19
- require 'logger'
20
- @logger = Logger.new($stdout)
21
- @logger.level = ENV['DEBUG'] ? Logger::DEBUG : Logger::INFO
22
- end
23
- @logger
24
- end
25
- end
26
-
27
- include KnifeCloudformation::Utils::AnimalStrings
28
- include KnifeCloudformation::Utils::Debug
29
-
30
- FOG_MAP = {
31
- :ec2 => :compute
32
- }
33
-
34
- attr_reader :credentials
35
-
36
- def initialize(args={})
37
- @ui = args[:ui]
38
- @credentials = @creds = args[:fog]
39
- @disconnect_long_jobs = args[:disconnect_long_jobs]
40
- @throttled = []
41
- @connections = {}
42
- @memo = Cache.new(credentials)
43
- @local = {:stacks => {}}
44
- end
45
-
46
- def logger
47
- @logger || self.class.logger
48
- end
49
-
50
- def logger=(l)
51
- @logger = l
52
- end
53
-
54
- def cache
55
- @memo
56
- end
57
-
58
- def clear_cache(*types)
59
- @memo.clear!(*types)
60
- true
61
- end
62
-
63
- def build_connection(type)
64
- type = type.to_sym
65
- type = FOG_MAP[type] if FOG_MAP[type]
66
- unless(@connections[type])
67
- case type
68
- when :compute
69
- @connections[:compute] = Fog::Compute::AWS.new(@creds)
70
- when :dns
71
- dns_creds = @creds.dup
72
- dns_creds.delete(:region) || dns_creds.delete('region')
73
- @connections[:dns] = Fog::DNS::AWS.new(dns_creds)
74
- else
75
- begin
76
- Fog.credentials = Fog.symbolize_credentials(@creds)
77
- @connections[type] = Fog::AWS[type]
78
- Fog.credentials = {}
79
- rescue NameError
80
- klass = [camel(type.to_s), 'AWS'].inject(Fog) do |memo, item|
81
- memo.const_defined?(item) ? memo.const_get(item) : break
82
- end
83
- if(klass)
84
- @connections[type] = klass.new(Fog.symbolize_credentials(@creds))
85
- else
86
- raise
87
- end
88
- end
89
- end
90
- end
91
- if(block_given?)
92
- throttleable do
93
- yield @connections[type]
94
- end
95
- else
96
- @connections[type]
97
- end
98
- end
99
- alias_method :aws, :build_connection
100
-
101
- DEFAULT_STACK_STATUS = %w(
102
- CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED
103
- ROLLBACK_IN_PROGRESS ROLLBACK_COMPLETE ROLLBACK_FAILED
104
- UPDATE_IN_PROGRESS UPDATE_COMPLETE UPDATE_COMPLETE_CLEANUP_IN_PROGRESS
105
- UPDATE_ROLLBACK_IN_PROGRESS UPDATE_ROLLBACK_FAILED
106
- UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_ROLLBACK_COMPLETE
107
- DELETE_IN_PROGRESS DELETE_FAILED
108
- )
109
-
110
- def stacks(args={})
111
- status = Array(args[:status] || DEFAULT_STACK_STATUS).flatten.compact.map do |stat|
112
- stat.to_s.upcase
113
- end
114
- @memo.init(:stacks_lock, :lock)
115
- @memo.init(:stacks, :stamped)
116
- if(args[:cache_time])
117
- @memo[:stacks].stamp
118
- else
119
- if(args[:refresh_every])
120
- cache.apply_limit(:stacks, args[:refresh_every].to_i)
121
- end
122
- if(@memo[:stacks].update_allowed? || args[:force_refresh])
123
- long_running_job(:stacks) do
124
- logger.debug 'Populating full cloudformation list from remote end point'
125
- stack_result = throttleable do
126
- aws(:cloud_formation).describe_stacks.body['Stacks']
127
- end
128
- if(stack_result)
129
- @memo[:stacks_lock].lock do
130
- @memo[:stacks].value = stack_result
131
- end
132
- end
133
- logger.debug 'Full cloudformation list from remote end point complete'
134
- end
135
- end
136
- end
137
- if(@memo[:stacks].value)
138
- @memo[:stacks].value.find_all do |s|
139
- status.include?(s['StackStatus'])
140
- end
141
- else
142
- []
143
- end
144
- end
145
-
146
- def throttleable
147
- if(@throttled.size > 0)
148
- if(Time.now.to_i - @throttled.last < Time.now.to_i - @throttled.size * 15)
149
- logger.error "Currently being throttled. Not running request!"
150
- return nil
151
- end
152
- end
153
- begin
154
- result = yield
155
- @throttled.clear
156
- result
157
- rescue Fog::Service::Error => e
158
- if(e.message == 'Throttling => Rate exceeded')
159
- logger.error "Remote end point is is currently throttling. Rate has been exceeded."
160
- @throttled << Time.now.to_i
161
- end
162
- nil
163
- end
164
- end
165
-
166
- def long_running_job(name)
167
- if(@disconnect_long_jobs)
168
- logger.debug "Disconnected long running jobs enabled. Starting: #{name}"
169
- lock_key = "long_jobs_lock_#{name}".to_sym
170
- @memo.init(lock_key, :lock)
171
- Thread.new do
172
- begin
173
- @memo[lock_key].lock do
174
- begin
175
- logger.info "Long running job started disconnected (#{name})"
176
- yield
177
- rescue => e
178
- logger.error "Long running job failure (#{name}): #{e.class} - #{e}\n#{e.backtrace.join("\n")}"
179
- end
180
- end
181
- rescue Redis::Lock::Timeout
182
- logger.warn "Long running process failed to aquire lock. Request not run (#{name})"
183
- end
184
- end
185
- else
186
- logger.debug "Disconnected long running jobs disabled. Starting #{name} inline"
187
- yield
188
- end
189
- end
190
-
191
- def name_from_stack_id(s_id)
192
- found = stacks.detect do |s|
193
- s['StackId'] == s_id
194
- end
195
- found ? found['StackName'] : raise(IndexError.new("Failed to locate stack with ID: #{s_id}"))
196
- end
197
-
198
- def id_from_stack_name(name)
199
- found = stacks.detect do |s|
200
- s['StackName'] == name
201
- end
202
- found ? found['StackId'] : raise(IndexError.new("Failed to locate stack with name: #{name}"))
203
- end
204
-
205
- def stack(*names)
206
- direct_load = names.delete(:ignore_seeds)
207
- result = names.map do |name|
208
- [name, name.start_with?('arn:') || direct_load ? name : id_from_stack_name(name)]
209
- end.map do |name, s_id|
210
- unless(@local[:stacks][s_id])
211
- unless(direct_load)
212
- seed = stacks.detect do |stk|
213
- stk['StackId'] == s_id
214
- end
215
- end
216
- if(seed)
217
- logger.debug "Requested stack (#{name}) loaded via cached seed"
218
- else
219
- logger.debug "Requested stack (#{name}) loaded directly with no seed"
220
- end
221
- @local[:stacks][s_id] = Stack.new(name, self, seed)
222
- end
223
- @local[:stacks][s_id]
224
- end
225
- result.size == 1 ? result.first : result
226
- end
227
-
228
- def create_stack(name, definition)
229
- Stack.create(name, definition, self)
230
- end
231
-
232
- # Output Helpers
233
-
234
- def process(things, args={})
235
- @event_ids ||= []
236
- processed = things.reverse.map do |thing|
237
- next if @event_ids.include?(thing['EventId'])
238
- @event_ids.push(thing['EventId']).compact!
239
- if(args[:attributes])
240
- args[:attributes].map do |key|
241
- thing[key].to_s
242
- end
243
- else
244
- thing.values
245
- end
246
- end
247
- args[:flat] ? processed.flatten : processed
248
- end
249
-
250
- def get_titles(thing, args={})
251
- attrs = args[:attributes] || []
252
- if(attrs.empty?)
253
- hash = thing.is_a?(Array) ? thing.first : thing
254
- hash ||= {}
255
- attrs = hash.keys
256
- end
257
- titles = attrs.map do |key|
258
- key.gsub(/([a-z])([A-Z])/, '\1 \2')
259
- end.compact
260
- if(args[:format])
261
- titles.map{|s| @ui.color(s, :bold)}
262
- else
263
- titles
264
- end
265
- end
266
- end
267
- end
@@ -1,435 +0,0 @@
1
- require 'knife-cloudformation/cache'
2
- require 'knife-cloudformation/aws_commons'
3
- require 'digest/sha2'
4
-
5
- module KnifeCloudformation
6
- class AwsCommons
7
- class Stack
8
-
9
- include KnifeCloudformation::Utils::Debug
10
- include KnifeCloudformation::Utils::JSON
11
- include KnifeCloudformation::Utils::AnimalStrings
12
-
13
- attr_reader :name, :raw_stack, :raw_resources, :common
14
-
15
- class << self
16
-
17
- ALLOWED_PARAMETER_ATTRIBUTES = %w(
18
- Type Default NoEcho AllowedValues AllowedPattern
19
- MaxLength MinLength MaxValue MinValue Description
20
- ConstraintDescription
21
- )
22
-
23
- include KnifeCloudformation::Utils::JSON
24
-
25
- def create(name, definition, aws_common)
26
- aws_common.aws(:cloud_formation).create_stack(name, definition)
27
- new(name, aws_common)
28
- end
29
-
30
- def build_stack_definition(template, options={})
31
- stack = Mash.new
32
- options.each do |key, value|
33
- format_key = key.to_s.split('_').map do |k|
34
- "#{k.slice(0,1).upcase}#{k.slice(1,k.length)}"
35
- end.join
36
- stack[format_key] = value
37
- end
38
- enable_capabilities!(stack, template)
39
- clean_parameters!(template)
40
- stack['TemplateBody'] = _to_json(template)
41
- stack
42
- end
43
-
44
- # Currently only checking for IAM resources since that's all
45
- # that is supported for creation
46
- def enable_capabilities!(stack, template)
47
- found = Array(template['Resources']).detect do |resource_name, resource|
48
- resource['Type'].start_with?('AWS::IAM')
49
- end
50
- stack['Capabilities'] = ['CAPABILITY_IAM'] if found
51
- nil
52
- end
53
-
54
- def clean_parameters!(template)
55
- template['Parameters'].each do |name, options|
56
- options.delete_if do |attribute, value|
57
- !ALLOWED_PARAMETER_ATTRIBUTES.include?(attribute)
58
- end
59
- end
60
- end
61
-
62
- end
63
-
64
- def initialize(name, common, raw_stack=nil)
65
- @name = name
66
- @common = common
67
- @memo = Cache.new(common.credentials.merge(:stack => name))
68
- reset_local
69
- @memo.init(:raw_stack, :stamped)
70
- if(raw_stack)
71
- if(@memo[:stacks])
72
- if(@memo[:stacks].stamp > @memo[:raw_stack].stamp)
73
- @memo[:raw_stack].value = raw_stack
74
- end
75
- else
76
- @memo[:raw_stack].value = raw_stack
77
- end
78
- end
79
- load_stack
80
- @force_refresh = false
81
- @force_refresh = in_progress?
82
- end
83
-
84
- ## Actions ##
85
-
86
- def update(definition)
87
- if(definition.keys.detect{|k|k.is_a?(Symbol)})
88
- definition = format_definition(definition)
89
- end
90
- res = common.aws(:cloud_formation).update_stack(name, definition)
91
- reload!
92
- res
93
- end
94
-
95
- def format_definition(def_hash)
96
- new_hash = {}
97
- def_hash.each do |k,v|
98
- new_hash[camel(k)] = v
99
- end
100
- if(new_hash['TemplateBody'].is_a?(Hash))
101
- new_hash['TemplateBody'] = _to_json(new_hash['TemplateBody'])
102
- end
103
- new_hash
104
- end
105
-
106
- def destroy
107
- res = common.aws(:cloud_formation).delete_stack(name)
108
- reload!
109
- res
110
- end
111
-
112
- def load_stack(*args)
113
- @memo.init(:raw_stack, :stamped)
114
- begin
115
- @memo.init(:raw_stack_lock, :lock)
116
- @memo[:raw_stack_lock].lock do
117
- if(args.include?(:force) || @memo[:raw_stack].update_allowed?)
118
- @memo[:raw_stack].value = common.aws(:cloud_formation)
119
- .describe_stacks('StackName' => name)
120
- .body['Stacks'].first
121
- end
122
- end
123
- rescue => e
124
- if(defined?(Redis) && e.is_a?(Redis::Lock::LockTimeout))
125
- # someone else is updating
126
- debug 'Got lock timeout on stack load'
127
- else
128
- raise
129
- end
130
- end
131
- end
132
-
133
- def load_resources
134
- @memo.init(:raw_resources, :stamped)
135
- begin
136
- @memo.init(:raw_resources_lock, :lock)
137
- @memo[:raw_resources_lock].lock do
138
- if(@memo[:raw_resources].update_allowed?)
139
- @memo[:raw_resources].value = common.aws(:cloud_formation)
140
- .describe_stack_resources('StackName' => name)
141
- .body['StackResources']
142
- end
143
- end
144
- rescue => e
145
- if(defined?(Redis) && e.is_a?(Redis::Lock::LockTimeout))
146
- debug 'Got lock timeout on resource load'
147
- else
148
- raise e
149
- end
150
- end
151
- end
152
-
153
- def refresh?(bool=nil)
154
- bool || (bool.nil? && @force_refresh)
155
- end
156
-
157
- def reset_local
158
- @local = {
159
- :nodes => []
160
- }
161
- end
162
-
163
- def reload!
164
- @memo.clear! do
165
- load_stack(:force)
166
- load_resources
167
- @force_refresh = in_progress?
168
- end
169
- true
170
- end
171
-
172
- ## Information ##
173
-
174
- def serialize
175
- _to_json(to_hash)
176
- end
177
-
178
- def to_hash(extra_data={})
179
- {
180
- :template_body => template,
181
- :parameters => parameters,
182
- :capabilities => capabilities,
183
- :disable_rollback => disable_rollback,
184
- :notification_ARNs => notification_arns,
185
- :timeout_in_minutes => timeout
186
- }.merge(extra_data)
187
- end
188
-
189
- def template
190
- @memo.init(:template, :value)
191
- unless(@memo[:template].value)
192
- @memo[:template].value = _from_json(
193
- common.aws(:cloud_formation)
194
- .get_template(name).body['TemplateBody']
195
- )
196
- end
197
- @memo[:template].value
198
- end
199
-
200
- ## Stack metadata ##
201
- def parameters(raw=false)
202
- if(raw)
203
- @memo[:raw_stack].value['Parameters']
204
- else
205
- @memo.init(:parameters, :value)
206
- unless(@memo[:parameters].value)
207
- @memo[:parameters].value = Hash[*(
208
- @memo[:raw_stack].value['Parameters'].map do |ary|
209
- [ary['ParameterKey'], ary['ParameterValue']]
210
- end.flatten
211
- )]
212
- end
213
- @memo[:parameters].value
214
- end
215
- end
216
-
217
- def capabilities
218
- @memo[:raw_stack].value['Capabilities']
219
- end
220
-
221
- def disable_rollback
222
- @memo[:raw_stack].value['DisableRollback']
223
- end
224
-
225
- def notification_arns
226
- @memo[:raw_stack].value['NotificationARNs']
227
- end
228
-
229
- def timeout_in_minutes
230
- @memo[:raw_stack].value['TimeoutInMinutes']
231
- end
232
- alias_method :timeout, :timeout_in_minutes
233
-
234
- def stack_id
235
- @memo[:raw_stack].value['StackId']
236
- end
237
- alias_method :id, :stack_id
238
-
239
- def creation_time
240
- @memo[:raw_stack].value['CreationTime']
241
- end
242
- alias_method :created_at, :creation_time
243
-
244
- def status(force_refresh=nil)
245
- load_stack if refresh?(force_refresh)
246
- @memo[:raw_stack].value['StackStatus']
247
- end
248
-
249
- def resources(force_refresh=nil)
250
- load_resources if @memo[:raw_resources].nil? || refresh?(force_refresh)
251
- @memo[:raw_resources].value
252
- end
253
-
254
- def events(all=false)
255
- @memo.init(:events, :stamped)
256
- res = []
257
- if(@memo[:events].value.nil? || refresh?)
258
- begin
259
- @memo.init(:events_lock, :lock)
260
- @memo[:events_lock].lock do
261
- if(@memo[:events].update_allowed?)
262
- res = common.aws(:cloud_formation).describe_stack_events(name).body['StackEvents']
263
- current = @memo[:events].value || []
264
- current_events = current.map{|e| e['EventId']}
265
- res.delete_if{|e| current_events.include?(e['EventId'])}
266
- current += res
267
- current.uniq!
268
- current.sort!{|x,y| x['Timestamp'] <=> y['Timestamp']}
269
- @memo[:events].value = current
270
- end
271
- end
272
- rescue => e
273
- if(defined?(Redis) && e.is_a?(Redis::Lock::LockTimeout))
274
- debug 'Got lock timeout on events'
275
- else
276
- raise
277
- end
278
- end
279
- end
280
- all ? @memo[:events].value : res
281
- end
282
-
283
- def outputs(style=:unformatted)
284
- case style
285
- when :formatted
286
- Hash[*(
287
- @memo[:raw_stack].value['Outputs'].map do |item|
288
- [item['OutputKey'].gsub(/(?<![A-Z])([A-Z])/, '_\1').sub(/^_/, '').downcase.to_sym, item['OutputValue']]
289
- end.flatten
290
- )]
291
- when :unformatted
292
- Hash[*(
293
- @memo[:raw_stack].value['Outputs'].map do |item|
294
- [item['OutputKey'], item['OutputValue']]
295
- end.flatten
296
- )]
297
- else
298
- @memo[:raw_stack].value['Outputs']
299
- end
300
- end
301
-
302
- def event_start_index(given_events, status)
303
- Array(given_events).flatten.compact.rindex do |e|
304
- e['ResourceType'] == 'AWS::CloudFormation::Stack' &&
305
- e['ResourceStatus'] == status.to_s.upcase
306
- end.to_i
307
- end
308
-
309
- # min:: do not return value lower than this (defaults to 5)
310
- # Returns Numeric < 100 to represent completed resources
311
- # percentage (never returns less than 5)
312
- def percent_complete(min=5)
313
- if(complete?)
314
- 100
315
- else
316
- all_events = events(:all)
317
- if(all_events)
318
- total_expected = template['Resources'].size
319
- action = performing
320
- start = event_start_index(all_events, "#{action}_in_progress".to_sym)
321
- finished = all_events.find_all do |e|
322
- e['ResourceStatus'] == "#{action}_complete".upcase ||
323
- e['ResourceStatus'] == "#{action}_failed".upcase
324
- end.size
325
- calculated = ((finished / total_expected.to_f) * 100).to_i
326
- calculated < min ? min : calculated
327
- else
328
- 100 # Assume deletion and no events == complete
329
- end
330
- end
331
- end
332
-
333
- ## State ##
334
-
335
- def in_progress?
336
- status.to_s.downcase.end_with?('in_progress')
337
- end
338
-
339
- def complete?
340
- stat = status.to_s.downcase
341
- stat.end_with?('complete') || stat.end_with?('failed')
342
- end
343
-
344
- def failed?
345
- stat = status.to_s.downcase
346
- stat.end_with?('failed') || (stat.include?('rollback') && stat.end_with?('complete'))
347
- end
348
-
349
- def success?
350
- !failed? && complete?
351
- end
352
-
353
- def creating?
354
- in_progress? && status.to_s.downcase.start_with?('create')
355
- end
356
-
357
- def deleting?
358
- in_progress? && status.to_s.downcase.start_with?('delete')
359
- end
360
-
361
- def updating?
362
- in_progress? && status.to_s.downcase.start_with?('update')
363
- end
364
-
365
- def rollbacking?
366
- in_progress? && status.to_s.downcase.start_with?('rollback')
367
- end
368
-
369
- def performing
370
- if(in_progress?)
371
- status.to_s.downcase.split('_').first.to_sym
372
- end
373
- end
374
-
375
- # Lets build in some color coding!
376
- def red?
377
- failed? || deleting?
378
- end
379
-
380
- def yellow?
381
- !red? && !green?
382
- end
383
-
384
- def green?
385
- success? || creating? || updating?
386
- end
387
-
388
- ## Fog instance helpers ##
389
-
390
- RESOURCE_FILTER_KEYS = {
391
- :auto_scaling_group => 'AutoScalingGroupNames'
392
- }
393
-
394
- def expand_resource(resource)
395
- kind = resource['ResourceType'].split('::')[1]
396
- kind_snake = snake(kind)
397
- aws = common.aws(kind_snake)
398
- aws.send("#{snake(resource['ResourceType'].split('::').last).to_s.split('_').last}s").get(resource['PhysicalResourceId'])
399
- end
400
-
401
- def nodes
402
- if(@local[:nodes].empty?)
403
- as_resources = resources.find_all do |r|
404
- r['ResourceType'] == 'AWS::AutoScaling::AutoScalingGroup'
405
- end
406
- value = as_resources.map do |as_resource|
407
- as_group = expand_resource(as_resource)
408
- as_group.instances.map do |inst|
409
- common.aws(:ec2).servers.get(inst.id)
410
- end
411
- end.flatten
412
- @local[:nodes] = value unless in_progress?
413
- end
414
- value || @local[:nodes]
415
- end
416
-
417
- def nodes_data(*args)
418
- cache_key = ['nd', name, Digest::SHA256.hexdigest(args.map(&:to_s).join)].join('_')
419
- @memo.init(cache_key, :value)
420
- unless(@memo[cache_key].value)
421
- data = nodes.map do |n|
422
- [:id, args].flatten.compact.map do |k|
423
- n.send(k)
424
- end
425
- end
426
- end
427
- unless(data.empty?)
428
- @memo[cache_key].value = data unless in_progress?
429
- end
430
- data || @memo[cache_key].value
431
- end
432
-
433
- end
434
- end
435
- end