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,10 +1,11 @@
|
|
1
|
-
require 'knife-cloudformation
|
1
|
+
require 'knife-cloudformation'
|
2
2
|
|
3
3
|
class Chef
|
4
4
|
class Knife
|
5
|
+
# Cloudformation describe command
|
5
6
|
class CloudformationDescribe < Knife
|
6
7
|
|
7
|
-
include KnifeCloudformation::
|
8
|
+
include KnifeCloudformation::Knife::Base
|
8
9
|
|
9
10
|
banner 'knife cloudformation describe NAME'
|
10
11
|
|
@@ -13,13 +14,11 @@ class Chef
|
|
13
14
|
:long => '--resources',
|
14
15
|
:description => 'Display resources for stack'
|
15
16
|
)
|
16
|
-
|
17
17
|
option(:outputs,
|
18
18
|
:short => '-o',
|
19
19
|
:long => '--outputs',
|
20
20
|
:description => 'Display output for stack'
|
21
21
|
)
|
22
|
-
|
23
22
|
option(:attribute,
|
24
23
|
:short => '-a ATTR',
|
25
24
|
:long => '--attribute ATTR',
|
@@ -29,45 +28,70 @@ class Chef
|
|
29
28
|
Chef::Config[:knife][:cloudformation][:attributes].push(val).uniq!
|
30
29
|
}
|
31
30
|
)
|
32
|
-
|
33
31
|
option(:all,
|
34
32
|
:long => '--all-attributes',
|
35
33
|
:description => 'Print all attributes'
|
36
34
|
)
|
37
35
|
|
38
|
-
|
36
|
+
# information available
|
37
|
+
unless(defined?(AVAILABLE_DISPLAYS))
|
38
|
+
AVAILABLE_DISPLAYS = [:resources, :outputs]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Run the stack describe action
|
42
|
+
def _run
|
39
43
|
stack_name = name_args.last
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
stack = provider.stacks.get(stack_name)
|
45
|
+
if(stack)
|
46
|
+
display = [].tap do |to_display|
|
47
|
+
AVAILABLE_DISPLAYS.each do |display_option|
|
48
|
+
if(config[display_option])
|
49
|
+
to_display << display_option
|
50
|
+
end
|
46
51
|
end
|
47
|
-
|
48
|
-
|
52
|
+
end
|
53
|
+
display = AVAILABLE_DISPLAYS.dup if display.empty?
|
54
|
+
display.each do |display_method|
|
55
|
+
self.send(display_method, stack)
|
56
|
+
ui.info ''
|
49
57
|
end
|
50
58
|
else
|
51
|
-
|
52
|
-
|
53
|
-
)
|
59
|
+
ui.fatal "Failed to find requested stack: #{ui.color(stack_name, :bold, :red)}"
|
60
|
+
exit -1
|
54
61
|
end
|
55
62
|
end
|
56
63
|
|
57
|
-
|
58
|
-
|
64
|
+
# Display resources
|
65
|
+
#
|
66
|
+
# @param stack [Miasma::Models::Orchestration::Stack]
|
67
|
+
def resources(stack)
|
68
|
+
stack_resources = stack.resources.all.sort do |x, y|
|
69
|
+
y.updated <=> x.updated
|
70
|
+
end.map do |resource|
|
71
|
+
Mash.new(resource.attributes)
|
72
|
+
end
|
73
|
+
things_output(stack.name, stack_resources, :resources)
|
59
74
|
end
|
60
75
|
|
61
|
-
|
62
|
-
|
63
|
-
|
76
|
+
# Display outputs
|
77
|
+
#
|
78
|
+
# @param stack [Miasma::Models::Orchestration::Stack]
|
79
|
+
def outputs(stack)
|
80
|
+
ui.info "Outputs for stack: #{ui.color(stack.name, :bold)}"
|
81
|
+
unless(stack.outputs.empty?)
|
82
|
+
stack.outputs.each do |output|
|
83
|
+
key, value = output.key, output.value
|
84
|
+
key = snake(key).to_s.split('_').map(&:capitalize).join(' ')
|
85
|
+
ui.info [' ', ui.color("#{key}:", :bold), value].join(' ')
|
86
|
+
end
|
87
|
+
else
|
88
|
+
ui.info " #{ui.color('No outputs found')}"
|
64
89
|
end
|
65
90
|
end
|
66
91
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
92
|
+
# @return [Array<String>] default attributes
|
93
|
+
def default_attributes
|
94
|
+
%w(updated logical_id type status status_reason)
|
71
95
|
end
|
72
96
|
|
73
97
|
end
|
@@ -1,10 +1,11 @@
|
|
1
|
-
require 'knife-cloudformation
|
1
|
+
require 'knife-cloudformation'
|
2
2
|
|
3
3
|
class Chef
|
4
4
|
class Knife
|
5
|
+
# Cloudformation destroy command
|
5
6
|
class CloudformationDestroy < Knife
|
6
7
|
|
7
|
-
include KnifeCloudformation::
|
8
|
+
include KnifeCloudformation::Knife::Base
|
8
9
|
|
9
10
|
banner 'knife cloudformation destroy NAME [NAME]'
|
10
11
|
|
@@ -16,31 +17,29 @@ class Chef
|
|
16
17
|
:proc => lambda {|val| Chef::Config[:knife][:cloudformation][:poll] = val }
|
17
18
|
)
|
18
19
|
|
19
|
-
|
20
|
+
# Run the stack destruction action
|
21
|
+
def _run
|
20
22
|
stacks = name_args.sort
|
21
23
|
plural = 's' if stacks.size > 1
|
22
24
|
ui.warn "Destroying Cloud Formation#{plural}: #{ui.color(stacks.join(', '), :bold)}"
|
23
25
|
ui.confirm "Destroy formation#{plural}"
|
24
26
|
stacks.each do |stack_name|
|
25
|
-
|
26
|
-
|
27
|
+
stack = provider.stacks.get(stack_name)
|
28
|
+
if(stack)
|
29
|
+
stack.destroy
|
30
|
+
else
|
31
|
+
ui.warn "Failed to locate requested stack: #{ui.color(stack_name, :bold)}"
|
32
|
+
end
|
27
33
|
end
|
28
34
|
if(config[:polling])
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
# ignore this error since this is the end result we want!
|
35
|
+
if(stacks.size == 1)
|
36
|
+
provider.fetch_stacks
|
37
|
+
poll_stack(stacks.first)
|
38
|
+
else
|
39
|
+
ui.error "Stack polling is not available when multiple stack deletion is requested!"
|
35
40
|
end
|
36
|
-
ui.info " -> Destroyed Cloud Formation#{plural}: #{ui.color(stacks.join(', '), :bold, :red)}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def destroy_formation!(stack_name)
|
41
|
-
get_things(stack_name, 'Failed to perform destruction') do
|
42
|
-
stack(stack_name).destroy
|
43
41
|
end
|
42
|
+
ui.info " -> Destroyed Cloud Formation#{plural}: #{ui.color(stacks.join(', '), :bold, :red)}"
|
44
43
|
end
|
45
44
|
|
46
45
|
end
|
@@ -1,18 +1,19 @@
|
|
1
|
-
require 'knife-cloudformation
|
1
|
+
require 'knife-cloudformation'
|
2
2
|
|
3
3
|
class Chef
|
4
4
|
class Knife
|
5
|
+
# Cloudformation list command
|
5
6
|
class CloudformationEvents < Knife
|
6
7
|
|
7
8
|
banner 'knife cloudformation events NAME'
|
8
9
|
|
9
|
-
include KnifeCloudformation::
|
10
|
+
include KnifeCloudformation::Knife::Base
|
10
11
|
|
11
12
|
option(:polling,
|
12
13
|
:short => '-p',
|
13
14
|
:long => '--[no-]poll',
|
14
15
|
:boolean => true,
|
15
|
-
:default =>
|
16
|
+
:default => true,
|
16
17
|
:description => 'Poll events while stack status is "in progress"',
|
17
18
|
:proc => lambda {|v| Chef::Config[:knife][:cloudformation][:poll] = v }
|
18
19
|
)
|
@@ -41,28 +42,61 @@ class Chef
|
|
41
42
|
:description => 'Print all attributes'
|
42
43
|
)
|
43
44
|
|
44
|
-
|
45
|
+
# Run the events list action
|
46
|
+
def _run
|
45
47
|
name = name_args.first
|
46
48
|
ui.info "Cloud Formation Events for Stack: #{ui.color(name, :bold)}\n"
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
stack = provider.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[:id]
|
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] || 15)
|
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
|
+
stack.reload
|
67
|
+
end
|
68
|
+
# Extra to see completion
|
69
|
+
things_output(nil, get_events(stack, last_id), 'events', :no_title, :ignore_empty_output)
|
52
70
|
end
|
53
|
-
|
54
|
-
|
71
|
+
else
|
72
|
+
ui.fatal "Failed to locate requested stack: #{ui.color(name, :bold, :red)}"
|
73
|
+
exit -1
|
55
74
|
end
|
56
75
|
end
|
57
76
|
|
58
|
-
|
77
|
+
# Fetch events from stack
|
78
|
+
#
|
79
|
+
# @param stack [Miasma::Models::Orchestration::Stack]
|
80
|
+
# @param last_id [String] only return events after this ID
|
81
|
+
# @return [Array<Hash>]
|
82
|
+
def get_events(stack, last_id=nil)
|
59
83
|
get_things do
|
60
|
-
stack
|
84
|
+
stack_events = stack.events.all
|
85
|
+
if(last_id)
|
86
|
+
start_index = stack_events.index{|event| event.id == last_id}
|
87
|
+
events = stack_events.slice(0, start_index.to_i)
|
88
|
+
else
|
89
|
+
events = stack_events
|
90
|
+
end
|
91
|
+
events.map do |event|
|
92
|
+
Mash.new(event.attributes)
|
93
|
+
end
|
61
94
|
end
|
62
95
|
end
|
63
96
|
|
97
|
+
# @return [Array<String>] default attributes for events
|
64
98
|
def default_attributes
|
65
|
-
%w(
|
99
|
+
%w(time resource_logical_id resource_status resource_status_reason)
|
66
100
|
end
|
67
101
|
end
|
68
102
|
end
|
@@ -1,20 +1,37 @@
|
|
1
|
-
require 'knife-cloudformation
|
2
|
-
require 'knife-cloudformation/export'
|
1
|
+
require 'knife-cloudformation'
|
3
2
|
|
4
3
|
class Chef
|
5
4
|
class Knife
|
5
|
+
# Cloudformation export command
|
6
6
|
class CloudformationExport < Knife
|
7
7
|
|
8
|
-
include KnifeCloudformation::
|
8
|
+
include KnifeCloudformation::Knife::Base
|
9
|
+
include KnifeCloudformation::Utils::ObjectStorage
|
9
10
|
|
10
|
-
banner 'knife cloudformation export
|
11
|
+
banner 'knife cloudformation export STACK_NAME'
|
11
12
|
|
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
|
+
)
|
13
18
|
|
14
19
|
option(:path,
|
15
20
|
:long => '--export-path PATH',
|
16
|
-
:description => '
|
17
|
-
:proc => lambda{|v| Chef::Config[:knife][:cloudformation][:
|
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}
|
18
35
|
)
|
19
36
|
|
20
37
|
option(:ignore_parameters,
|
@@ -22,8 +39,7 @@ class Chef
|
|
22
39
|
:long => '--exclude-parameter NAME',
|
23
40
|
:description => 'Exclude parameter from export (can be used multiple times)',
|
24
41
|
:proc => lambda{|v|
|
25
|
-
Chef::Config[:knife][:cloudformation][:
|
26
|
-
Chef::Config[:knife][:cloudformation][:ignore_parameters].push(v).uniq!
|
42
|
+
Chef::Config[:knife][:cloudformation][:export][:ignore_parameters].push(v).uniq!
|
27
43
|
}
|
28
44
|
)
|
29
45
|
|
@@ -31,7 +47,7 @@ class Chef
|
|
31
47
|
:long => '--chef-environment-parameter NAME',
|
32
48
|
:description => 'Parameter used within stack to specify Chef environment',
|
33
49
|
:proc => lambda{|v|
|
34
|
-
Chef::Config[:knife][:cloudformation][:chef_environment_parameter] = v
|
50
|
+
Chef::Config[:knife][:cloudformation][:export][:chef_environment_parameter] = v
|
35
51
|
}
|
36
52
|
)
|
37
53
|
|
@@ -40,38 +56,105 @@ class Chef
|
|
40
56
|
:boolean => true,
|
41
57
|
:default => true,
|
42
58
|
:description => 'Freezes first run files',
|
43
|
-
:proc => lambda{|v| Chef::Config[:knife][:cloudformation][:chef_popsicle] = v }
|
59
|
+
:proc => lambda{|v| Chef::Config[:knife][:cloudformation][:export][:chef_popsicle] = v }
|
44
60
|
)
|
45
61
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
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}"
|
58
73
|
ui.confirm 'Perform export'
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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)
|
63
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
|
64
102
|
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
70
116
|
end
|
71
117
|
else
|
72
|
-
|
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))
|
73
157
|
end
|
74
|
-
ui.info "#{ui.color('Stack export', :bold)} (#{name_args.first}): #{ui.color('complete', :green)}"
|
75
158
|
end
|
76
159
|
|
77
160
|
end
|