knife-cloudformation 0.1.22 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/README.md +56 -2
- data/knife-cloudformation.gemspec +4 -7
- data/lib/chef/knife/cloudformation_create.rb +105 -245
- data/lib/chef/knife/cloudformation_describe.rb +50 -26
- data/lib/chef/knife/cloudformation_destroy.rb +17 -18
- data/lib/chef/knife/cloudformation_events.rb +48 -14
- data/lib/chef/knife/cloudformation_export.rb +117 -34
- data/lib/chef/knife/cloudformation_import.rb +124 -18
- data/lib/chef/knife/cloudformation_inspect.rb +159 -71
- data/lib/chef/knife/cloudformation_list.rb +20 -24
- data/lib/chef/knife/cloudformation_promote.rb +40 -0
- data/lib/chef/knife/cloudformation_update.rb +132 -15
- data/lib/chef/knife/cloudformation_validate.rb +35 -0
- data/lib/knife-cloudformation.rb +28 -0
- data/lib/knife-cloudformation/cache.rb +213 -35
- data/lib/knife-cloudformation/knife.rb +9 -0
- data/lib/knife-cloudformation/knife/base.rb +179 -0
- data/lib/knife-cloudformation/knife/stack.rb +94 -0
- data/lib/knife-cloudformation/knife/template.rb +174 -0
- data/lib/knife-cloudformation/monkey_patch.rb +8 -0
- data/lib/knife-cloudformation/monkey_patch/stack.rb +195 -0
- data/lib/knife-cloudformation/provider.rb +225 -0
- data/lib/knife-cloudformation/utils.rb +18 -98
- data/lib/knife-cloudformation/utils/animal_strings.rb +28 -0
- data/lib/knife-cloudformation/utils/debug.rb +31 -0
- data/lib/knife-cloudformation/utils/json.rb +64 -0
- data/lib/knife-cloudformation/utils/object_storage.rb +28 -0
- data/lib/knife-cloudformation/utils/output.rb +79 -0
- data/lib/knife-cloudformation/utils/path_selector.rb +99 -0
- data/lib/knife-cloudformation/utils/ssher.rb +29 -0
- data/lib/knife-cloudformation/utils/stack_exporter.rb +271 -0
- data/lib/knife-cloudformation/utils/stack_parameter_scrubber.rb +35 -0
- data/lib/knife-cloudformation/utils/stack_parameter_validator.rb +124 -0
- data/lib/knife-cloudformation/version.rb +2 -4
- metadata +47 -94
- data/Gemfile +0 -3
- data/Gemfile.lock +0 -90
- data/knife-cloudformation-0.1.20.gem +0 -0
- data/lib/knife-cloudformation/aws_commons.rb +0 -267
- data/lib/knife-cloudformation/aws_commons/stack.rb +0 -435
- data/lib/knife-cloudformation/aws_commons/stack_parameter_validator.rb +0 -79
- data/lib/knife-cloudformation/cloudformation_base.rb +0 -168
- data/lib/knife-cloudformation/export.rb +0 -174
@@ -1,79 +0,0 @@
|
|
1
|
-
require 'knife-cloudformation/aws_commons/stack'
|
2
|
-
|
3
|
-
module KnifeCloudformation
|
4
|
-
class AwsCommons
|
5
|
-
|
6
|
-
class Stack
|
7
|
-
|
8
|
-
class ParameterValidator
|
9
|
-
class << self
|
10
|
-
|
11
|
-
include KnifeCloudformation::Utils::AnimalStrings
|
12
|
-
|
13
|
-
def validate(value, parameter_definition)
|
14
|
-
return [[:blank, 'Value cannot be blank']] if value.to_s.strip.empty?
|
15
|
-
result = %w(AllowedValues AllowedPattern MaxLength MinLength MaxValue MinValue).map do |key|
|
16
|
-
if(parameter_definition[key])
|
17
|
-
res = self.send(snake(key), value, parameter_definition)
|
18
|
-
res == true ? true : [snake(key), res]
|
19
|
-
else
|
20
|
-
true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
result.delete_if{|x| x == true}
|
24
|
-
result.empty? ? true : result
|
25
|
-
end
|
26
|
-
|
27
|
-
def allowed_values(value, pdef)
|
28
|
-
if(pdef['AllowedValues'].include?(value))
|
29
|
-
true
|
30
|
-
else
|
31
|
-
"Not an allowed value: #{pdef['AllowedValues'].join(', ')}"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def allowed_pattern(value, pdef)
|
36
|
-
if(value.match(/#{pdef['AllowedPattern']}/))
|
37
|
-
true
|
38
|
-
else
|
39
|
-
"Not a valid pattern. Must match: #{pdef['AllowedPattern']}"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def max_length(value, pdef)
|
44
|
-
if(value.length <= pdef['MaxLength'].to_i)
|
45
|
-
true
|
46
|
-
else
|
47
|
-
"Value must not exceed #{pdef['MaxLength']} characters"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def min_length(value, pdef)
|
52
|
-
if(value.length >= pdef['MinLength'].to_i)
|
53
|
-
true
|
54
|
-
else
|
55
|
-
"Value must be at least #{pdef['MinLength']} characters"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def max_value(value, pdef)
|
60
|
-
if(value.to_i <= pdef['MaxValue'].to_i)
|
61
|
-
true
|
62
|
-
else
|
63
|
-
"Value must not be greater than #{pdef['MaxValue']}"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def min_value(value, pdef)
|
68
|
-
if(value.to_i >= pdef['MinValue'].to_i)
|
69
|
-
true
|
70
|
-
else
|
71
|
-
"Value must not be less than #{pdef['MinValue']}"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,168 +0,0 @@
|
|
1
|
-
require 'chef/knife'
|
2
|
-
require 'knife-cloudformation/utils'
|
3
|
-
require 'knife-cloudformation/aws_commons'
|
4
|
-
|
5
|
-
module KnifeCloudformation
|
6
|
-
|
7
|
-
module KnifeBase
|
8
|
-
|
9
|
-
module InstanceMethods
|
10
|
-
|
11
|
-
def aws
|
12
|
-
self.class.con(ui)
|
13
|
-
end
|
14
|
-
|
15
|
-
def _debug(e, *args)
|
16
|
-
if(ENV['DEBUG'])
|
17
|
-
ui.fatal "Exception information: #{e.class}: #{e}\n#{e.backtrace.join("\n")}\n"
|
18
|
-
args.each do |string|
|
19
|
-
ui.fatal string
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def stack(name)
|
25
|
-
self.class.con(ui).stack(name, :ignore_seeds)
|
26
|
-
end
|
27
|
-
|
28
|
-
def allowed_attributes
|
29
|
-
Chef::Config[:knife][:cloudformation][:attributes] || default_attributes
|
30
|
-
end
|
31
|
-
|
32
|
-
def default_attributes
|
33
|
-
%w(Timestamp StackName StackId)
|
34
|
-
end
|
35
|
-
|
36
|
-
def attribute_allowed?(attr)
|
37
|
-
config[:all_attributes] || allowed_attributes.include?(attr)
|
38
|
-
end
|
39
|
-
|
40
|
-
def poll_stack(name)
|
41
|
-
knife_events = Chef::Knife::CloudformationEvents.new
|
42
|
-
knife_events.name_args.push(name)
|
43
|
-
Chef::Config[:knife][:cloudformation][:poll] = true
|
44
|
-
knife_events.run
|
45
|
-
end
|
46
|
-
|
47
|
-
def things_output(stack, things, what, *args)
|
48
|
-
unless(args.include?(:no_title))
|
49
|
-
output = aws.get_titles(things, :format => true, :attributes => allowed_attributes)
|
50
|
-
else
|
51
|
-
output = []
|
52
|
-
end
|
53
|
-
columns = allowed_attributes.size
|
54
|
-
output += aws.process(things, :flat => true, :attributes => allowed_attributes)
|
55
|
-
output.compact.flatten
|
56
|
-
if(output.empty?)
|
57
|
-
ui.warn 'No information found' unless args.include?(:ignore_empty_output)
|
58
|
-
else
|
59
|
-
ui.info "#{what.to_s.capitalize} for stack: #{ui.color(stack, :bold)}" if stack
|
60
|
-
ui.info "#{ui.list(output, :uneven_columns_across, columns)}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def get_things(stack=nil, message=nil)
|
65
|
-
begin
|
66
|
-
yield
|
67
|
-
rescue => e
|
68
|
-
ui.fatal "#{message || 'Failed to retrieve information'}#{" for requested stack: #{stack}" if stack}"
|
69
|
-
ui.fatal "Reason: #{e}"
|
70
|
-
_debug(e)
|
71
|
-
exit 1
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
module ClassMethods
|
78
|
-
|
79
|
-
def con(ui=nil)
|
80
|
-
unless(@common)
|
81
|
-
@common = KnifeCloudformation::AwsCommons.new(
|
82
|
-
:ui => ui,
|
83
|
-
:fog => {
|
84
|
-
:aws_access_key_id => _key,
|
85
|
-
:aws_secret_access_key => _secret,
|
86
|
-
:region => _region
|
87
|
-
}
|
88
|
-
)
|
89
|
-
end
|
90
|
-
@common
|
91
|
-
end
|
92
|
-
|
93
|
-
def _key
|
94
|
-
Chef::Config[:knife][:cloudformation][:credentials][:key] ||
|
95
|
-
Chef::Config[:knife][:aws_access_key_id]
|
96
|
-
end
|
97
|
-
|
98
|
-
def _secret
|
99
|
-
Chef::Config[:knife][:cloudformation][:credentials][:secret] ||
|
100
|
-
Chef::Config[:knife][:aws_secret_access_key]
|
101
|
-
end
|
102
|
-
|
103
|
-
def _region
|
104
|
-
Chef::Config[:knife][:cloudformation][:credentials][:region] ||
|
105
|
-
Chef::Config[:knife][:region]
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
|
110
|
-
class << self
|
111
|
-
def included(klass)
|
112
|
-
klass.instance_eval do
|
113
|
-
|
114
|
-
extend KnifeCloudformation::KnifeBase::ClassMethods
|
115
|
-
include KnifeCloudformation::KnifeBase::InstanceMethods
|
116
|
-
include KnifeCloudformation::Utils::JSON
|
117
|
-
include KnifeCloudformation::Utils::AnimalStrings
|
118
|
-
|
119
|
-
deps do
|
120
|
-
require 'fog'
|
121
|
-
Chef::Config[:knife][:cloudformation] ||= Mash.new
|
122
|
-
Chef::Config[:knife][:cloudformation][:credentials] ||= Mash.new
|
123
|
-
Chef::Config[:knife][:cloudformation][:options] ||= Mash.new
|
124
|
-
end
|
125
|
-
|
126
|
-
option(:key,
|
127
|
-
:short => '-K KEY',
|
128
|
-
:long => '--key KEY',
|
129
|
-
:description => 'AWS access key id',
|
130
|
-
:proc => lambda {|val|
|
131
|
-
Chef::Config[:knife][:cloudformation][:credentials][:key] = val
|
132
|
-
}
|
133
|
-
)
|
134
|
-
option(:secret,
|
135
|
-
:short => '-S SECRET',
|
136
|
-
:long => '--secret SECRET',
|
137
|
-
:description => 'AWS secret access key',
|
138
|
-
:proc => lambda {|val|
|
139
|
-
Chef::Config[:knife][:cloudformation][:credentials][:secret] = val
|
140
|
-
}
|
141
|
-
)
|
142
|
-
option(:region,
|
143
|
-
:short => '-r REGION',
|
144
|
-
:long => '--region REGION',
|
145
|
-
:description => 'AWS region',
|
146
|
-
:proc => lambda {|val|
|
147
|
-
Chef::Config[:knife][:cloudformation][:credentials][:region] = val
|
148
|
-
}
|
149
|
-
)
|
150
|
-
|
151
|
-
|
152
|
-
# Populate up the hashes so they are available for knife config
|
153
|
-
# with issues of nils
|
154
|
-
['knife.cloudformation.credentials', 'knife.cloudformation.options'].each do |stack|
|
155
|
-
stack.split('.').inject(Chef::Config) do |memo, item|
|
156
|
-
memo[item.to_sym] = Mash.new unless memo[item.to_sym]
|
157
|
-
memo[item.to_sym]
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
Chef::Config[:knife][:cloudformation] ||= Mash.new
|
162
|
-
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
end
|
@@ -1,174 +0,0 @@
|
|
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
|
-
:aws_commons => nil
|
12
|
-
}
|
13
|
-
|
14
|
-
attr_reader :stack, :stack_name, :stack_id, :options, :aws_commons
|
15
|
-
|
16
|
-
def initialize(stack_name, options={})
|
17
|
-
@stack_name = stack_name
|
18
|
-
@options = DEFAULT_OPTIONS.merge(options)
|
19
|
-
if(aws_commons?)
|
20
|
-
@aws_commons = options[:aws_commons]
|
21
|
-
else
|
22
|
-
raise ArgumentError.new('Expecting `AwsCommons` instance but none provided!')
|
23
|
-
end
|
24
|
-
load_stack
|
25
|
-
end
|
26
|
-
|
27
|
-
def export
|
28
|
-
exported = stack.to_hash
|
29
|
-
if(chef_popsicle?)
|
30
|
-
freeze_runlists(exported)
|
31
|
-
end
|
32
|
-
remove_ignored_parameters(exported)
|
33
|
-
exported
|
34
|
-
end
|
35
|
-
|
36
|
-
def method_missing(*args)
|
37
|
-
m = args.first.to_s
|
38
|
-
if(m.end_with?('?') && options.has_key?(k = m.sub('?', '').to_sym))
|
39
|
-
!!options[k]
|
40
|
-
else
|
41
|
-
super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
protected
|
46
|
-
|
47
|
-
def remove_ignored_parameters(export)
|
48
|
-
options[:ignored_parameters].each do |param|
|
49
|
-
export[:parameters].delete(param)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def chef_environment_name
|
54
|
-
if(chef_environment_parameter?)
|
55
|
-
name = stack[:parameters][options[:chef_environment_parameter]]
|
56
|
-
end
|
57
|
-
name || '_default'
|
58
|
-
end
|
59
|
-
|
60
|
-
def environment
|
61
|
-
unless(@env)
|
62
|
-
@env = Chef::Environment.load('imdev')
|
63
|
-
end
|
64
|
-
@env
|
65
|
-
end
|
66
|
-
|
67
|
-
def load_stack
|
68
|
-
@stack = AwsCommons::Stack.new(stack_name, aws_commons)
|
69
|
-
@stack_id = @stack.stack_id
|
70
|
-
@stack
|
71
|
-
end
|
72
|
-
|
73
|
-
def allowed_cookbook_version(cookbook)
|
74
|
-
restriction = environment.cookbook_versions[cookbook]
|
75
|
-
requirement = Gem::Requirement.new(restriction)
|
76
|
-
Chef::CookbookVersion.available_versions(cookbook).detect do |v|
|
77
|
-
requirement.satisfied_by?(Gem::Version.new(v))
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def extract_runlist_item(item)
|
82
|
-
rl_item = item.is_a?(Chef::RunList::RunListItem) ? item : Chef::RunList::RunListItem.new(item)
|
83
|
-
static_content = Mash.new(:run_list => [])
|
84
|
-
if(rl_item.recipe?)
|
85
|
-
cookbook, recipe = rl_item.name.split('::')
|
86
|
-
peg_version = allowed_cookbook_version(cookbook)
|
87
|
-
static_content[:run_list] << "recipe[#{[cookbook, recipe || 'default'].join('::')}@#{peg_version}]"
|
88
|
-
elsif(rl_item.role?)
|
89
|
-
role = Chef::Role.load(rl_item.name)
|
90
|
-
role.run_list.each do |item|
|
91
|
-
static_content = Chef::Mixin::DeepMerge.merge(static_content, extract_runlist_item(item))
|
92
|
-
end
|
93
|
-
static_content = Chef::Mixin::DeepMerge.merge(
|
94
|
-
static_content, Chef::Mixin::DeepMerge.merge(role.default_attributes, role.override_attributes)
|
95
|
-
)
|
96
|
-
else
|
97
|
-
# dunno what this is
|
98
|
-
end
|
99
|
-
static_content
|
100
|
-
end
|
101
|
-
|
102
|
-
def unpack_and_freeze_runlist(first_run)
|
103
|
-
extracted_runlists = first_run['run_list'].map do |item|
|
104
|
-
extract_runlist_item(cf_replace(item))
|
105
|
-
end
|
106
|
-
first_run.delete('run_list')
|
107
|
-
first_run.replace(
|
108
|
-
extracted_runlists.inject(first_run) do |memo, first_run_item|
|
109
|
-
Chef::Mixin::DeepMerge.merge(memo, first_run_item)
|
110
|
-
end
|
111
|
-
)
|
112
|
-
end
|
113
|
-
|
114
|
-
def freeze_runlists(exported)
|
115
|
-
first_runs = locate_runlists(exported)
|
116
|
-
first_runs.each do |first_run|
|
117
|
-
unpack_and_freeze_runlist(first_run)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def locate_runlists(thing)
|
122
|
-
result = []
|
123
|
-
case thing
|
124
|
-
when Hash
|
125
|
-
if(thing['content'] && thing['content']['run_list'])
|
126
|
-
result << thing['content']
|
127
|
-
else
|
128
|
-
thing.each do |k,v|
|
129
|
-
result += locate_runlists(v)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
when Array
|
133
|
-
thing.each do |v|
|
134
|
-
result += locate_runlists(v)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
result
|
138
|
-
end
|
139
|
-
|
140
|
-
def cf_replace(hsh)
|
141
|
-
if(hsh.is_a?(Hash))
|
142
|
-
case hsh.keys.first
|
143
|
-
when 'Fn::Join'
|
144
|
-
cf_join(*hsh.values.first)
|
145
|
-
when 'Ref'
|
146
|
-
cf_ref(hsh.values.first)
|
147
|
-
else
|
148
|
-
hsh
|
149
|
-
end
|
150
|
-
else
|
151
|
-
hsh
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def cf_ref(ref_name)
|
156
|
-
if(stack.parameters.has_key?(ref_name))
|
157
|
-
stack.parameters[ref_name]
|
158
|
-
else
|
159
|
-
raise KeyError.new("No parameter found with given reference name (#{ref_name}). " <<
|
160
|
-
"Only parameter based references supported!")
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def cf_join(delim, args)
|
165
|
-
args.map do |arg|
|
166
|
-
if(arg.is_a?(Hash))
|
167
|
-
cf_replace(arg)
|
168
|
-
else
|
169
|
-
arg.to_s
|
170
|
-
end
|
171
|
-
end.join(delim)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|