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