knife-cloudformation 0.2.24 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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