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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 83a445d83a052179cb88558b3f2b1a159a9c3c6f
4
- data.tar.gz: e097d47c746572b17f0015be0570754816f83bc6
3
+ metadata.gz: 23cf366cd18f3224855b2d3e09863a0695dbb7eb
4
+ data.tar.gz: 2e0f8941b6131cdfcc5eadbcb9f50681b8d0119d
5
5
  SHA512:
6
- metadata.gz: cb4823fea15e14b41b321f56606788050a2f8b61cc482c9a2637ad49a60da9f4ca0f4245a798c4762318c28b315af70ea189b7e9ff4f64dc3798c2609b231344
7
- data.tar.gz: a2ded11aad1e6847373b48ed92f21aacb4c357324ff2789538a84ee3eafe044054eccca83404a0d4cc0389fdb9aa9b505a7f0b9cb5038794f9c1946e726cdbec
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
- stack = provider.connection.stacks.build(
89
- Chef::Config[:knife][:cloudformation][:options].dup.merge(
90
- :name => name,
91
- :template => file
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
- apply_stacks!(stack)
96
- stack.template = KnifeCloudformation::Utils::StackParameterScrubber.scrub!(stack.template)
107
+ apply_stacks!(stack)
108
+ stack.template = KnifeCloudformation::Utils::StackParameterScrubber.scrub!(stack.template)
97
109
 
98
- if(config[:print_only])
99
- ui.info _format_json(translate_template(stack.template))
100
- exit 0
101
- end
110
+ if(config[:print_only])
111
+ ui.info _format_json(translate_template(stack.template))
112
+ exit 0
113
+ end
102
114
 
103
- # TODO: if nested stacks and not extracted (based on config
104
- # option, break resources out and iterate each stack and use
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
- populate_parameters!(stack.template)
108
- stack.parameters = Chef::Config[:knife][:cloudformation][:options][:parameters]
118
+ stack.template = translate_template(stack.template)
119
+ stack.save
109
120
 
110
- stack.template = translate_template(stack.template)
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
- stack = provider.connection.stacks.get(name)
40
+ stack_info = "#{ui.color('Name:', :bold)} #{name}"
41
41
 
42
- if(stack)
43
- ui.info "#{ui.color('Cloud Formation:', :bold)} #{ui.color('update', :green)}"
44
- stack_info = "#{ui.color('Name:', :bold)} #{name}"
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
- if(Chef::Config[:knife][:cloudformation][:file])
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
- apply_stacks!(stack)
50
+ unpack_nesting(name, file, :update)
55
51
 
56
- if(file)
57
- stack_parameters_update!(stack)
58
- stack.template = translate_template(file)
59
- stack.parameters = Chef::Config[:knife][:cloudformation][:parameters]
60
- else
61
- stack_parameters_update!(stack)
62
- end
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
- stack.save
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
- if(Chef::Config[:knife][:cloudformation][:poll])
67
- poll_stack(stack.name)
68
- if(stack.success?)
69
- ui.info "Stack update complete: #{ui.color('SUCCESS', :green)}"
70
- knife_output = Chef::Knife::CloudformationDescribe.new
71
- knife_output.name_args.push(name)
72
- knife_output.config[:outputs] = true
73
- knife_output.run
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.fatal "Update of stack #{ui.color(name, :bold)}: #{ui.color('FAILED', :red, :bold)}"
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.warn 'Stack state polling has been disabled.'
85
- ui.info "Stack update initialized for #{ui.color(name, :green)}"
112
+ ui.fatal "Failed to locate requested stack: #{ui.color(name, :red, :bold)}"
113
+ exit -1
86
114
  end
87
- else
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][:stacks][:ignore_parameters].include?(key)
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'] || stack['parameters'])
55
+ if(stack['Parameters'])
21
56
  Chef::Config[:knife][:cloudformation][:options][:parameters] ||= Mash.new
22
- stack.fetch('Parameters', stack.fetch('parameters', {})).each do |k,v|
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][k] || v['Default'] || v['default']
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
- Chef::Config[:knife][:cloudformation][:options][:parameters][k] = answer
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
- unless(bucket)
39
- raise "Failed to locate configured bucket for stack template storage (#{bucket})!"
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
- file = bucket.files.build
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
@@ -1,4 +1,4 @@
1
1
  module KnifeCloudformation
2
2
  # Current library version
3
- VERSION = Gem::Version.new('0.2.10')
3
+ VERSION = Gem::Version.new('0.2.12')
4
4
  end
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.10
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: 2014-12-22 00:00:00.000000000 Z
11
+ date: 2015-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef