knife-cloudformation 0.1.22 → 0.2.0
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.
- 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
|