knife-cloudformation 0.2.24 → 0.5.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +16 -0
  4. data/knife-cloudformation.gemspec +14 -4
  5. data/lib/knife-cloudformation.rb +0 -28
  6. data/lib/knife-cloudformation/version.rb +1 -1
  7. metadata +18 -80
  8. data/lib/chef/knife/cloudformation_create.rb +0 -147
  9. data/lib/chef/knife/cloudformation_describe.rb +0 -99
  10. data/lib/chef/knife/cloudformation_destroy.rb +0 -84
  11. data/lib/chef/knife/cloudformation_events.rb +0 -117
  12. data/lib/chef/knife/cloudformation_export.rb +0 -162
  13. data/lib/chef/knife/cloudformation_import.rb +0 -141
  14. data/lib/chef/knife/cloudformation_inspect.rb +0 -206
  15. data/lib/chef/knife/cloudformation_list.rb +0 -72
  16. data/lib/chef/knife/cloudformation_promote.rb +0 -40
  17. data/lib/chef/knife/cloudformation_update.rb +0 -137
  18. data/lib/chef/knife/cloudformation_validate.rb +0 -36
  19. data/lib/knife-cloudformation/cache.rb +0 -385
  20. data/lib/knife-cloudformation/knife.rb +0 -9
  21. data/lib/knife-cloudformation/knife/base.rb +0 -195
  22. data/lib/knife-cloudformation/knife/stack.rb +0 -197
  23. data/lib/knife-cloudformation/knife/template.rb +0 -213
  24. data/lib/knife-cloudformation/monkey_patch.rb +0 -8
  25. data/lib/knife-cloudformation/monkey_patch/stack.rb +0 -195
  26. data/lib/knife-cloudformation/provider.rb +0 -225
  27. data/lib/knife-cloudformation/utils.rb +0 -24
  28. data/lib/knife-cloudformation/utils/animal_strings.rb +0 -28
  29. data/lib/knife-cloudformation/utils/debug.rb +0 -31
  30. data/lib/knife-cloudformation/utils/json.rb +0 -64
  31. data/lib/knife-cloudformation/utils/object_storage.rb +0 -28
  32. data/lib/knife-cloudformation/utils/output.rb +0 -79
  33. data/lib/knife-cloudformation/utils/path_selector.rb +0 -99
  34. data/lib/knife-cloudformation/utils/ssher.rb +0 -29
  35. data/lib/knife-cloudformation/utils/stack_exporter.rb +0 -271
  36. data/lib/knife-cloudformation/utils/stack_parameter_scrubber.rb +0 -37
  37. data/lib/knife-cloudformation/utils/stack_parameter_validator.rb +0 -124
