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,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
|