knife-cloudformation 0.1.14 → 0.1.16
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.
- data/CHANGELOG.md +5 -0
- data/knife-cloudformation.gemspec +1 -0
- data/lib/chef/knife/cloudformation_create.rb +19 -7
- data/lib/chef/knife/cloudformation_describe.rb +1 -1
- data/lib/chef/knife/cloudformation_destroy.rb +2 -2
- data/lib/chef/knife/cloudformation_events.rb +1 -1
- data/lib/chef/knife/cloudformation_export.rb +79 -0
- data/lib/chef/knife/cloudformation_import.rb +35 -0
- data/lib/chef/knife/cloudformation_inspect.rb +1 -1
- data/lib/chef/knife/cloudformation_list.rb +1 -1
- data/lib/chef/knife/cloudformation_update.rb +1 -1
- data/lib/knife-cloudformation/aws_commons/stack.rb +1 -1
- data/lib/knife-cloudformation/aws_commons.rb +5 -2
- data/lib/knife-cloudformation/export.rb +173 -0
- data/lib/knife-cloudformation/version.rb +1 -1
- metadata +22 -3
- /data/lib/{chef/knife → knife-cloudformation}/cloudformation_base.rb +0 -0
data/CHANGELOG.md
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.require_path = 'lib'
|
12
12
|
s.add_dependency 'chef'
|
13
13
|
s.add_dependency 'fog', '~> 1.17'
|
14
|
+
s.add_dependency 'unf'
|
14
15
|
s.add_dependency 'net-sftp'
|
15
16
|
s.add_dependency 'sparkle_formation', '~> 0.1.2'
|
16
17
|
s.add_dependency 'redis-objects'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'sparkle_formation'
|
2
2
|
require 'pathname'
|
3
|
-
require '
|
3
|
+
require 'knife-cloudformation/cloudformation_base'
|
4
4
|
|
5
5
|
class Chef
|
6
6
|
class Knife
|
@@ -126,13 +126,17 @@ class Chef
|
|
126
126
|
exit 1
|
127
127
|
end
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
129
|
+
unless(Chef::Config[:knife][:cloudformation][:template])
|
130
|
+
set_paths_and_discover_file!
|
131
|
+
unless(File.exists?(Chef::Config[:knife][:cloudformation][:file].to_s))
|
132
|
+
ui.fatal "Invalid formation file path provided: #{Chef::Config[:knife][:cloudformation][:file]}"
|
133
|
+
exit 1
|
134
|
+
end
|
133
135
|
end
|
134
136
|
|
135
|
-
if(Chef::Config[:knife][:cloudformation][:
|
137
|
+
if(Chef::Config[:knife][:cloudformation][:template])
|
138
|
+
file = Chef::Config[:knife][:cloudformation][:template]
|
139
|
+
elsif(Chef::Config[:knife][:cloudformation][:processing])
|
136
140
|
file = SparkleFormation.compile(Chef::Config[:knife][:cloudformation][:file])
|
137
141
|
else
|
138
142
|
file = _from_json(File.read(Chef::Config[:knife][:cloudformation][:file]))
|
@@ -143,7 +147,14 @@ class Chef
|
|
143
147
|
exit 1
|
144
148
|
end
|
145
149
|
ui.info "#{ui.color('Cloud Formation: ', :bold)} #{ui.color(action_type, :green)}"
|
146
|
-
|
150
|
+
stack_info = "#{ui.color('Name:', :bold)} #{name}"
|
151
|
+
if(Chef::Config[:knife][:cloudformation][:path])
|
152
|
+
stack_info << " #{ui.color('Path:', :bold)} #{Chef::Config[:knife][:cloudformation][:file]}"
|
153
|
+
if(Chef::Config[:knife][:cloudformation][:disable_processing])
|
154
|
+
stack_info << " #{ui.color('(not pre-processed)', :yellow)}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
ui.info " -> #{stack_info}"
|
147
158
|
populate_parameters!(file)
|
148
159
|
stack_def = KnifeCloudformation::AwsCommons::Stack.build_stack_definition(file, Chef::Config[:knife][:cloudformation][:options])
|
149
160
|
aws.create_stack(name, stack_def)
|
@@ -175,6 +186,7 @@ class Chef
|
|
175
186
|
if(stack['Parameters'])
|
176
187
|
Chef::Config[:knife][:cloudformation][:options][:parameters] ||= Mash.new
|
177
188
|
stack['Parameters'].each do |k,v|
|
189
|
+
next if Chef::Config[:knife][:cloudformation][:options][:parameters][k]
|
178
190
|
valid = false
|
179
191
|
until(valid)
|
180
192
|
default = Chef::Config[:knife][:cloudformation][:options][:parameters][k] || v['Default']
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'knife-cloudformation/cloudformation_base'
|
2
2
|
|
3
3
|
class Chef
|
4
4
|
class Knife
|
@@ -30,7 +30,7 @@ class Chef
|
|
30
30
|
stacks.each do |stack_name|
|
31
31
|
poll_stack(stack_name)
|
32
32
|
end
|
33
|
-
rescue Fog::AWS::CloudFormation::NotFound
|
33
|
+
rescue SystemExit, Fog::AWS::CloudFormation::NotFound
|
34
34
|
# ignore this error since this is the end result we want!
|
35
35
|
end
|
36
36
|
ui.info " -> Destroyed Cloud Formation#{plural}: #{ui.color(stacks.join(', '), :bold, :red)}"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'knife-cloudformation/cloudformation_base'
|
2
|
+
require 'knife-cloudformation/export'
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
class Knife
|
6
|
+
class CloudformationExport < Knife
|
7
|
+
|
8
|
+
include KnifeCloudformation::KnifeBase
|
9
|
+
|
10
|
+
banner 'knife cloudformation export NAME'
|
11
|
+
|
12
|
+
# TODO: Add option for s3 exports
|
13
|
+
|
14
|
+
option(:path,
|
15
|
+
:long => '--export-path PATH',
|
16
|
+
:description => 'Path to write export JSON file',
|
17
|
+
:proc => lambda{|v| Chef::Config[:knife][:cloudformation][:export_path] = v }
|
18
|
+
)
|
19
|
+
|
20
|
+
option(:ignore_parameters,
|
21
|
+
:short => '-P NAME',
|
22
|
+
:long => '--exclude-parameter NAME',
|
23
|
+
:description => 'Exclude parameter from export (can be used multiple times)',
|
24
|
+
:proc => lambda{|v|
|
25
|
+
Chef::Config[:knife][:cloudformation][:ignore_parameters] ||= []
|
26
|
+
Chef::Config[:knife][:cloudformation][:ignore_parameters].push(v).uniq!
|
27
|
+
}
|
28
|
+
)
|
29
|
+
|
30
|
+
option(:chef_environment_parameter,
|
31
|
+
:long => '--chef-environment-parameter NAME',
|
32
|
+
:description => 'Parameter used within stack to specify Chef environment',
|
33
|
+
:proc => lambda{|v|
|
34
|
+
Chef::Config[:knife][:cloudformation][:chef_environment_parameter] = v
|
35
|
+
}
|
36
|
+
)
|
37
|
+
|
38
|
+
option(:chef_popsicle,
|
39
|
+
:long => '--[no-]freeze-run-list',
|
40
|
+
:boolean => true,
|
41
|
+
:default => true,
|
42
|
+
:description => 'Freezes first run files',
|
43
|
+
:proc => lambda{|v| Chef::Config[:knife][:cloudformation][:chef_popsicle] = v }
|
44
|
+
)
|
45
|
+
|
46
|
+
def run
|
47
|
+
if(Chef::Config[:knife][:cloudformation][:export_path])
|
48
|
+
file_path = File.join(
|
49
|
+
Chef::Config[:knife][:cloudformation][:export_path], "#{name_args.first}-#{Time.now.to_i}.json"
|
50
|
+
)
|
51
|
+
end
|
52
|
+
ui.info "#{ui.color('Stack Export:', :bold)} #{name_args.first}"
|
53
|
+
if(file_path)
|
54
|
+
ui.info " - Writing to: #{file_path}"
|
55
|
+
else
|
56
|
+
ui.info " - Printing to console"
|
57
|
+
end
|
58
|
+
ui.confirm 'Perform export'
|
59
|
+
ex_opts = {}
|
60
|
+
[:chef_popsicle, :chef_environment_parameter, :ignore_parameters].each do |key|
|
61
|
+
unless(Chef::Config[:knife][:cloudformation][key].nil?)
|
62
|
+
ex_opts[key] = Chef::Config[:knife][:cloudformation][key]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
exporter = KnifeCloudformation::Export.new(name_args.first, ex_opts.merge(:aws_commons => aws))
|
66
|
+
result = exporter.export
|
67
|
+
if(file_path)
|
68
|
+
File.open(file_path, 'w') do |file|
|
69
|
+
file.puts _format_json(result)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
ui.info _format_json(result)
|
73
|
+
end
|
74
|
+
ui.info "#{ui.color('Stack export', :bold)} (#{name_args.first}): #{ui.color('complete', :green)}"
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'knife-cloudformation/cloudformation_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class CloudformationImport < Knife
|
6
|
+
|
7
|
+
include KnifeCloudformation::KnifeBase
|
8
|
+
|
9
|
+
banner 'knife cloudformation import NEW_STACK_NAME JSON_EXPORT_FILE'
|
10
|
+
|
11
|
+
def run
|
12
|
+
stack_name, json_file = name_args
|
13
|
+
ui.info "#{ui.color('Stack Import', :bold)} (#{json_file})"
|
14
|
+
if(File.exists?(json_file))
|
15
|
+
stack = _from_json(File.read(json_file))
|
16
|
+
creator = Chef::Knife::CloudformationCreate.new
|
17
|
+
creator.name_args = [stack_name]
|
18
|
+
Chef::Config[:knife][:cloudformation][:template] = stack['template_body']
|
19
|
+
Chef::Config[:knife][:cloudformation][:options] = Mash.new
|
20
|
+
Chef::Config[:knife][:cloudformation][:options][:parameters] = Mash.new
|
21
|
+
stack['parameters'].each do |k,v|
|
22
|
+
Chef::Config[:knife][:cloudformation][:options][:parameters][k] = v
|
23
|
+
end
|
24
|
+
ui.info ' - Starting creation of import'
|
25
|
+
creator.run
|
26
|
+
ui.info "#{ui.color('Stack Import', :bold)} (#{json_file}): #{ui.color('complete', :green)}"
|
27
|
+
else
|
28
|
+
ui.error "Failed to locate JSON export file (#{json_file})"
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -229,7 +229,7 @@ module KnifeCloudformation
|
|
229
229
|
def timeout_in_minutes
|
230
230
|
@memo[:raw_stack].value['TimeoutInMinutes']
|
231
231
|
end
|
232
|
-
alias_method :
|
232
|
+
alias_method :timeout, :timeout_in_minutes
|
233
233
|
|
234
234
|
def stack_id
|
235
235
|
@memo[:raw_stack].value['StackId']
|
@@ -90,6 +90,7 @@ module KnifeCloudformation
|
|
90
90
|
end
|
91
91
|
if(@memo[:stacks].update_allowed? || args[:force_refresh])
|
92
92
|
@memo[:stacks_lock].lock do
|
93
|
+
@_stacks_cached = true
|
93
94
|
@memo[:stacks].value = aws(:cloud_formation).describe_stacks.body['Stacks']
|
94
95
|
end
|
95
96
|
end
|
@@ -118,8 +119,10 @@ module KnifeCloudformation
|
|
118
119
|
[name, name.start_with?('arn:') ? name : id_from_stack_name(name)]
|
119
120
|
end.map do |name, s_id|
|
120
121
|
unless(@local[:stacks][s_id])
|
121
|
-
|
122
|
-
|
122
|
+
if(@_stacks_cached)
|
123
|
+
seed = stacks.detect do |stk|
|
124
|
+
stk['StackId'] == s_id
|
125
|
+
end
|
123
126
|
end
|
124
127
|
@local[:stacks][s_id] = Stack.new(name, self, seed)
|
125
128
|
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'knife-cloudformation/aws_commons'
|
2
|
+
require 'chef'
|
3
|
+
|
4
|
+
module KnifeCloudformation
|
5
|
+
class Export
|
6
|
+
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
:chef_popsicle => true,
|
9
|
+
:ignored_parameters => ['Environment', 'StackCreator'],
|
10
|
+
:chef_environment_parameter => 'Environment'
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_reader :stack, :stack_name, :stack_id, :options, :aws_commons
|
14
|
+
|
15
|
+
def initialize(stack_name, options={})
|
16
|
+
@stack_name = stack_name
|
17
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
18
|
+
if(aws_commons?)
|
19
|
+
@aws_commons = options[:aws_commons]
|
20
|
+
else
|
21
|
+
raise ArgumentError.new('Expecting `AwsCommons` instance but none provided!')
|
22
|
+
end
|
23
|
+
load_stack
|
24
|
+
end
|
25
|
+
|
26
|
+
def export
|
27
|
+
exported = stack.to_hash
|
28
|
+
if(chef_popsicle?)
|
29
|
+
freeze_runlists(exported)
|
30
|
+
end
|
31
|
+
remove_ignored_parameters(exported)
|
32
|
+
exported
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(*args)
|
36
|
+
m = args.first.to_s
|
37
|
+
if(m.end_with?('?') && options.has_key?(k = m.sub('?', '').to_sym))
|
38
|
+
!!options[k]
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def remove_ignored_parameters(export)
|
47
|
+
options[:ignored_parameters].each do |param|
|
48
|
+
export[:parameters].delete(param)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def chef_environment_name
|
53
|
+
if(chef_environment_parameter?)
|
54
|
+
name = stack[:parameters][options[:chef_environment_parameter]]
|
55
|
+
end
|
56
|
+
name || '_default'
|
57
|
+
end
|
58
|
+
|
59
|
+
def environment
|
60
|
+
unless(@env)
|
61
|
+
@env = Chef::Environment.load('imdev')
|
62
|
+
end
|
63
|
+
@env
|
64
|
+
end
|
65
|
+
|
66
|
+
def load_stack
|
67
|
+
@stack = AwsCommons::Stack.new(stack_name, aws_commons)
|
68
|
+
@stack_id = @stack.stack_id
|
69
|
+
@stack
|
70
|
+
end
|
71
|
+
|
72
|
+
def allowed_cookbook_version(cookbook)
|
73
|
+
restriction = environment.cookbook_versions[cookbook]
|
74
|
+
requirement = Gem::Requirement.new(restriction)
|
75
|
+
Chef::CookbookVersion.available_versions(cookbook).detect do |v|
|
76
|
+
requirement.satisfied_by?(Gem::Version.new(v))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def extract_runlist_item(item)
|
81
|
+
rl_item = item.is_a?(Chef::RunList::RunListItem) ? item : Chef::RunList::RunListItem.new(item)
|
82
|
+
static_content = Mash.new(:run_list => [])
|
83
|
+
if(rl_item.recipe?)
|
84
|
+
cookbook, recipe = rl_item.name.split('::')
|
85
|
+
peg_version = allowed_cookbook_version(cookbook)
|
86
|
+
static_content[:run_list] << "recipe[#{[cookbook, recipe || 'default'].join('::')}@#{peg_version}]"
|
87
|
+
elsif(rl_item.role?)
|
88
|
+
role = Chef::Role.load(rl_item.name)
|
89
|
+
role.run_list.each do |item|
|
90
|
+
static_content = Chef::Mixin::DeepMerge.merge(static_content, extract_runlist_item(item))
|
91
|
+
end
|
92
|
+
static_content = Chef::Mixin::DeepMerge.merge(
|
93
|
+
static_content, Chef::Mixin::DeepMerge.merge(role.default_attributes, role.override_attributes)
|
94
|
+
)
|
95
|
+
else
|
96
|
+
# dunno what this is
|
97
|
+
end
|
98
|
+
static_content
|
99
|
+
end
|
100
|
+
|
101
|
+
def unpack_and_freeze_runlist(first_run)
|
102
|
+
extracted_runlists = first_run['run_list'].map do |item|
|
103
|
+
extract_runlist_item(cf_replace(item))
|
104
|
+
end
|
105
|
+
first_run.delete('run_list')
|
106
|
+
first_run.replace(
|
107
|
+
extracted_runlists.inject(first_run) do |memo, first_run_item|
|
108
|
+
Chef::Mixin::DeepMerge.merge(memo, first_run_item)
|
109
|
+
end
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
def freeze_runlists(exported)
|
114
|
+
first_runs = locate_runlists(exported)
|
115
|
+
first_runs.each do |first_run|
|
116
|
+
unpack_and_freeze_runlist(first_run)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def locate_runlists(thing)
|
121
|
+
result = []
|
122
|
+
case thing
|
123
|
+
when Hash
|
124
|
+
if(thing['content'] && thing['content']['run_list'])
|
125
|
+
result << thing['content']
|
126
|
+
else
|
127
|
+
thing.each do |k,v|
|
128
|
+
result += locate_runlists(v)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
when Array
|
132
|
+
thing.each do |v|
|
133
|
+
result += locate_runlists(v)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
result
|
137
|
+
end
|
138
|
+
|
139
|
+
def cf_replace(hsh)
|
140
|
+
if(hsh.is_a?(Hash))
|
141
|
+
case hsh.keys.first
|
142
|
+
when 'Fn::Join'
|
143
|
+
cf_join(*hsh.values.first)
|
144
|
+
when 'Ref'
|
145
|
+
cf_ref(hsh.values.first)
|
146
|
+
else
|
147
|
+
hsh
|
148
|
+
end
|
149
|
+
else
|
150
|
+
hsh
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def cf_ref(ref_name)
|
155
|
+
if(stack.parameters.has_key?(ref_name))
|
156
|
+
stack.parameters[ref_name]
|
157
|
+
else
|
158
|
+
raise KeyError.new("No parameter found with given reference name (#{ref_name}). " <<
|
159
|
+
"Only parameter based references supported!")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def cf_join(delim, args)
|
164
|
+
args.map do |arg|
|
165
|
+
if(arg.is_a?(Hash))
|
166
|
+
cf_replace(arg)
|
167
|
+
else
|
168
|
+
arg.to_s
|
169
|
+
end
|
170
|
+
end.join(delim)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-cloudformation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.16
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chef
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '1.17'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: unf
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
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'
|
46
62
|
- !ruby/object:Gem::Dependency
|
47
63
|
name: net-sftp
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,15 +130,18 @@ extensions: []
|
|
114
130
|
extra_rdoc_files: []
|
115
131
|
files:
|
116
132
|
- lib/chef/knife/cloudformation_events.rb
|
117
|
-
- lib/chef/knife/cloudformation_base.rb
|
118
133
|
- lib/chef/knife/cloudformation_destroy.rb
|
119
134
|
- lib/chef/knife/cloudformation_describe.rb
|
135
|
+
- lib/chef/knife/cloudformation_export.rb
|
120
136
|
- lib/chef/knife/cloudformation_list.rb
|
121
137
|
- lib/chef/knife/cloudformation_create.rb
|
122
138
|
- lib/chef/knife/cloudformation_update.rb
|
123
139
|
- lib/chef/knife/cloudformation_inspect.rb
|
140
|
+
- lib/chef/knife/cloudformation_import.rb
|
124
141
|
- lib/knife-cloudformation.rb
|
142
|
+
- lib/knife-cloudformation/cloudformation_base.rb
|
125
143
|
- lib/knife-cloudformation/version.rb
|
144
|
+
- lib/knife-cloudformation/export.rb
|
126
145
|
- lib/knife-cloudformation/aws_commons.rb
|
127
146
|
- lib/knife-cloudformation/cache.rb
|
128
147
|
- lib/knife-cloudformation/utils.rb
|
File without changes
|