knife-cloudformation 0.2.24 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +16 -0
  4. data/knife-cloudformation.gemspec +14 -4
  5. data/lib/knife-cloudformation.rb +0 -28
  6. data/lib/knife-cloudformation/version.rb +1 -1
  7. metadata +18 -80
  8. data/lib/chef/knife/cloudformation_create.rb +0 -147
  9. data/lib/chef/knife/cloudformation_describe.rb +0 -99
  10. data/lib/chef/knife/cloudformation_destroy.rb +0 -84
  11. data/lib/chef/knife/cloudformation_events.rb +0 -117
  12. data/lib/chef/knife/cloudformation_export.rb +0 -162
  13. data/lib/chef/knife/cloudformation_import.rb +0 -141
  14. data/lib/chef/knife/cloudformation_inspect.rb +0 -206
  15. data/lib/chef/knife/cloudformation_list.rb +0 -72
  16. data/lib/chef/knife/cloudformation_promote.rb +0 -40
  17. data/lib/chef/knife/cloudformation_update.rb +0 -137
  18. data/lib/chef/knife/cloudformation_validate.rb +0 -36
  19. data/lib/knife-cloudformation/cache.rb +0 -385
  20. data/lib/knife-cloudformation/knife.rb +0 -9
  21. data/lib/knife-cloudformation/knife/base.rb +0 -195
  22. data/lib/knife-cloudformation/knife/stack.rb +0 -197
  23. data/lib/knife-cloudformation/knife/template.rb +0 -213
  24. data/lib/knife-cloudformation/monkey_patch.rb +0 -8
  25. data/lib/knife-cloudformation/monkey_patch/stack.rb +0 -195
  26. data/lib/knife-cloudformation/provider.rb +0 -225
  27. data/lib/knife-cloudformation/utils.rb +0 -24
  28. data/lib/knife-cloudformation/utils/animal_strings.rb +0 -28
  29. data/lib/knife-cloudformation/utils/debug.rb +0 -31
  30. data/lib/knife-cloudformation/utils/json.rb +0 -64
  31. data/lib/knife-cloudformation/utils/object_storage.rb +0 -28
  32. data/lib/knife-cloudformation/utils/output.rb +0 -79
  33. data/lib/knife-cloudformation/utils/path_selector.rb +0 -99
  34. data/lib/knife-cloudformation/utils/ssher.rb +0 -29
  35. data/lib/knife-cloudformation/utils/stack_exporter.rb +0 -271
  36. data/lib/knife-cloudformation/utils/stack_parameter_scrubber.rb +0 -37
  37. data/lib/knife-cloudformation/utils/stack_parameter_validator.rb +0 -124