@@ -1,36 +0,0 @@
1
- require 'pathname'
2
- require 'sparkle_formation'
3
- require 'knife-cloudformation'
4
-
5
- class Chef
6
- class Knife
7
- # Cloudformation validate command
8
- class CloudformationValidate < Knife
9
-
10
- include KnifeCloudformation::Knife::Base
11
- include KnifeCloudformation::Knife::Template
12
-
13
- banner 'knife cloudformation validate'
14
-
15
- def _run
16
- file = load_template_file
17
- file.delete('sfn_nested_stack')
18
- ui.info "#{ui.color('Cloud Formation Validation: ', :bold)} #{Chef::Config[:knife][:cloudformation][:file].sub(Dir.pwd, '').sub(%r{^/}, '')}"
19
- file = KnifeCloudformation::Utils::StackParameterScrubber.scrub!(file)
20
- file = translate_template(file)
21
- begin
22
- result = provider.connection.stacks.build(
23
- :name => 'validation-stack',
24
- :template => file
25
- ).validate
26
- ui.info ui.color(' -> VALID', :bold, :green)
27
- rescue => e
28
- ui.info ui.color(' -> INVALID', :bold, :red)
29
- ui.fatal e.message
30
- failed = true
31
- end
32
- end
33
-
34
- end
35
- end
36
- end
@@ -1,385 +0,0 @@
1
- require 'digest/sha2'
2
- require 'thread'
3
- require 'knife-cloudformation'
4
-
5
- module KnifeCloudformation
6
- # Data caching helper
7
- class Cache
8
-
9
- class << self
10
-
11
- # Configure the caching approach to use
12
- #
13
- # @param type [Symbol] :redis or :local
14
- # @param args [Hash] redis connection arguments if used
15
- def configure(type, args={})
16
- type = type.to_sym
17
- case type
18
- when :redis
19
- begin
20
- require 'redis-objects'
21
- rescue LoadError
22
- $stderr.puts 'The `redis-objects` gem is required for Cache support!'
23
- raise
24
- end
25
- @_pid = Process.pid
26
- Redis::Objects.redis = Redis.new(args)
27
- when :local
28
- else
29
- raise TypeError.new("Unsupported caching type: #{type}")
30
- end
31
- enable(type)
32
- end
33
-
34
- # Set enabled caching type
35
- #
36
- # @param type [Symbol]
37
- # @return [Symbol]
38
- def enable(type)
39
- @type = type.to_sym
40
- end
41
-
42
- # @return [Symbol] type of caching enabled
43
- def type
44
- @type || :local
45
- end
46
-
47
- # Set/get time limit on data type
48
- #
49
- # @param kind [String, Symbol] data type
50
- # @param seconds [Integer]
51
- # return [Integer] seconds
52
- def apply_limit(kind, seconds=nil)
53
- @apply_limit ||= {}
54
- if(seconds)
55
- @apply_limit[kind.to_sym] = seconds.to_i
56
- end
57
- @apply_limit[kind.to_sym].to_i
58
- end
59
-
60
- # @return [Hash] default limits
61
- def default_limits
62
- (@apply_limit || {}).dup
63
- end
64
-
65
- # Ping the redis connection and reconnect if dead
66
- def redis_ping!
67
- if((@_pid && @_pid != Process.pid) || !Redis::Objects.redis.connected?)
68
- Redis::Objects.redis.client.reconnect
69
- @_pid = Process.pid
70
- end
71
- end
72
-
73
- end
74
-
75
- # @return [String] custom key for this cache
76
- attr_reader :key
77
-
78
- # Create new instance
79
- #
80
- # @param key [String, Array]
81
- def initialize(key)
82
- if(key.respond_to?(:sort))
83
- key = key.flatten if key.respond_to?(:flatten)
84
- key = key.map(&:to_s).sort
85
- end
86
- @key = Digest::SHA256.hexdigest(key.to_s)
87
- @apply_limit = self.class.default_limits
88
- end
89
-
90
- # Initialize a new data type
91
- #
92
- # @param name [Symbol] name of data
93
- # @param kind [Symbol] data type
94
- # @param args [Hash] options for data type
95
- def init(name, kind, args={})
96
- get_storage(self.class.type, kind, name, args)
97
- true
98
- end
99
-
100
- # @return [Hash] data registry
101
- def registry
102
- get_storage(self.class.type, :hash, "registry_#{key}")
103
- end
104
-
105
- # Clear data
106
- #
107
- # @param args [Symbol] list of names to delete
108
- # @return [TrueClass]
109
- # @note clears all data if no names provided
110
- def clear!(*args)
111
- internal_lock do
112
- args = registry.keys if args.empty?
113
- args.each do |key|
114
- value = self[key]
115
- if(value.respond_to?(:clear))
116
- value.clear
117
- elsif(value.respond_to?(:value))
118
- value.value = nil
119
- end
120
- registry.delete(key)
121
- end
122
- yield if block_given?
123
- end
124
- true
125
- end
126
-
127
- # Fetch item from storage
128
- #
129
- # @param store_type [Symbol]
130
- # @param data_type [Symbol]
131
- # @param name [Symbol] name of data
132
- # @param args [Hash] options for underlying storage
133
- # @return [Object]
134
- def get_storage(store_type, data_type, name, args={})
135
- full_name = "#{key}_#{name}"
136
- result = nil
137
- case store_type.to_sym
138
- when :redis
139
- result = get_redis_storage(data_type, full_name.to_s, args)
140
- when :local
141
- @_local_cache ||= {}
142
- unless(@_local_cache[full_name.to_s])
143
- @_local_cache[full_name.to_s] = get_local_storage(data_type, full_name.to_s, args)
144
- end
145
- result = @_local_cache[full_name.to_s]
146
- else
147
- raise TypeError.new("Unsupported caching storage type encountered: #{store_type}")
148
- end
149
- unless(full_name == "#{key}_registry_#{key}")
150
- registry[name.to_s] = data_type
151
- end
152
- result
153
- end
154
-
155
- # Fetch item from redis storage
156
- #
157
- # @param data_type [Symbol]
158
- # @param full_name [Symbol]
159
- # @param args [Hash]
160
- # @return [Object]
161
- def get_redis_storage(data_type, full_name, args={})
162
- self.class.redis_ping!
163
- case data_type.to_sym
164
- when :array
165
- Redis::List.new(full_name, {:marshal => true}.merge(args))
166
- when :hash
167
- Redis::HashKey.new(full_name)
168
- when :value
169
- Redis::Value.new(full_name, {:marshal => true}.merge(args))
170
- when :lock
171
- Redis::Lock.new(full_name, {:expiration => 60, :timeout => 0.1}.merge(args))
172
- when :stamped
173
- Stamped.new(full_name.sub("#{key}_", '').to_sym, get_redis_storage(:value, full_name), self)
174
- else
175
- raise TypeError.new("Unsupported caching data type encountered: #{data_type}")
176
- end
177
- end
178
-
179
- # Fetch item from local storage
180
- #
181
- # @param data_type [Symbol]
182
- # @param full_name [Symbol]
183
- # @param args [Hash]
184
- # @return [Object]
185
- # @todo make proper singleton for local storage
186
- def get_local_storage(data_type, full_name, args={})
187
- @storage ||= {}
188
- @storage[full_name] ||= case data_type.to_sym
189
- when :array
190
- []
191
- when :hash
192
- {}
193
- when :value
194
- LocalValue.new
195
- when :lock
196
- LocalLock.new(full_name, {:expiration => 60, :timeout => 0.1}.merge(args))
197
- when :stamped
198
- Stamped.new(full_name.sub("#{key}_", '').to_sym, get_local_storage(:value, full_name), self)
199
- else
200
- raise TypeError.new("Unsupported caching data type encountered: #{data_type}")
201
- end
202
- end
203
-
204
- # Execute block within internal lock
205
- #
206
- # @return [Object] result of yield
207
- # @note for internal use
208
- def internal_lock
209
- get_storage(self.class.type, :lock, :internal_access, :timeout => 20, :expiration => 120).lock do
210
- yield
211
- end
212
- end
213
-
214
- # Fetch data
215
- #
216
- # @param name [String, Symbol]
217
- # @return [Object, NilClass]
218
- def [](name)
219
- if(kind = registry[name.to_s])
220
- get_storage(self.class.type, kind, name)
221
- else
222
- nil
223
- end
224
- end
225
-
226
- # Set data
227
- #
228
- # @param key [Object]
229
- # @param val [Object]
230
- # @note this will never work, thus you should never use it
231
- def []=(key, val)
232
- raise 'Setting backend data is not allowed'
233
- end
234
-
235
- # Check if cache time has expired
236
- #
237
- # @param key [String, Symbol] value key
238
- # @param stamp [Time, Integer]
239
- # @return [TrueClass, FalseClass]
240
- def time_check_allow?(key, stamp)
241
- Time.now.to_i - stamp.to_i > apply_limit(key)
242
- end
243
-
244
- # Apply time limit for data type
245
- #
246
- # @param kind [String, Symbol] data type
247
- # @param seconds [Integer]
248
- # return [Integer]
249
- def apply_limit(kind, seconds=nil)
250
- @apply_limit ||= {}
251
- if(seconds)
252
- @apply_limit[kind.to_sym] = seconds.to_i
253
- end
254
- @apply_limit[kind.to_sym].to_i
255
- end
256
-
257
- # Perform action within lock
258
- #
259
- # @param lock_name [String, Symbol] name of lock
260
- # @param raise_on_locked [TrueClass, FalseClass] raise execption if lock wait times out
261
- # @return [Object] result of yield
262
- def locked_action(lock_name, raise_on_locked=false)
263
- begin
264
- self[lock_name].lock do
265
- yield
266
- end
267
- rescue => e
268
- if(e.class.to_s == 'Redis::Lock::LockTimeout')
269
- raise if raise_on_locked
270
- else
271
- raise
272
- end
273
- end
274
- end
275
-
276
- # Simple value for memory cache
277
- class LocalValue
278
- # @return [Object] value
279
- attr_accessor :value
280
- def initialize(*args)
281
- @value = nil
282
- end
283
- end
284
-
285
- # Simple lock for memory cache
286
- class LocalLock
287
-
288
- # @return [Symbol] key name
289
- attr_reader :_key
290
- # @return [Numeric] timeout
291
- attr_reader :_timeout
292
- # @return [Mutex] underlying lock
293
- attr_reader :_lock
294
-
295
- # Create new instance
296
- #
297
- # @param name [Symbol] name of lock
298
- # @param args [Hash]
299
- # @option args [Numeric] :timeout
300
- def initialize(name, args={})
301
- @_key = name
302
- @_timeout = args.fetch(:timeout, -1).to_f
303
- @_lock = Mutex.new
304
- end
305
-
306
- # Aquire lock and yield
307
- #
308
- # @yield block to execute within lock
309
- # @return [Object] result of yield
310
- def lock
311
- locked = false
312
- attempt_start = Time.now.to_f
313
- while(!locked && (_timeout < 0 || Time.now.to_f - attempt_start < _timeout))
314
- locked = _lock.try_lock
315
- end
316
- if(locked)
317
- begin
318
- yield
319
- ensure
320
- _lock.unlock if _lock.locked?
321
- end
322
- else
323
- raise Redis::Lock::LockTimeout.new "Timeout on lock #{_key} exceeded #{_timeout} sec"
324
- end
325
- end
326
-
327
- # Clear the lock
328
- #
329
- # @note this is a noop
330
- def clear
331
- # noop
332
- end
333
- end
334
-
335
- # Wrapper to auto stamp values
336
- class Stamped
337
-
338
- # Create new instance
339
- #
340
- # @param name [String, Symbol]
341
- # @param base [Redis::Value, LocalValue]
342
- # @param cache [Cache]
343
- def initialize(name, base, cache)
344
- @name = name.to_sym
345
- @base = base
346
- @cache = cache
347
- end
348
-
349
- # @return [Object] value stored
350
- def value
351
- @base.value[:value] if set?
352
- end
353
-
354
- # Store value and update timestamp
355
- #
356
- # @param v [Object] value
357
- # @return [Object]
358
- def value=(v)
359
- @base.value = {:stamp => Time.now.to_f, :value => v}
360
- v
361
- end
362
-
363
- # @return [TrueClass, FalseClass] is value set
364
- def set?
365
- @base.value.is_a?(Hash)
366
- end
367
-
368
- # @return [Float] timestamp of last set (or 0.0 if unset)
369
- def stamp
370
- set? ? @base.value[:stamp] : 0.0
371
- end
372
-
373
- # Force a timestamp update
374
- def restamp!
375
- self.value = value
376
- end
377
-
378
- # @return [TrueClass, FalseClass] update is allowed based on stamp and limits
379
- def update_allowed?
380
- !set? || @cache.time_check_allow?(@name, @base.value[:stamp])
381
- end
382
- end
383
-
384
- end
385
- end
@@ -1,9 +0,0 @@
1
- require 'knife-cloudformation'
2
-
3
- module KnifeCloudformation
4
- module Knife
5
- autoload :Base, 'knife-cloudformation/knife/base'
6
- autoload :Stack, 'knife-cloudformation/knife/stack'
7
- autoload :Template, 'knife-cloudformation/knife/template'
8
- end
9
- end
@@ -1,195 +0,0 @@
1
- require 'chef/knife'
2
- require 'knife-cloudformation'
3
-
4
- module KnifeCloudformation
5
- module Knife
6
- # Base to build cloudformation related knife commands
7
- module Base
8
-
9
- # Instance methods for cloudformation command classes
10
- module InstanceMethods
11
-
12
- # @return [KnifeCloudformation::Provider]
13
- def provider
14
- self.class.provider
15
- end
16
-
17
- # Write exception information if debug is enabled
18
- #
19
- # @param e [Exception]
20
- # @param args [String] extra strings to output
21
- def _debug(e, *args)
22
- if(ENV['DEBUG'])
23
- ui.fatal "Exception information: #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}\n"
24
- if(e.is_a?(Miasma::Error::ApiError))
25
- ui.fatal "Response body: #{e.response.body.to_s.inspect}"
26
- end
27
- args.each do |string|
28
- ui.fatal string
29
- end
30
- end
31
- end
32
-
33
- # Get stack
34
- #
35
- # @param name [String] name of stack
36
- # @return [Miasma::Models::Orchestration::Stack]
37
- def stack(name)
38
- provider.stacks.get(name)
39
- end
40
-
41
- # @return [Array<String>] attributes to display
42
- def allowed_attributes
43
- Chef::Config[:knife][:cloudformation][:attributes] || default_attributes
44
- end
45
-
46
- # @return [Array<String>] default attributes to display
47
- def default_attributes
48
- %w(timestamp stack_name id)
49
- end
50
-
51
- # Check if attribute is allowed for display
52
- #
53
- # @param attr [String]
54
- # @return [TrueClass, FalseClass]
55
- def attribute_allowed?(attr)
56
- config[:all_attributes] || allowed_attributes.include?(attr)
57
- end
58
-
59
- # Poll events on stack
60
- #
61
- # @param name [String] name of stack
62
- def poll_stack(name)
63
- retry_attempts = 0
64
- begin
65
- provider.connection.stacks.reload
66
- knife_events = Chef::Knife::CloudformationEvents.new
67
- knife_events.name_args.push(name)
68
- Chef::Config[:knife][:cloudformation][:poll] = true
69
- knife_events.run
70
- rescue => e
71
- if(retry_attempts < Chef::Config[:knife][:cloudformation].fetch(:max_poll_retries, 5))
72
- retry_attempts += 1
73
- warn "Unexpected error encountered (#{e.class}: #{e}) Retrying [retry count: #{retry_attempts}]"
74
- sleep(1)
75
- retry
76
- else
77
- raise
78
- end
79
- end
80
- end
81
-
82
- # Wrapper for information retrieval. Provides consistent error
83
- # message for failures
84
- #
85
- # @param stack [String] stack name
86
- # @param message [String] failure message
87
- # @yield block to wrap error handling
88
- # @return [Object] result of yield
89
- def get_things(stack=nil, message=nil)
90
- begin
91
- yield
92
- rescue => e
93
- ui.fatal "#{message || 'Failed to retrieve information'}#{" for requested stack: #{stack}" if stack}"
94
- ui.fatal "Reason: #{e}"
95
- _debug(e)
96
- exit 1
97
- end
98
- end
99
-
100
- # Disable chef configuration. Let the dep loader do that for us
101
- # so it doesn't squash config values set via options
102
- def configure_chef
103
- true
104
- end
105
-
106
- # Wrapper to allow consistent exception handling
107
- def run
108
- begin
109
- _run
110
- rescue => e
111
- ui.fatal "Unexpected Error: #{e.message}"
112
- _debug(e)
113
- exit 1
114
- end
115
- end
116
-
117
- end
118
-
119
- module ClassMethods
120
-
121
- # @return [KnifeCloudformation::Provider]
122
- def provider
123
- Thread.current[:_provider] ||= KnifeCloudformation::Provider.new(
124
- :miasma => Chef::Config[:knife][:cloudformation][:credentials],
125
- :async => false,
126
- :fetch => false
127
- )
128
- end
129
-
130
- # @return [FalseClass]
131
- def use_separate_defaults?
132
- false
133
- end
134
-
135
- end
136
-
137
- class << self
138
- def included(klass)
139
- klass.instance_eval do
140
-
141
- extend KnifeCloudformation::Knife::Base::ClassMethods
142
- include KnifeCloudformation::Knife::Base::InstanceMethods
143
- include KnifeCloudformation::Utils::JSON
144
- include KnifeCloudformation::Utils::AnimalStrings
145
- include KnifeCloudformation::Utils::Output
146
-
147
- deps do
148
- Chef::Knife.new.configure_chef
149
- require 'miasma'
150
- Chef::Config[:knife][:cloudformation] ||= Mash.new
151
- Chef::Config[:knife][:cloudformation][:credentials] ||= Mash.new
152
- Chef::Config[:knife][:cloudformation][:options] ||= Mash.new
153
- Chef::Config[:knife][:cloudformation][:ignore_parameters] = []
154
- %w(poll interactive_parameters apply_nesting).each do |key|
155
- if(Chef::Config[:knife][:cloudformation][key].nil?)
156
- Chef::Config[:knife][:cloudformation][key] = true
157
- end
158
- end
159
- end
160
-
161
- option(:credentials,
162
- :short => '-S CREDENTIALS',
163
- :long => '--credentials CREDENTIALS',
164
- :description => 'Miasma API options. Comma delimited or used multiple times. (-S "aws_access_key_id=MYKEY")',
165
- :proc => lambda {|val|
166
- val.split(',').each do |pair|
167
- key, value = pair.split('=')
168
- Chef::Config[:knife][:cloudformation][:credentials][key] = value
169
- end
170
- }
171
- )
172
-
173
- option(:ignore_parameter,
174
- :long => '--ignore-parameter PARAMETER_NAME',
175
- :description => 'Parameter to ignore during modifications (can be used multiple times)',
176
- :proc => lambda{|val| Chef::Config[:knife][:cloudformation][:ignore_parameters].push(val).uniq! }
177
- )
178
-
179
- # Populate up the hashes so they are available for knife config
180
- # with issues of nils
181
- ['knife.cloudformation.credentials', 'knife.cloudformation.options'].each do |stack|
182
- stack.split('.').inject(Chef::Config) do |memo, item|
183
- memo[item.to_sym] = Mash.new unless memo[item.to_sym]
184
- memo[item.to_sym]
185
- end
186
- end
187
-
188
- end
189
-
190
- end
191
- end
192
- end
193
-
194
- end
195
- end