knife-cloudformation 0.0.1

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