knife-cloudformation 0.2.10 → 0.2.12
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 +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
|