@@ -1,84 +0,0 @@
1
- require 'knife-cloudformation'
2
-
3
- class Chef
4
- class Knife
5
- # Cloudformation destroy command
6
- class CloudformationDestroy < Knife
7
-
8
- include KnifeCloudformation::Knife::Base
9
-
10
- banner 'knife cloudformation destroy NAME [NAME]'
11
-
12
- option(:polling,
13
- :long => '--[no-]poll',
14
- :description => 'Enable stack event polling.',
15
- :boolean => true,
16
- :default => true,
17
- :proc => lambda {|val| Chef::Config[:knife][:cloudformation][:poll] = val }
18
- )
19
-
20
- # Run the stack destruction action
21
- def _run
22
- stacks = name_args.sort
23
- plural = 's' if stacks.size > 1
24
- globs = stacks.find_all do |s|
25
- s !~ /^[a-zA-Z0-9-]+$/
26
- end
27
- unless(globs.empty?)
28
- glob_stacks = provider.connection.stacks.all.find_all do |remote_stack|
29
- globs.detect do |glob|
30
- File.fnmatch(glob, remote_stack.name)
31
- end
32
- end
33
- stacks += glob_stacks.map(&:name)
34
- stacks -= globs
35
- stacks.sort!
36
- end
37
- ui.warn "Destroying Cloud Formation#{plural}: #{ui.color(stacks.join(', '), :bold)}"
38
- ui.confirm "Destroy formation#{plural}"
39
- stacks.each do |stack_name|
40
- stack = provider.connection.stacks.get(stack_name)
41
- if(stack)
42
- nested_stack_cleanup!(stack)
43
- stack.destroy
44
- else
45
- ui.warn "Failed to locate requested stack: #{ui.color(stack_name, :bold)}"
46
- end
47
- end
48
- if(config[:polling])
49
- if(stacks.size == 1)
50
- poll_stack(stacks.first)
51
- else
52
- ui.error "Stack polling is not available when multiple stack deletion is requested!"
53
- end
54
- end
55
- ui.info " -> Destroyed Cloud Formation#{plural}: #{ui.color(stacks.join(', '), :bold, :red)}"
56
- end
57
-
58
- # Cleanup persisted templates if nested stack resources are included
59
- def nested_stack_cleanup!(stack)
60
- nest_stacks = stack.template.fetch('Resources', {}).values.find_all do |resource|
61
- resource['Type'] == 'AWS::CloudFormation::Stack'
62
- end.each do |resource|
63
- url = resource['Properties']['TemplateURL']
64
- if(url)
65
- _, bucket_name, path = URI.parse(url).path.split('/', 3)
66
- bucket = provider.connection.api_for(:storage).buckets.get(bucket_name)
67
- if(bucket)
68
- file = bucket.files.get(path)
69
- if(file)
70
- file.destroy
71
- ui.info "Deleted nested stack template! (Bucket: #{bucket_name} Template: #{path})"
72
- else
73
- ui.warn "Failed to locate template file within bucket for deletion! (#{path})"
74
- end
75
- else
76
- ui.warn "Failed to locate bucket containing template file for deletion! (#{bucket_name})"
77
- end
78
- end
79
- end
80
- end
81
-
82
- end
83
- end
84
- end
@@ -1,117 +0,0 @@
1
- require 'knife-cloudformation'
2
-
3
- class Chef
4
- class Knife
5
- # Cloudformation list command
6
- class CloudformationEvents < Knife
7
-
8
- banner 'knife cloudformation events NAME'
9
-
10
- include KnifeCloudformation::Knife::Base
11
-
12
- option(:polling,
13
- :short => '-p',
14
- :long => '--[no-]poll',
15
- :boolean => true,
16
- :default => true,
17
- :description => 'Poll events while stack status is "in progress"',
18
- :proc => lambda {|v| Chef::Config[:knife][:cloudformation][:poll] = v }
19
- )
20
-
21
- option(:attribute,
22
- :short => '-a ATTR',
23
- :long => '--attribute ATTR',
24
- :description => 'Attribute to print. Can be used multiple times.',
25
- :proc => lambda {|val|
26
- Chef::Config[:knife][:cloudformation][:attributes] ||= []
27
- Chef::Config[:knife][:cloudformation][:attributes].push(val).uniq!
28
- }
29
- )
30
-
31
- option(:poll_delay,
32
- :short => '-D secs',
33
- :long => '--poll-delay secs',
34
- :description => 'Number of seconds to pause between event poll',
35
- :proc => lambda {|val|
36
- Chef::Config[:knife][:cloudformation][:poll_delay] = val.to_i
37
- }
38
- )
39
-
40
- option(:all_attributes,
41
- :long => '--all-attributes',
42
- :description => 'Print all attributes'
43
- )
44
-
45
- # Run the events list action
46
- def _run
47
- name = name_args.first
48
- ui.info "Cloud Formation Events for Stack: #{ui.color(name, :bold)}\n"
49
- stack = provider.connection.stacks.get(name)
50
- last_id = nil
51
- if(stack)
52
- events = get_events(stack)
53
- things_output(name, events, 'events')
54
- last_id = events.last ? events.last[:id] : nil
55
- if(Chef::Config[:knife][:cloudformation][:poll])
56
- cycle_events = true
57
- while(cycle_events)
58
- cycle_events = stack.in_progress?
59
- sleep(Chef::Config[:knife][:cloudformation][:poll_delay] || 10)
60
- stack.events.reload
61
- events = get_events(stack, last_id)
62
- unless(events.empty?)
63
- last_id = events.last[:id]
64
- things_output(nil, events, 'events', :no_title, :ignore_empty_output)
65
- end
66
- nest_stacks = stack.resources.all.find_all do |resource|
67
- resource.state.to_s.end_with?('in_progress') &&
68
- resource.type == 'AWS::CloudFormation::Stack'
69
- end
70
- if(nest_stacks)
71
- nest_stacks.each do |nest_stack|
72
- begin
73
- poll_stack(nest_stack.id)
74
- ui.info "Complete event listing for nested stack (#{nest_stack.name})"
75
- rescue => e
76
- ui.warn "Error encountered on event listing for nested stack - #{e} (#{nest_stack.name})"
77
- end
78
- end
79
- end
80
- stack.reload
81
- end
82
- # Extra to see completion
83
- things_output(nil, get_events(stack, last_id), 'events', :no_title, :ignore_empty_output)
84
- end
85
- else
86
- ui.fatal "Failed to locate requested stack: #{ui.color(name, :bold, :red)}"
87
- raise "Failed to locate stack: #{name}!"
88
- end
89
- end
90
-
91
- # Fetch events from stack
92
- #
93
- # @param stack [Miasma::Models::Orchestration::Stack]
94
- # @param last_id [String] only return events after this ID
95
- # @return [Array<Hash>]
96
- def get_events(stack, last_id=nil)
97
- get_things do
98
- stack_events = stack.events.all
99
- if(last_id)
100
- start_index = stack_events.index{|event| event.id == last_id}
101
- events = stack_events.slice(0, start_index.to_i)
102
- else
103
- events = stack_events
104
- end
105
- events.map do |event|
106
- Mash.new(event.attributes)
107
- end
108
- end
109
- end
110
-
111
- # @return [Array<String>] default attributes for events
112
- def default_attributes
113
- %w(time resource_logical_id resource_status resource_status_reason)
114
- end
115
- end
116
- end
117
- end
@@ -1,162 +0,0 @@
1
- require 'knife-cloudformation'
2
-
3
- class Chef
4
- class Knife
5
- # Cloudformation export command
6
- class CloudformationExport < Knife
7
-
8
- include KnifeCloudformation::Knife::Base
9
- include KnifeCloudformation::Utils::ObjectStorage
10
-
11
- banner 'knife cloudformation export STACK_NAME'
12
-
13
- option(:export_name,
14
- :long => '--export-file-name NAME',
15
- :description => 'File basename to contain the export. Can be callable block if defined within configuration',
16
- :proc => lambda{|v| Chef::Config[:knife][:cloudformation][:export][:name] = v}
17
- )
18
-
19
- option(:path,
20
- :long => '--export-path PATH',
21
- :description => 'Directory path write export JSON file',
22
- :proc => lambda{|v| Chef::Config[:knife][:cloudformation][:export][:path] = v}
23
- )
24
-
25
- option(:bucket,
26
- :long => '--export-bucket BUCKET_NAME',
27
- :description => 'Remote file bucket to write export JSON file',
28
- :proc => lambda{|v| Chef::Config[:knife][:cloudformation][:export][:bucket] = v}
29
- )
30
-
31
- option(:bucket_prefix,
32
- :long => '--bucket-key-prefix PREFIX',
33
- :description => 'Key prefix for file storage in bucket. Can be callable block if defined within configuration',
34
- :proc => lambda{|v| Chef::Config[:knife][:cloudformation][:export][:bucket_prefix] = v}
35
- )
36
-
37
- option(:ignore_parameters,
38
- :short => '-P NAME',
39
- :long => '--exclude-parameter NAME',
40
- :description => 'Exclude parameter from export (can be used multiple times)',
41
- :proc => lambda{|v|
42
- Chef::Config[:knife][:cloudformation][:export][:ignore_parameters].push(v).uniq!
43
- }
44
- )
45
-
46
- option(:chef_environment_parameter,
47
- :long => '--chef-environment-parameter NAME',
48
- :description => 'Parameter used within stack to specify Chef environment',
49
- :proc => lambda{|v|
50
- Chef::Config[:knife][:cloudformation][:export][:chef_environment_parameter] = v
51
- }
52
- )
53
-
54
- option(:chef_popsicle,
55
- :long => '--[no-]freeze-run-list',
56
- :boolean => true,
57
- :default => true,
58
- :description => 'Freezes first run files',
59
- :proc => lambda{|v| Chef::Config[:knife][:cloudformation][:export][:chef_popsicle] = v }
60
- )
61
-
62
- unless(Chef::Config[:knife][:cloudformation].has_key?(:export))
63
- Chef::Config[:knife][:cloudformation][:export] = Mash.new(
64
- :credentials => Mash.new,
65
- :ignore_parameters => []
66
- )
67
- end
68
-
69
- # Run export action
70
- def _run
71
- stack_name = name_args.first
72
- ui.info "#{ui.color('Stack Export:', :bold)} #{stack_name}"
73
- ui.confirm 'Perform export'
74
- stack = provider.stacks.get(stack_name)
75
- if(stack)
76
- export_options = Mash.new.tap do |opts|
77
- [:chef_popsicle, :chef_environment_parameter, :ignore_parameters].each do |key|
78
- unless(Chef::Config[:knife][:cloudformation][:export][key].nil?)
79
- opts[key] = Chef::Config[:knife][:cloudformation][:export][key]
80
- end
81
- end
82
- end
83
- exporter = KnifeCloudformation::Utils::StackExporter.new(stack, export_options)
84
- result = exporter.export
85
- outputs = [
86
- write_to_file(result, stack),
87
- write_to_bucket(result, stack)
88
- ].compact
89
- if(outputs.empty?)
90
- ui.warn 'No persistent output location defined. Printing export:'
91
- ui.info _format_json(result)
92
- end
93
- ui.info "#{ui.color('Stack export', :bold)} (#{name_args.first}): #{ui.color('complete', :green)}"
94
- unless(outputs.empty?)
95
- outputs.each do |output|
96
- ui.info ui.color(" -> #{output}", :blue)
97
- end
98
- end
99
- else
100
- ui.fatal "Failed to discover requested stack: #{ui.color(stack_name, :red, :bold)}"
101
- exit -1
102
- end
103
- end
104
-
105
- # Generate file name for stack export JSON contents
106
- #
107
- # @param stack [Miasma::Models::Orchestration::Stack]
108
- # @return [String] file name
109
- def export_file_name(stack)
110
- name = Chef::Config[:knife][:cloudformation][:export][:file]
111
- if(name)
112
- if(name.respond_to?(:call))
113
- name.call(stack)
114
- else
115
- name.to_s
116
- end
117
- else
118
- "#{stack.stack_name}-#{Time.now.to_i}.json"
119
- end
120
- end
121
-
122
- # Write stack export to local file
123
- #
124
- # @param payload [Hash] stack export payload
125
- # @param stack [Misama::Stack::Orchestration::Stack]
126
- # @return [String, NilClass] path to file
127
- def write_to_file(payload, stack)
128
- raise NotImplementedError
129
- if(Chef::Config[:knife][:cloudformation][:export][:path])
130
- full_path = File.join(
131
- File.expand_path(Chef::Config[:knife][:cloudformation][:export][:path]),
132
- export_file_name(stack)
133
- )
134
- _, bucket, path = full_path.split('/', 3)
135
- directory = provider.service_for(:storage,
136
- :provider => :local,
137
- :local_root => '/'
138
- ).directories.get(bucket)
139
- file_store(payload, path, directory)
140
- end
141
- end
142
-
143
- # Write stack export to remote bucket
144
- #
145
- # @param payload [Hash] stack export payload
146
- # @param stack [Miasma::Models::Orchestration::Stack]
147
- # @return [String, NilClass] remote bucket key
148
- def write_to_bucket(payload, stack)
149
- raise NotImplementedError
150
- if(bucket = Chef::Config[:knife][:cloudformation][:export][:bucket])
151
- key_path = File.join(*[
152
- bucket_prefix(stack),
153
- export_file_name(stack)
154
- ].compact
155
- )
156
- file_store(payload, key_path, provider.service_for(:storage).directories.get(bucket))
157
- end
158
- end
159
-
160
- end
161
- end
162
- end
@@ -1,141 +0,0 @@
1
- require 'stringio'
2
- require 'knife-cloudformation'
3
-
4
- class Chef
5
- class Knife
6
- # Cloudformation import command
7
- class CloudformationImport < Knife
8
-
9
- include KnifeCloudformation::Knife::Base
10
- include KnifeCloudformation::Utils::JSON
11
- include KnifeCloudformation::Utils::ObjectStorage
12
- include KnifeCloudformation::Utils::PathSelector
13
-
14
- banner 'knife cloudformation import NEW_STACK_NAME [JSON_EXPORT_FILE]'
15
-
16
- option(:path,
17
- :long => '--import-path PATH',
18
- :description => 'Directory path JSON export files are located',
19
- :proc => lambda{|v|
20
- Chef::Config[:knife][:cloudformation][:import][:path] = File.expand_path(v)
21
- }
22
- )
23
-
24
- option(:bucket,
25
- :long => '--export-bucket BUCKET_NAME',
26
- :description => 'Remote file bucket JSON export files are located',
27
- :proc => lambda{|v| Chef::Config[:knife][:cloudformation][:import][:bucket] = v}
28
- )
29
-
30
- option(:bucket_prefix,
31
- :long => '--bucket-key-prefix PREFIX',
32
- :description => 'Key prefix for file storage in bucket. Can be callable block if defined within configuration',
33
- :proc => lambda{|v| Chef::Config[:knife][:cloudformation][:import][:bucket_prefix] = v}
34
- )
35
-
36
- unless(Chef::Config[:knife][:cloudformation].has_key?(:import))
37
- Chef::Config[:knife][:cloudformation][:import] = Mash.new
38
- end
39
-
40
- # Run the import action
41
- def _run
42
- stack_name, json_file = name_args
43
- ui.info "#{ui.color('Stack Import:', :bold)} #{stack_name}"
44
- unless(json_file)
45
- entries = [].tap do |_entries|
46
- _entries.push('s3') if Chef::Config[:knife][:cloudformation][:import][:bucket]
47
- _entries.push('fs') if Chef::Config[:knife][:cloudformation][:import][:path]
48
- end
49
- if(entries.size > 1)
50
- valid = false
51
- until(valid)
52
- answer = ui.ask_question('Import via file system (fs) or remote bucket (remote)?', :default => 'remote')
53
- valid = true if %w(remote fs).include?(answer)
54
- entries = [answer]
55
- end
56
- elsif(entries.size < 1)
57
- ui.fatal 'No path or bucket set. Unable to perform dynamic lookup!'
58
- exit 1
59
- end
60
- case entries.first
61
- when 'remote'
62
- json_file = remote_discovery
63
- else
64
- json_file = local_discovery
65
- end
66
- end
67
- if(File.exists?(json_file) || json_file.is_a?(IO))
68
- content = json_file.is_a?(IO) ? json_file.read : File.read(json_file)
69
- export = Mash.new(_from_json(content))
70
- begin
71
- creator = Chef::Knife::CloudformationCreate.new
72
- creator.name_args = [stack_name]
73
- Chef::Config[:knife][:cloudformation][:template] = _from_json(export[:stack][:template])
74
- Chef::Config[:knife][:cloudformation][:options] = export[:stack][:options]
75
- ui.info ' - Starting creation of import'
76
- creator.run
77
- ui.info "#{ui.color('Stack Import', :bold)} (#{json_file}): #{ui.color('complete', :green)}"
78
- rescue => e
79
- ui.fatal "Failed to import stack: #{e}"
80
- debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
81
- exit -1
82
- end
83
- else
84
- ui.fatal "Failed to locate JSON export file (#{json_file})"
85
- exit 1
86
- end
87
- end
88
-
89
- # Generate bucket prefix
90
- #
91
- # @return [String, NilClass]
92
- def bucket_prefix
93
- if(prefix = Chef::Config[:knife][:cloudformation][:import][:bucket_prefix])
94
- if(prefix.respond_to?(:cal))
95
- prefix.call
96
- else
97
- prefix.to_s
98
- end
99
- end
100
- end
101
-
102
- # Discover remote file
103
- #
104
- # @return [IO] stack export IO
105
- def remote_discovery
106
- storage = provider.service_for(:storage)
107
- directory = storage.directories.get(
108
- Chef::Config[:knife][:cloudformation][:import][:bucket]
109
- )
110
- file = prompt_for_file(
111
- directory,
112
- :directories_name => 'Collections',
113
- :files_names => 'Exports',
114
- :filter_prefix => bucket_prefix
115
- )
116
- if(file)
117
- remote_file = storage.files.get(file)
118
- StringIO.new(remote_file.body)
119
- end
120
- end
121
-
122
- # Discover remote file
123
- #
124
- # @return [IO] stack export IO
125
- def local_discovery
126
- _, bucket = Chef::Config[:knife][:cloudformation][:import][:path].split('/', 2)
127
- storage = provider.service_for(:storage,
128
- :provider => :local,
129
- :local_root => '/'
130
- )
131
- directory = storage.directories.get(bucket)
132
- prompt_for_file(
133
- directory,
134
- :directories_name => 'Collections',
135
- :files_names => 'Exports'
136
- )
137
- end
138
-
139
- end
140
- end
141
- end