knife-cloudformation 0.0.1

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.
@@ -0,0 +1,2 @@
1
+ ## v0.0.1
2
+ * Initial release
@@ -0,0 +1,3 @@
1
+ ## Knife Cloud Formation
2
+
3
+ Cloud formation tooling for knife.
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
+ require 'knife-cloudformation/version'
3
+ Gem::Specification.new do |s|
4
+ s.name = 'knife-cloudformation'
5
+ s.version = KnifeCloudformation::VERSION.version
6
+ s.summary = 'Knife tooling for Cloud Formation'
7
+ s.author = 'Chris Roberts'
8
+ s.email = 'chrisroberts.code@gmail.com'
9
+ s.homepage = 'http://github.com/heavywater/knife-cloudformation'
10
+ s.description = 'Knife tooling for Cloud Formation'
11
+ s.require_path = 'lib'
12
+ s.add_dependency 'chef'
13
+ s.add_dependency 'fog', '~> 1.12.1'
14
+ s.add_dependency 'attribute_struct', '~> 0.1.2'
15
+ s.files = Dir['**/*']
16
+ end
@@ -0,0 +1,177 @@
1
+ require 'chef/knife'
2
+
3
+ class Chef
4
+ class Knife
5
+ module CloudformationDefault
6
+ class << self
7
+ def included(klass)
8
+ klass.instance_eval do
9
+ deps do
10
+ require 'fog'
11
+ Chef::Config[:knife][:cloudformation] ||= Mash.new
12
+ Chef::Config[:knife][:cloudformation][:credentials] ||= Mash.new
13
+ Chef::Config[:knife][:cloudformation][:options] ||= Mash.new
14
+ end
15
+
16
+ option(:key,
17
+ :short => '-K KEY',
18
+ :long => '--key KEY',
19
+ :description => 'AWS access key id',
20
+ :proc => lambda {|val|
21
+ Chef::Config[:knife][:cloudformation][:credentials][:key] = val
22
+ }
23
+ )
24
+ option(:secret,
25
+ :short => '-S SECRET',
26
+ :long => '--secret SECRET',
27
+ :description => 'AWS secret access key',
28
+ :proc => lambda {|val|
29
+ Chef::Config[:knife][:cloudformation][:credentials][:secret] = val
30
+ }
31
+ )
32
+ option(:region,
33
+ :short => '-r REGION',
34
+ :long => '--region REGION',
35
+ :description => 'AWS region',
36
+ :proc => lambda {|val|
37
+ Chef::Config[:knife][:cloudformation][:credentials][:region] = val
38
+ }
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ class CloudformationBase < Knife
46
+
47
+ class << self
48
+ def aws_con
49
+ @connection ||= Fog::AWS::CloudFormation.new(
50
+ :aws_access_key_id => _key,
51
+ :aws_secret_access_key => _secret,
52
+ :region => _region
53
+ )
54
+ end
55
+
56
+ def _key
57
+ Chef::Config[:knife][:cloudformation][:credentials][:key] ||
58
+ Chef::Config[:knife][:aws_access_key_id]
59
+ end
60
+
61
+ def _secret
62
+ Chef::Config[:knife][:cloudformation][:credentials][:secret] ||
63
+ Chef::Config[:knife][:aws_secret_access_key]
64
+ end
65
+
66
+ def _region
67
+ Chef::Config[:knife][:cloudformation][:credentials][:region] ||
68
+ Chef::Config[:knife][:region]
69
+ end
70
+
71
+ end
72
+
73
+ def _debug(e, *args)
74
+ if(ENV['DEBUG'])
75
+ ui.fatal "Exception information: #{e.class}: #{e}\n#{e.backtrace.join("\n")}\n"
76
+ args.each do |string|
77
+ ui.fatal string
78
+ end
79
+ end
80
+ end
81
+
82
+ def aws_con
83
+ self.class.aws_con
84
+ end
85
+
86
+ def stack_status(name)
87
+ aws_con.describe_stacks('StackName' => name).body['Stacks'].first['StackStatus']
88
+ end
89
+
90
+ def get_titles(thing, format=false)
91
+ unless(@titles)
92
+ hash = thing.is_a?(Array) ? thing.first : thing
93
+ hash ||= {}
94
+ @titles = hash.keys.map do |key|
95
+ next unless attribute_allowed?(key)
96
+ key.gsub(/([a-z])([A-Z])/, '\1 \2')
97
+ end.compact
98
+ end
99
+ if(format)
100
+ @titles.map{|s| ui.color(s, :bold)}
101
+ else
102
+ @titles
103
+ end
104
+ end
105
+
106
+ def process(things)
107
+ @event_ids ||= []
108
+ things.reverse.map do |thing|
109
+ next if @event_ids.include?(thing['EventId'])
110
+ @event_ids.push(thing['EventId']).compact!
111
+ get_titles(thing).map do |key|
112
+ thing[key.gsub(' ', '')].to_s
113
+ end
114
+ end.flatten.compact
115
+ end
116
+
117
+ def allowed_attributes
118
+ Chef::Config[:knife][:cloudformation][:attributes] || default_attributes
119
+ end
120
+
121
+ def default_attributes
122
+ %w(Timestamp StackName StackId)
123
+ end
124
+
125
+ def attribute_allowed?(attr)
126
+ config[:all_attributes] || allowed_attributes.include?(attr)
127
+ end
128
+
129
+ def things_output(stack, things, what)
130
+ output = get_titles(things, :format)
131
+ output += process(things)
132
+ output.compact.flatten
133
+ if(output.empty?)
134
+ ui.warn 'No information found'
135
+ else
136
+ ui.info "#{what.to_s.capitalize} for stack: #{ui.color(stack, :bold)}" if stack
137
+ ui.info "#{ui.list(output, :uneven_columns_across, get_titles(things).size)}\n"
138
+ end
139
+ end
140
+
141
+ def get_things(stack=nil, message=nil)
142
+ begin
143
+ yield
144
+ rescue => e
145
+ ui.fatal "#{message || 'Failed to retrieve information'}#{" for requested stack: #{stack}" if stack}"
146
+ ui.fatal "Reason: #{e}"
147
+ _debug(e)
148
+ exit 1
149
+ end
150
+ end
151
+
152
+ def try_json_compat
153
+ begin
154
+ require 'chef/json_compat'
155
+ rescue
156
+ end
157
+ defined?(Chef::JSONCompat)
158
+ end
159
+
160
+ def _to_json(thing)
161
+ if(try_json_compat)
162
+ Chef::JSONCompat.to_json(thing)
163
+ else
164
+ JSON.dump(thing)
165
+ end
166
+ end
167
+
168
+ def _from_json(thing)
169
+ if(try_json_compat)
170
+ Chef::JSONCompat.from_json(thing)
171
+ else
172
+ JSON.read(thing)
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,178 @@
1
+ require 'knife-cloudformation/sparkle_formation'
2
+ require 'chef/knife/cloudformation_base'
3
+
4
+ class Chef
5
+ class Knife
6
+ class CloudformationCreate < CloudformationBase
7
+
8
+ banner 'knife cloudformation create NAME'
9
+
10
+ include CloudformationDefault
11
+
12
+ option(:parameter,
13
+ :short => '-p KEY:VALUE',
14
+ :long => '--parameter KEY:VALUE',
15
+ :description => 'Set parameter. Can be used multiple times.',
16
+ :proc => lambda {|val|
17
+ key,value = val.split(':')
18
+ Chef::Config[:knife][:cloudformation][:options][:parameters] ||= Mash.new
19
+ Chef::Config[:knife][:cloudformation][:options][:parameters][key] = value
20
+ }
21
+ )
22
+ option(:timeout,
23
+ :short => '-t MIN',
24
+ :long => '--timeout MIN',
25
+ :description => 'Set timeout for stack creation',
26
+ :proc => lambda {|val|
27
+ Chef::Config[:knife][:cloudformation][:options][:timeout_in_minutes] = val
28
+ }
29
+ )
30
+ option(:disable_rollback,
31
+ :short => '-R',
32
+ :long => '--disable-rollback',
33
+ :description => 'Disable rollback on stack creation failure',
34
+ :proc => lambda { Chef::Config[:knife][:cloudformation][:options][:disable_rollback] = true }
35
+ )
36
+ option(:capability,
37
+ :short => '-C CAPABILITY',
38
+ :long => '--capability CAPABILITY',
39
+ :description => 'Specify allowed capabilities. Can be used multiple times.',
40
+ :proc => lambda {|val|
41
+ Chef::Config[:knife][:cloudformation][:options][:capabilities] ||= []
42
+ Chef::Config[:knife][:cloudformation][:options][:capabilities].push(val).uniq!
43
+ }
44
+ )
45
+ option(:enable_processing,
46
+ :long => '--enable-processing',
47
+ :description => 'Call the unicorns.',
48
+ :proc => lambda { Chef::Config[:knife][:cloudformation][:options][:enable_processing] = true }
49
+ )
50
+ option(:disable_polling,
51
+ :long => '--disable-polling',
52
+ :description => 'Disable stack even polling.',
53
+ :proc => lambda { Chef::Config[:knife][:cloudformation][:options][:disable_polling] = true }
54
+ )
55
+ option(:notifications,
56
+ :long => '--notification ARN',
57
+ :description => 'Add notification ARN. Can be used multiple times.',
58
+ :proc => lambda {|val|
59
+ Chef::Config[:knife][:cloudformation][:options][:notification_ARNs] ||= []
60
+ Chef::Config[:knife][:cloudformation][:options][:notification_ARNs].push(val).uniq!
61
+ }
62
+ )
63
+ option(:file,
64
+ :short => '-F PATH',
65
+ :long => '--file PATH',
66
+ :description => 'Path to Cloud Formation to process',
67
+ :proc => lambda {|val|
68
+ Chef::Config[:knife][:cloudformation][:file] = val
69
+ }
70
+ )
71
+ option(:disable_interactive_parameters,
72
+ :long => '--no-parameter-prompts',
73
+ :description => 'Do not prompt for input on dynamic parameters'
74
+ )
75
+
76
+ def run
77
+ unless(File.exists?(Chef::Config[:knife][:cloudformation][:file].to_s))
78
+ ui.fatal "Invalid formation file path provided: #{Chef::Config[:knife][:cloudformation][:file]}"
79
+ exit 1
80
+ end
81
+ name = name_args.first
82
+ if(Chef::Config[:knife][:cloudformation][:enable_processing])
83
+ file = KnifeCloudformation::SparkleFormation.compile(Chef::Config[:knife][:cloudformation][:file])
84
+ else
85
+ file = File.read(Chef::Config[:knife][:cloudformation][:file])
86
+ end
87
+ ui.info "#{ui.color('Cloud Formation: ', :bold)} #{ui.color('CREATE', :green)}"
88
+ ui.info " -> #{ui.color('Name:', :bold)} #{name} #{ui.color('Path:', :bold)} #{Chef::Config[:knife][:cloudformation][:file]} #{ui.color('(not pre-processed)', :yellow) if Chef::Config[:knife][:cloudformation][:disable_processing]}"
89
+ stack = build_stack(file)
90
+ create_stack(name, stack)
91
+ unless(Chef::Config[:knife][:cloudformation][:disable_polling])
92
+ poll_stack(name)
93
+ else
94
+ ui.warn 'Stack state polling has been disabled.'
95
+ end
96
+ ui.info "Stack creation complete: #{ui.color('SUCCESS', :green)}"
97
+ end
98
+
99
+ def build_stack(template)
100
+ stack = Mash.new
101
+ Chef::Config[:knife][:cloudformation][:options].each do |key, value|
102
+ format_key = key.split('_').map(&:capitalize).join
103
+ case value
104
+ when Hash
105
+ i = 1
106
+ value.each do |k, v|
107
+ stack["#{format_key}.member.#{i}.#{format_key[0, (format_key.length - 1)]}Key"] = k
108
+ stack["#{format_key}.member.#{i}.#{format_key[0, (format_key.length - 1)]}Value"] = v
109
+ end
110
+ when Array
111
+ value.each_with_index do |v, i|
112
+ stack["#{format_key}.member.#{i+1}"] = v
113
+ end
114
+ else
115
+ stack[format_key] = value
116
+ end
117
+ end
118
+ populate_parameters!(template)
119
+ stack['TemplateBody'] = Chef::JSONCompat.to_json(template)
120
+ stack
121
+ end
122
+
123
+ def populate_parameters!(stack)
124
+ unless(config[:disable_interactive_parameters])
125
+ if(stack['Parameters'])
126
+ Chef::Config[:knife][:cloudformation][:options][:parameters] ||= Mash.new
127
+ stack['Parameters'].each do |k,v|
128
+ valid = false
129
+ until(valid)
130
+ default = Chef::Config[:knife][:cloudformation][:options][:parameters][k] || v['Default']
131
+ answer = ui.ask_question("#{k.split(/([A-Z]+[^A-Z]*)/).find_all{|s|!s.empty?}.join(' ')} ", :default => default)
132
+ if(v['AllowedValues'])
133
+ valid = v['AllowedValues'].include?(answer)
134
+ else
135
+ valid = true
136
+ end
137
+ if(valid)
138
+ Chef::Config[:knife][:cloudformation][:options][:parameters][k] = answer
139
+ else
140
+ ui.error "Not an allowed value: #{v['AllowedValues'].join(', ')}"
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ def create_stack(name, stack)
149
+ begin
150
+ res = aws_con.create_stack(name, stack)
151
+ rescue => e
152
+ ui.fatal "Failed to create stack #{name}. Reason: #{e}"
153
+ _debug(e, "Generated template used:\n#{stack.inspect}")
154
+ exit 1
155
+ end
156
+ end
157
+
158
+ def poll_stack(name)
159
+ knife_events = Chef::Knife::CloudformationEvents.new
160
+ knife_events.name_args.push(name)
161
+ Chef::Config[:knife][:cloudformation][:poll] = true
162
+ knife_events.run
163
+ unless(create_successful?(name))
164
+ ui.fatal "Creation of new stack #{ui.color(name, :bold)}: #{ui.color('FAILED', :red, :bold)}"
165
+ exit 1
166
+ end
167
+ end
168
+
169
+ def create_in_progress?(name)
170
+ stack_status(name) == 'CREATE_IN_PROGRESS'
171
+ end
172
+
173
+ def create_successful?(name)
174
+ stack_status(name) == 'CREATE_COMPLETE'
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,57 @@
1
+ require 'chef/knife/cloudformation_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class CloudformationDescribe < CloudformationBase
6
+ include CloudformationDefault
7
+
8
+ banner 'knife cloudformation describe NAME'
9
+
10
+ option(:resources,
11
+ :short => '-r',
12
+ :long => '--resources',
13
+ :description => 'Display resources for stack(s)'
14
+ )
15
+
16
+ option(:attribute,
17
+ :short => '-a ATTR',
18
+ :long => '--attribute ATTR',
19
+ :description => 'Attribute to print. Can be used multiple times.',
20
+ :proc => lambda {|val|
21
+ Chef::Config[:knife][:cloudformation][:attributes] ||= []
22
+ Chef::Config[:knife][:cloudformation][:attributes].push(val).uniq!
23
+ }
24
+ )
25
+
26
+ option(:all,
27
+ :long => '--all-attributes',
28
+ :description => 'Print all attributes'
29
+ )
30
+
31
+ def run
32
+ name_args.each do |stack|
33
+ things_output(stack,
34
+ *(config[:resources] ? [get_resources(stack), :resources] : [get_description(stack), :description])
35
+ )
36
+ end
37
+ end
38
+
39
+ def default_attributes
40
+ config[:resources] ? %w(Timestamp ResourceType ResourceStatus) : %w(StackName CreationTime StackStatus DisableRollback)
41
+ end
42
+
43
+ def get_description(stack)
44
+ get_things(stack) do
45
+ [aws_con.describe_stacks('StackName' => stack).body['Stacks'].first]
46
+ end
47
+ end
48
+
49
+ def get_resources(stack)
50
+ get_things(stack) do
51
+ aws_con.describe_stack_resources('StackName' => stack).body['StackResources']
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,28 @@
1
+ require 'chef/knife/cloudformation_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class CloudformationDestroy < CloudformationBase
6
+
7
+ include CloudformationDefault
8
+
9
+ banner 'knife cloudformation destroy NAME[ NAME ...]'
10
+
11
+ def run
12
+ name_args.each do |stack_name|
13
+ ui.warn "Destroying Cloud Formation: #{ui.color(stack_name, :bold)}"
14
+ ui.confirm 'Destroy this formation'
15
+ destroy_formation!(stack_name)
16
+ ui.info " -> Destroyed Cloud Formation: #{ui.color(stack_name, :bold, :red)}"
17
+ end
18
+ end
19
+
20
+ def destroy_formation!(stack_name)
21
+ get_things(stack_name, 'Failed to perform destruction') do
22
+ aws_con.delete_stack(stack_name)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+ require 'chef/knife/cloudformation_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class CloudformationEvents < CloudformationBase
6
+
7
+ banner 'knife cloudformation events NAME'
8
+
9
+ include CloudformationDefault
10
+
11
+ option(:poll,
12
+ :short => '-p',
13
+ :long => '--poll',
14
+ :description => 'Poll events while stack status is "in progress"',
15
+ :proc => lambda {|v| Chef::Config[:knife][:cloudformation][:poll] = true }
16
+ )
17
+
18
+ option(:attribute,
19
+ :short => '-a ATTR',
20
+ :long => '--attribute ATTR',
21
+ :description => 'Attribute to print. Can be used multiple times.',
22
+ :proc => lambda {|val|
23
+ Chef::Config[:knife][:cloudformation][:attributes] ||= []
24
+ Chef::Config[:knife][:cloudformation][:attributes].push(val).uniq!
25
+ }
26
+ )
27
+
28
+ option(:all_attributes,
29
+ :long => '--all-attributes',
30
+ :description => 'Print all attributes'
31
+ )
32
+
33
+ def run
34
+ name = name_args.first
35
+ ui.info "Cloud Formation Events for Stack: #{ui.color(name, :bold)}\n"
36
+ events = stack_events(name)
37
+ output = get_titles(events, :format)
38
+ output += process(events)
39
+ ui.info ui.list(output.flatten, :uneven_columns_across, allowed_attributes.size)
40
+ if(Chef::Config[:knife][:cloudformation][:poll])
41
+ poll_stack(name)
42
+ end
43
+ end
44
+
45
+ def stack_events(name, continue_from_last=true)
46
+ get_things(name) do
47
+ @_stack_events ||= Mash.new
48
+ if(@_stack_events[name])
49
+ options = {'NextToken' => @_stack_events[name]}
50
+ else
51
+ options = {}
52
+ end
53
+ res = aws_con.describe_stack_events(name, options)
54
+ @_stack_events[name] = res.body['StackToken']
55
+ res.body['StackEvents']
56
+ end
57
+ end
58
+
59
+ def poll_stack(name)
60
+ while(stack_in_progress?(name))
61
+ events = stack_events(name)
62
+ output = process(events)
63
+ unless(output.empty?)
64
+ ui.info ui.list(output, :uneven_columns_across, allowed_attributes.size)
65
+ end
66
+ sleep(1)
67
+ end
68
+ # One more to see completion
69
+ events = stack_events(name)
70
+ output = process(events)
71
+ unless(output.empty?)
72
+ ui.info ui.list(output, :uneven_columns_across, allowed_attributes.size)
73
+ end
74
+ end
75
+
76
+ def stack_in_progress?(name)
77
+ stack_status(name).downcase.include?('in_progress')
78
+ end
79
+
80
+ def default_attributes
81
+ %w(Timestamp LogicalResourceId ResourceType ResourceStatus)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,63 @@
1
+ require 'chef/knife/cloudformation_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class CloudformationList < CloudformationBase
6
+ include CloudformationDefault
7
+
8
+ banner 'knife cloudformation list NAME'
9
+
10
+ option(:attribute,
11
+ :short => '-a ATTR',
12
+ :long => '--attribute ATTR',
13
+ :description => 'Attribute to print. Can be used multiple times.',
14
+ :proc => lambda {|val|
15
+ Chef::Config[:knife][:cloudformation][:attributes] ||= []
16
+ Chef::Config[:knife][:cloudformation][:attributes].push(val).uniq!
17
+ }
18
+ )
19
+
20
+ option(:all,
21
+ :long => '--all-attributes',
22
+ :description => 'Print all attributes'
23
+ )
24
+
25
+ option(:status,
26
+ :short => '-S STATUS',
27
+ :long => '--status STATUS',
28
+ :description => 'Match given status. Use "none" to disable. Can be used multiple times.',
29
+ :proc => lambda {|val|
30
+ Chef::Config[:knife][:cloudformation][:status] ||= []
31
+ Chef::Config[:knife][:cloudformation][:status].push(val).uniq!
32
+ }
33
+ )
34
+
35
+ def run
36
+ things_output(nil, get_list, nil)
37
+ end
38
+
39
+ def get_list
40
+ get_things do
41
+ aws_con.list_stacks(aws_filter_hash).body['StackSummaries']
42
+ end
43
+ end
44
+
45
+ def default_attributes
46
+ %w(StackName CreationTime StackStatus TemplateDescription)
47
+ end
48
+
49
+ def status_filter
50
+ val = Chef::Config[:knife][:cloudformation][:status] || %w(CREATE_COMPLETE CREATE_IN_PROGRESS UPDATE_IN_PROGRESS UPDATE_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_COMPLETE)
51
+ val.map(&:downcase).include?('none') ? [] : val.map(&:upcase)
52
+ end
53
+
54
+ def aws_filter_hash
55
+ hash = Mash.new
56
+ status_filter.each_with_index do |filter, i|
57
+ hash["StackStatusFilter.member.#{i+1}"] = filter
58
+ end
59
+ hash
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1 @@
1
+ require 'knife-cloudformation/version'
@@ -0,0 +1,70 @@
1
+ require 'chef/mash'
2
+ require 'attribute_struct'
3
+
4
+ AttributeStruct.camel_keys = true
5
+
6
+ module KnifeCloudformation
7
+ class SparkleFormation
8
+ class << self
9
+ def compile(path)
10
+ formation = self.instance_eval(IO.read(path), path, 1)
11
+ formation.compile._dump
12
+ end
13
+
14
+ def build(&block)
15
+ struct = AttributeStruct.new
16
+ struct.instance_exec(&block)
17
+ struct
18
+ end
19
+
20
+ def load_component(path)
21
+ self.instance_eval(IO.read(path), path, 1)
22
+ end
23
+
24
+ end
25
+
26
+ attr_reader :name
27
+ attr_reader :sparkle_path
28
+ attr_reader :components
29
+ attr_reader :load_order
30
+
31
+ def initialize(name, options={})
32
+ @name = name
33
+ @sparkle_path = options[:sparkle_path] || File.join(Dir.pwd, 'cloudformation/components')
34
+ @components = Mash.new
35
+ @load_order = []
36
+ end
37
+
38
+ def load(*args)
39
+ args.each do |thing|
40
+ if(thing.is_a?(Symbol))
41
+ path = File.join(sparkle_path, "#{thing}.rb")
42
+ else
43
+ path = thing
44
+ end
45
+ key = File.basename(path).sub('.rb', '')
46
+ components[key] = self.class.load_component(path)
47
+ @load_order << key
48
+ end
49
+ self
50
+ end
51
+
52
+ def overrides(&block)
53
+ @overrides = self.class.build(&block)
54
+ self
55
+ end
56
+
57
+ # Returns compiled Mash instance
58
+ def compile
59
+ compiled = AttributeStruct.new
60
+ @load_order.each do |key|
61
+ compiled._merge!(components[key])
62
+ end
63
+ if(@overrides)
64
+ compiled._merge!(@overrides)
65
+ end
66
+ compiled
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,6 @@
1
+ module KnifeCloudformation
2
+ class Version < Gem::Version
3
+ end
4
+
5
+ VERSION = Version.new('0.0.1')
6
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-cloudformation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Chris Roberts
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: fog
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.12.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.12.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: attribute_struct
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.1.2
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.2
62
+ description: Knife tooling for Cloud Formation
63
+ email: chrisroberts.code@gmail.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - lib/chef/knife/cloudformation_events.rb
69
+ - lib/chef/knife/cloudformation_base.rb
70
+ - lib/chef/knife/cloudformation_destroy.rb
71
+ - lib/chef/knife/cloudformation_describe.rb
72
+ - lib/chef/knife/cloudformation_list.rb
73
+ - lib/chef/knife/cloudformation_create.rb
74
+ - lib/knife-cloudformation.rb
75
+ - lib/knife-cloudformation/version.rb
76
+ - lib/knife-cloudformation/sparkle_formation.rb
77
+ - README.md
78
+ - knife-cloudformation.gemspec
79
+ - CHANGELOG.md
80
+ homepage: http://github.com/heavywater/knife-cloudformation
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.24
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Knife tooling for Cloud Formation
104
+ test_files: []
105
+ has_rdoc: