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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +56 -2
  4. data/knife-cloudformation.gemspec +4 -7
  5. data/lib/chef/knife/cloudformation_create.rb +105 -245
  6. data/lib/chef/knife/cloudformation_describe.rb +50 -26
  7. data/lib/chef/knife/cloudformation_destroy.rb +17 -18
  8. data/lib/chef/knife/cloudformation_events.rb +48 -14
  9. data/lib/chef/knife/cloudformation_export.rb +117 -34
  10. data/lib/chef/knife/cloudformation_import.rb +124 -18
  11. data/lib/chef/knife/cloudformation_inspect.rb +159 -71
  12. data/lib/chef/knife/cloudformation_list.rb +20 -24
  13. data/lib/chef/knife/cloudformation_promote.rb +40 -0
  14. data/lib/chef/knife/cloudformation_update.rb +132 -15
  15. data/lib/chef/knife/cloudformation_validate.rb +35 -0
  16. data/lib/knife-cloudformation.rb +28 -0
  17. data/lib/knife-cloudformation/cache.rb +213 -35
  18. data/lib/knife-cloudformation/knife.rb +9 -0
  19. data/lib/knife-cloudformation/knife/base.rb +179 -0
  20. data/lib/knife-cloudformation/knife/stack.rb +94 -0
  21. data/lib/knife-cloudformation/knife/template.rb +174 -0
  22. data/lib/knife-cloudformation/monkey_patch.rb +8 -0
  23. data/lib/knife-cloudformation/monkey_patch/stack.rb +195 -0
  24. data/lib/knife-cloudformation/provider.rb +225 -0
  25. data/lib/knife-cloudformation/utils.rb +18 -98
  26. data/lib/knife-cloudformation/utils/animal_strings.rb +28 -0
  27. data/lib/knife-cloudformation/utils/debug.rb +31 -0
  28. data/lib/knife-cloudformation/utils/json.rb +64 -0
  29. data/lib/knife-cloudformation/utils/object_storage.rb +28 -0
  30. data/lib/knife-cloudformation/utils/output.rb +79 -0
  31. data/lib/knife-cloudformation/utils/path_selector.rb +99 -0
  32. data/lib/knife-cloudformation/utils/ssher.rb +29 -0
  33. data/lib/knife-cloudformation/utils/stack_exporter.rb +271 -0
  34. data/lib/knife-cloudformation/utils/stack_parameter_scrubber.rb +35 -0
  35. data/lib/knife-cloudformation/utils/stack_parameter_validator.rb +124 -0
  36. data/lib/knife-cloudformation/version.rb +2 -4
  37. metadata +47 -94
  38. data/Gemfile +0 -3
  39. data/Gemfile.lock +0 -90
  40. data/knife-cloudformation-0.1.20.gem +0 -0
  41. data/lib/knife-cloudformation/aws_commons.rb +0 -267
  42. data/lib/knife-cloudformation/aws_commons/stack.rb +0 -435
  43. data/lib/knife-cloudformation/aws_commons/stack_parameter_validator.rb +0 -79
  44. data/lib/knife-cloudformation/cloudformation_base.rb +0 -168
  45. 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