knife-cloudformation 0.2.10 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/chef/knife/cloudformation_create.rb +28 -18
- data/lib/chef/knife/cloudformation_update.rb +67 -52
- data/lib/knife-cloudformation/knife/stack.rb +46 -5
- data/lib/knife-cloudformation/knife/template.rb +18 -14
- data/lib/knife-cloudformation/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23cf366cd18f3224855b2d3e09863a0695dbb7eb
|
4
|
+
data.tar.gz: 2e0f8941b6131cdfcc5eadbcb9f50681b8d0119d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e69f7ec1cab91bcf3805ac23771a9162e41bd3b52fe921992821af2bd256043e1edafd8c422a338c8a05966de2e743152d1c06a260e29d36401fa0aa52180f43
|
7
|
+
data.tar.gz: 7c0de5e87fc4c5631037dc1a115dd3ca3dac39c89836e5664c9df5edfac2f92705b6a776c6f4a6d0178adf98699663f7145f28fb511f39f328969959efb4f7ea
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## v0.2.12
|
2
|
+
* Use template to provide logical parameter ordering on stack update
|
3
|
+
* Only set parameters when not the default template value
|
4
|
+
* Do not save nested stacks to remote bucket when in print-only mode
|
5
|
+
* Add initial support for un-nested stack create and update
|
6
|
+
* Fix nested stack flagging usage
|
7
|
+
|
1
8
|
## v0.2.10
|
2
9
|
* Add initial nested stack support
|
3
10
|
|
@@ -71,6 +71,7 @@ class Chef
|
|
71
71
|
file = Chef::Config[:knife][:cloudformation][:template]
|
72
72
|
else
|
73
73
|
file = load_template_file
|
74
|
+
nested_stacks = file.delete('sfn_nested_stack')
|
74
75
|
end
|
75
76
|
ui.info "#{ui.color('Cloud Formation:', :bold)} #{ui.color('create', :green)}"
|
76
77
|
stack_info = "#{ui.color('Name:', :bold)} #{name}"
|
@@ -85,30 +86,39 @@ class Chef
|
|
85
86
|
ui.info " -> #{stack_info}"
|
86
87
|
end
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
if(nested_stacks)
|
90
|
+
|
91
|
+
if(config[:print_only])
|
92
|
+
ui.info _format_json(translate_template(file))
|
93
|
+
exit 0
|
94
|
+
end
|
95
|
+
|
96
|
+
unpack_nesting(name, file, :create)
|
97
|
+
|
98
|
+
else
|
99
|
+
|
100
|
+
stack = provider.connection.stacks.build(
|
101
|
+
Chef::Config[:knife][:cloudformation][:options].dup.merge(
|
102
|
+
:name => name,
|
103
|
+
:template => file
|
104
|
+
)
|
92
105
|
)
|
93
|
-
)
|
94
106
|
|
95
|
-
|
96
|
-
|
107
|
+
apply_stacks!(stack)
|
108
|
+
stack.template = KnifeCloudformation::Utils::StackParameterScrubber.scrub!(stack.template)
|
97
109
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
110
|
+
if(config[:print_only])
|
111
|
+
ui.info _format_json(translate_template(stack.template))
|
112
|
+
exit 0
|
113
|
+
end
|
102
114
|
|
103
|
-
|
104
|
-
|
105
|
-
# apply-stack as we iterate the list
|
115
|
+
populate_parameters!(stack.template)
|
116
|
+
stack.parameters = Chef::Config[:knife][:cloudformation][:options][:parameters]
|
106
117
|
|
107
|
-
|
108
|
-
|
118
|
+
stack.template = translate_template(stack.template)
|
119
|
+
stack.save
|
109
120
|
|
110
|
-
|
111
|
-
stack.save
|
121
|
+
end
|
112
122
|
|
113
123
|
if(Chef::Config[:knife][:cloudformation][:poll])
|
114
124
|
poll_stack(stack.name)
|
@@ -37,56 +37,82 @@ class Chef
|
|
37
37
|
exit 1
|
38
38
|
end
|
39
39
|
|
40
|
-
|
40
|
+
stack_info = "#{ui.color('Name:', :bold)} #{name}"
|
41
41
|
|
42
|
-
if(
|
43
|
-
|
44
|
-
stack_info
|
42
|
+
if(Chef::Config[:knife][:cloudformation][:file])
|
43
|
+
file = load_template_file
|
44
|
+
stack_info << " #{ui.color('Path:', :bold)} #{Chef::Config[:knife][:cloudformation][:file]}"
|
45
|
+
nested_stacks = file.delete('sfn_nested_stack')
|
46
|
+
end
|
45
47
|
|
46
|
-
|
47
|
-
file = load_template_file
|
48
|
-
stack_info << " #{ui.color('Path:', :bold)} #{Chef::Config[:knife][:cloudformation][:file]}"
|
49
|
-
else
|
50
|
-
stack_info << " #{ui.color('(no temlate update)', :yellow)}"
|
51
|
-
end
|
52
|
-
ui.info " -> #{stack_info}"
|
48
|
+
if(nested_stacks)
|
53
49
|
|
54
|
-
|
50
|
+
unpack_nesting(name, file, :update)
|
55
51
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
52
|
+
else
|
53
|
+
stack = provider.connection.stacks.get(name)
|
54
|
+
|
55
|
+
if(stack)
|
56
|
+
ui.info "#{ui.color('Cloud Formation:', :bold)} #{ui.color('update', :green)}"
|
57
|
+
|
58
|
+
unless(file)
|
59
|
+
if(Chef::Config[:knife][:cloudformation][:template])
|
60
|
+
file = Chef::Config[:knife][:cloudformation][:template]
|
61
|
+
stack_info << " #{ui.color('(template provided)', :green)}"
|
62
|
+
else
|
63
|
+
stack_info << " #{ui.color('(no template update)', :yellow)}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
ui.info " -> #{stack_info}"
|
67
|
+
|
68
|
+
apply_stacks!(stack)
|
69
|
+
|
70
|
+
if(file)
|
71
|
+
populate_parameters!(file, stack.parameters)
|
72
|
+
stack.template = translate_template(file)
|
73
|
+
stack.parameters = Chef::Config[:knife][:cloudformation][:parameters]
|
74
|
+
stack.template = KnifeCloudformation::Utils::StackParameterScrubber.scrub!(stack.template)
|
75
|
+
else
|
76
|
+
populate_parameters!(stack.template, stack.parameters)
|
77
|
+
stack.parameters = Chef::Config[:knife][:cloudformation][:parameters]
|
78
|
+
end
|
63
79
|
|
64
|
-
|
80
|
+
begin
|
81
|
+
stack.save
|
82
|
+
rescue Miasma::Error::ApiError::RequestError => e
|
83
|
+
if(e.message.downcase.include?('no updates')) # :'(
|
84
|
+
ui.warn "No updates detected for stack (#{stack.name})"
|
85
|
+
else
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
end
|
65
89
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
90
|
+
if(Chef::Config[:knife][:cloudformation][:poll])
|
91
|
+
poll_stack(stack.name)
|
92
|
+
if(stack.success?)
|
93
|
+
ui.info "Stack update complete: #{ui.color('SUCCESS', :green)}"
|
94
|
+
knife_output = Chef::Knife::CloudformationDescribe.new
|
95
|
+
knife_output.name_args.push(name)
|
96
|
+
knife_output.config[:outputs] = true
|
97
|
+
knife_output.run
|
98
|
+
else
|
99
|
+
ui.fatal "Update of stack #{ui.color(name, :bold)}: #{ui.color('FAILED', :red, :bold)}"
|
100
|
+
ui.info ""
|
101
|
+
knife_inspect = Chef::Knife::CloudformationInspect.new
|
102
|
+
knife_inspect.name_args.push(name)
|
103
|
+
knife_inspect.config[:instance_failure] = true
|
104
|
+
knife_inspect.run
|
105
|
+
exit 1
|
106
|
+
end
|
74
107
|
else
|
75
|
-
ui.
|
76
|
-
ui.info ""
|
77
|
-
knife_inspect = Chef::Knife::CloudformationInspect.new
|
78
|
-
knife_inspect.name_args.push(name)
|
79
|
-
knife_inspect.config[:instance_failure] = true
|
80
|
-
knife_inspect.run
|
81
|
-
exit 1
|
108
|
+
ui.warn 'Stack state polling has been disabled.'
|
109
|
+
ui.info "Stack update initialized for #{ui.color(name, :green)}"
|
82
110
|
end
|
83
111
|
else
|
84
|
-
ui.
|
85
|
-
|
112
|
+
ui.fatal "Failed to locate requested stack: #{ui.color(name, :red, :bold)}"
|
113
|
+
exit -1
|
86
114
|
end
|
87
|
-
|
88
|
-
ui.fatal "Failed to locate requested stack: #{ui.color(name, :red, :bold)}"
|
89
|
-
exit -1
|
115
|
+
|
90
116
|
end
|
91
117
|
end
|
92
118
|
|
@@ -116,7 +142,7 @@ class Chef
|
|
116
142
|
remote_stack = provider.connection.stacks.get(stack_name)
|
117
143
|
if(remote_stack)
|
118
144
|
remote_stack.parameters.each do |key, value|
|
119
|
-
next if Chef::Config[:knife][:cloudformation]
|
145
|
+
next if Chef::Config[:knife][:cloudformation].fetch(:stacks, {}).fetch(:ignore_parameters, []).include?(key)
|
120
146
|
if(stack.parameters.has_key?(key))
|
121
147
|
stack.parameters[key] = value
|
122
148
|
end
|
@@ -129,17 +155,6 @@ class Chef
|
|
129
155
|
stack
|
130
156
|
end
|
131
157
|
|
132
|
-
# Update parameters within existing stack
|
133
|
-
#
|
134
|
-
# @param stack [Miasma::Models::Orchestration::Stack]
|
135
|
-
# @return [Miasma::Models::Orchestration::Stack]
|
136
|
-
def stack_parameters_update!(stack)
|
137
|
-
stack.parameters.each do |key, value|
|
138
|
-
answer = ui.ask_question("#{key.split(/([A-Z]+[^A-Z]*)/).find_all{|s|!s.empty?}.join(' ')}: ", :default => value)
|
139
|
-
stack.parameters[key] = answer
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
158
|
end
|
144
159
|
end
|
145
160
|
end
|
@@ -11,25 +11,66 @@ module KnifeCloudformation
|
|
11
11
|
# maximum number of attempts to get valid parameter value
|
12
12
|
MAX_PARAMETER_ATTEMPTS = 5
|
13
13
|
|
14
|
+
# Unpack nested stack and run action on each stack, applying
|
15
|
+
# the previous stacks automatically
|
16
|
+
#
|
17
|
+
# @param name [String] container stack name
|
18
|
+
# @param file [Hash] stack template
|
19
|
+
# @param action [String] create or update
|
20
|
+
# @return [TrueClass]
|
21
|
+
def unpack_nesting(name, file, action)
|
22
|
+
|
23
|
+
# @todo move this init into setup
|
24
|
+
Chef::Config[:knife][:cloudformation][action.to_sym] ||= Mash.new
|
25
|
+
Chef::Config[:knife][:cloudformation][action.to_sym][:apply_stacks] ||= []
|
26
|
+
|
27
|
+
orig_params = Chef::Config[:knife][:cloudformation][:options][:parameters]
|
28
|
+
|
29
|
+
file['Resources'].each do |stack_resource_name, stack_resource|
|
30
|
+
|
31
|
+
nested_stack_name = "#{name}-#{stack_resource_name}"
|
32
|
+
nested_stack_template = stack_resource['Properties']['Stack']
|
33
|
+
Chef::Config[:knife][:cloudformation][:options][:parameters] = orig_params
|
34
|
+
|
35
|
+
klass = Chef::Knife.const_get("Cloudformation#{action.to_s.capitalize}")
|
36
|
+
nested_stack_runner = klass.new
|
37
|
+
nested_stack_runner.name_args.push(nested_stack_name)
|
38
|
+
Chef::Config[:knife][:cloudformation][:template] = nested_stack_template
|
39
|
+
nested_stack_runner.run
|
40
|
+
Chef::Config[:knife][:cloudformation][action.to_sym][:apply_stacks].push(nested_stack_name).uniq!
|
41
|
+
Chef::Config[:knife][:cloudformation][:template] = nil
|
42
|
+
provider.connection.stacks.reload
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
14
49
|
# Prompt for parameter values and store result
|
15
50
|
#
|
16
51
|
# @param stack [Hash] stack template
|
17
52
|
# @return [Hash]
|
18
|
-
def populate_parameters!(stack)
|
53
|
+
def populate_parameters!(stack, current_params={})
|
19
54
|
if(Chef::Config[:knife][:cloudformation][:interactive_parameters])
|
20
|
-
if(stack['Parameters']
|
55
|
+
if(stack['Parameters'])
|
21
56
|
Chef::Config[:knife][:cloudformation][:options][:parameters] ||= Mash.new
|
22
|
-
stack.fetch('Parameters',
|
57
|
+
stack.fetch('Parameters', {}).each do |k,v|
|
23
58
|
next if Chef::Config[:knife][:cloudformation][:options][:parameters][k]
|
24
59
|
attempt = 0
|
25
60
|
valid = false
|
26
61
|
until(valid)
|
27
62
|
attempt += 1
|
28
|
-
default = Chef::Config[:knife][:cloudformation][:options][:parameters]
|
63
|
+
default = Chef::Config[:knife][:cloudformation][:options][:parameters].fetch(
|
64
|
+
k, current_params.fetch(
|
65
|
+
k, v['Default']
|
66
|
+
)
|
67
|
+
)
|
29
68
|
answer = ui.ask_question("#{k.split(/([A-Z]+[^A-Z]*)/).find_all{|s|!s.empty?}.join(' ')}: ", :default => default)
|
30
69
|
validation = KnifeCloudformation::Utils::StackParameterValidator.validate(answer, v)
|
31
70
|
if(validation == true)
|
32
|
-
|
71
|
+
unless(answer == default)
|
72
|
+
Chef::Config[:knife][:cloudformation][:options][:parameters][k] = answer
|
73
|
+
end
|
33
74
|
valid = true
|
34
75
|
else
|
35
76
|
validation.each do |validation_error|
|
@@ -30,27 +30,31 @@ module KnifeCloudformation
|
|
30
30
|
elsif(Chef::Config[:knife][:cloudformation][:file])
|
31
31
|
if(Chef::Config[:knife][:cloudformation][:processing])
|
32
32
|
sf = SparkleFormation.compile(Chef::Config[:knife][:cloudformation][:file], :sparkle)
|
33
|
+
if(sf.nested? && !sf.isolated_nests?)
|
34
|
+
raise TypeError.new('Template does not contain isolated stack nesting! Cannot process in existing state.')
|
35
|
+
end
|
33
36
|
if(sf.nested? && Chef::Config[:knife][:cloudformation][:apply_nesting])
|
34
37
|
sf.apply_nesting do |stack_name, stack_definition|
|
35
38
|
bucket = provider.connection.api_for(:storage).buckets.get(
|
36
39
|
Chef::Config[:knife][:cloudformation][:nesting_bucket]
|
37
40
|
)
|
38
|
-
|
39
|
-
|
41
|
+
if(options[:print_only])
|
42
|
+
"http://example.com/bucket/#{name_args.first}_#{stack_name}.json"
|
43
|
+
else
|
44
|
+
unless(bucket)
|
45
|
+
raise "Failed to locate configured bucket for stack template storage (#{bucket})!"
|
46
|
+
end
|
47
|
+
file = bucket.files.build
|
48
|
+
file.name = "#{name_args.first}_#{stack_name}.json"
|
49
|
+
file.content_type = 'text/json'
|
50
|
+
file.body = MultiJson.dump(KnifeCloudformation::Utils::StackParameterScrubber.scrub!(stack_definition))
|
51
|
+
file.save
|
52
|
+
# TODO: what if we need extra params?
|
53
|
+
url = URI.parse(file.url)
|
54
|
+
"#{url.scheme}://#{url.host}#{url.path}"
|
40
55
|
end
|
41
|
-
|
42
|
-
file.name = "#{name_args.first}_#{stack_name}.json"
|
43
|
-
file.content_type = 'text/json'
|
44
|
-
file.body = MultiJson.dump(KnifeCloudformation::Utils::StackParameterScrubber.scrub!(stack_definition))
|
45
|
-
file.save
|
46
|
-
# TODO: what if we need extra params?
|
47
|
-
url = URI.parse(file.url)
|
48
|
-
"#{url.scheme}://#{url.host}#{url.path}"
|
49
|
-
end
|
56
|
+
end.merge('sfn_nested_stack' => !!sf.nested?)
|
50
57
|
else
|
51
|
-
if(sf.nested? && !sf.isolated_nests?)
|
52
|
-
raise TypeError.new('Template does not contain isolated stack nesting! Cannot process in existing state.')
|
53
|
-
end
|
54
58
|
sf.dump
|
55
59
|
end
|
56
60
|
else
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-cloudformation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Roberts
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef
|