stackup 0.7.0 → 0.8.0
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/README.md +12 -1
- data/bin/stackup +66 -25
- data/examples/Rakefile +11 -0
- data/examples/{parameters.yml → parameters-terse.yml} +0 -0
- data/examples/{parameters.json → parameters-verbose.json} +0 -0
- data/lib/stackup/differ.rb +32 -0
- data/lib/stackup/parameters.rb +76 -0
- data/lib/stackup/rake_tasks.rb +50 -12
- data/lib/stackup/stack.rb +45 -43
- data/lib/stackup/version.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/stackup/parameters_spec.rb +149 -0
- data/spec/stackup/stack_spec.rb +63 -5
- metadata +10 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2f56d7cc442a15b14e0df0bd2ed9b9c39b34197
|
4
|
+
data.tar.gz: 187f7372696c5f6ae84530e218bb9c0c0bf691bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f33df243de76851d2f63c8776b8ee44eb2634219fb838adee0d096ecca2fffc19feeb2effcc50aadded2d0f4e04dec2fd7a5640045fe4362782b19874ae81bc1
|
7
|
+
data.tar.gz: b72c70703f52d5be553ffff3a2c98e360b7cf008aa8f03c993d3920888c84d897a55d38ab8705655d567b6bec52c1b31fa7e377ee3ecb86650e7188cb28e7ae9
|
data/README.md
CHANGED
@@ -37,6 +37,8 @@ Called with `--list`, it will list stacks:
|
|
37
37
|
foo-bar-test
|
38
38
|
zzz-production
|
39
39
|
|
40
|
+
The command-line support inputs (template and parameters) in either JSON or YAML format.
|
41
|
+
|
40
42
|
### Stack create/update
|
41
43
|
|
42
44
|
Use sub-command "up" to create or update a stack, as appropriate:
|
@@ -79,7 +81,7 @@ Stackup integrates with Rake to generate handy tasks for managing a stack, e.g.
|
|
79
81
|
|
80
82
|
require "stackup/rake_tasks"
|
81
83
|
|
82
|
-
Stackup::RakeTasks("app") do |t|
|
84
|
+
Stackup::RakeTasks.new("app") do |t|
|
83
85
|
t.stack = "my-app"
|
84
86
|
t.template = "app-template.json"
|
85
87
|
end
|
@@ -90,3 +92,12 @@ providing tasks:
|
|
90
92
|
rake app:down # Delete my-app stack
|
91
93
|
rake app:inspect # Show my-app stack outputs and resources
|
92
94
|
rake app:up # Update my-app stack
|
95
|
+
|
96
|
+
Parameters and tags may be specified via files, or as a Hash, e.g.
|
97
|
+
|
98
|
+
Stackup::RakeTasks.new("app") do |t|
|
99
|
+
t.stack = "my-app"
|
100
|
+
t.template = "app-template.json"
|
101
|
+
t.parameters = "production-params.json"
|
102
|
+
t.tags = { "environment" => "production" }
|
103
|
+
end
|
data/bin/stackup
CHANGED
@@ -4,9 +4,9 @@ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
|
|
4
4
|
|
5
5
|
require "clamp"
|
6
6
|
require "console_logger"
|
7
|
-
require "diffy"
|
8
7
|
require "multi_json"
|
9
8
|
require "stackup"
|
9
|
+
require "stackup/differ"
|
10
10
|
require "stackup/version"
|
11
11
|
require "yaml"
|
12
12
|
|
@@ -22,6 +22,14 @@ Clamp do
|
|
22
22
|
|
23
23
|
option ["-Y", "--yaml"], :flag, "output data in YAML format"
|
24
24
|
|
25
|
+
option ["--region"], "REGION", "set region" do |arg|
|
26
|
+
unless arg =~ /^[a-z]{2}-[a-z]+-\d$/
|
27
|
+
fail ArgumentError, "#{arg.inspect} doesn't look like a region"
|
28
|
+
end
|
29
|
+
Aws.config.update(:region => arg)
|
30
|
+
arg
|
31
|
+
end
|
32
|
+
|
25
33
|
option "--debug", :flag, "enable debugging"
|
26
34
|
|
27
35
|
option ["--version"], :flag, "display version" do
|
@@ -103,6 +111,13 @@ Clamp do
|
|
103
111
|
option ["-p", "--parameters"], "FILE", "parameters file",
|
104
112
|
:attribute_name => :parameters_file
|
105
113
|
|
114
|
+
option ["-o", "--override"], "PARAM=VALUE", "parameters overrides",
|
115
|
+
:multivalued => true,
|
116
|
+
:attribute_name => :override_list
|
117
|
+
|
118
|
+
option "--tags", "FILE", "stack tags file",
|
119
|
+
:attribute_name => :tags_file
|
120
|
+
|
106
121
|
option "--policy", "FILE", "stack policy file",
|
107
122
|
:attribute_name => :policy_file
|
108
123
|
|
@@ -117,7 +132,8 @@ Clamp do
|
|
117
132
|
options = {}
|
118
133
|
options[:template] = load_data(template_file) if template_file
|
119
134
|
options[:on_failure] = on_failure
|
120
|
-
options[:parameters] =
|
135
|
+
options[:parameters] = parameters
|
136
|
+
options[:tags] = load_data(tags_file) if tags_file
|
121
137
|
options[:stack_policy] = load_data(policy_file) if policy_file
|
122
138
|
options[:use_previous_template] = use_previous_template?
|
123
139
|
report_change do
|
@@ -125,6 +141,26 @@ Clamp do
|
|
125
141
|
end
|
126
142
|
end
|
127
143
|
|
144
|
+
private
|
145
|
+
|
146
|
+
def parameters
|
147
|
+
parameters_from_file.merge(parameter_overrides)
|
148
|
+
end
|
149
|
+
|
150
|
+
def parameters_from_file
|
151
|
+
return {} unless parameters_file
|
152
|
+
Stackup::Parameters.new(load_data(parameters_file)).to_hash
|
153
|
+
end
|
154
|
+
|
155
|
+
def parameter_overrides
|
156
|
+
{}.tap do |result|
|
157
|
+
override_list.each do |override|
|
158
|
+
key, value = override.split("=", 2)
|
159
|
+
result[key] = value
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
128
164
|
end
|
129
165
|
|
130
166
|
subcommand "diff", "Compare template/params to current stack." do
|
@@ -135,41 +171,45 @@ Clamp do
|
|
135
171
|
option ["-p", "--parameters"], "FILE", "parameters file",
|
136
172
|
:attribute_name => :parameters_file
|
137
173
|
|
174
|
+
option "--tags", "FILE", "stack tags file",
|
175
|
+
:attribute_name => :tags_file
|
176
|
+
|
138
177
|
option "--diff-format", "FORMAT", "'text', 'color', or 'html'", :default => "color"
|
139
178
|
|
140
179
|
def execute
|
141
|
-
|
180
|
+
current = {}
|
181
|
+
planned = {}
|
142
182
|
if template_file
|
143
|
-
|
144
|
-
|
183
|
+
current["Template"] = stack.template
|
184
|
+
planned["Template"] = load_data(template_file)
|
145
185
|
end
|
146
186
|
if parameters_file
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
187
|
+
current["Parameters"] = existing_parameters.sort.to_h
|
188
|
+
planned["Parameters"] = new_parameters.sort.to_h
|
189
|
+
end
|
190
|
+
if tags_file
|
191
|
+
current["Tags"] = stack.tags.sort.to_h
|
192
|
+
planned["Tags"] = load_data(tags_file).sort.to_h
|
151
193
|
end
|
194
|
+
signal_usage_error "specify '--template' or '--parameters'" if planned.empty?
|
195
|
+
puts differ.diff(current, planned)
|
152
196
|
end
|
153
197
|
|
154
198
|
private
|
155
199
|
|
156
|
-
def
|
157
|
-
|
158
|
-
pending = format_data(pending_data) + "\n"
|
159
|
-
diff = Diffy::Diff.new(existing, pending).to_s(diff_format.to_sym)
|
160
|
-
return if diff =~ /\A\s*\Z/
|
161
|
-
puts diff
|
200
|
+
def differ
|
201
|
+
Stackup::Differ.new(diff_format, &method(:format_data))
|
162
202
|
end
|
163
203
|
|
164
|
-
def
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
204
|
+
def existing_parameters
|
205
|
+
@existing_parameters ||= stack.parameters
|
206
|
+
end
|
207
|
+
|
208
|
+
def new_parameters
|
209
|
+
result = load_data(parameters_file)
|
210
|
+
result = Stackup::Parameters.new(result).to_hash
|
211
|
+
result = existing_parameters.merge(result)
|
212
|
+
result.sort
|
173
213
|
end
|
174
214
|
|
175
215
|
end
|
@@ -284,12 +324,13 @@ Clamp do
|
|
284
324
|
|
285
325
|
end
|
286
326
|
|
287
|
-
subcommand "inspect", "
|
327
|
+
subcommand "inspect", "Display stack particulars" do
|
288
328
|
|
289
329
|
def execute
|
290
330
|
data = {
|
291
331
|
"Status" => stack.status,
|
292
332
|
"Parameters" => stack.parameters,
|
333
|
+
"Tags" => stack.tags,
|
293
334
|
"Resources" => stack.resources,
|
294
335
|
"Outputs" => stack.outputs
|
295
336
|
}
|
data/examples/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "stackup/rake_tasks"
|
2
|
+
|
3
|
+
Stackup::RakeTasks.new("demo") do |t|
|
4
|
+
t.stack = ENV.fetch("STACKUP_DEMO_STACK", "stackup-demo")
|
5
|
+
t.template = "template.json"
|
6
|
+
t.parameters = "parameters-verbose.json"
|
7
|
+
t.tags = {
|
8
|
+
"environment" => "dev",
|
9
|
+
"team" => "rea-oss"
|
10
|
+
}
|
11
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "diffy"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module Stackup
|
5
|
+
|
6
|
+
# Generates diffs of data.
|
7
|
+
#
|
8
|
+
class Differ
|
9
|
+
|
10
|
+
def initialize(diff_style = :color, &data_formatter)
|
11
|
+
@diff_style = diff_style.to_sym
|
12
|
+
@data_formatter = data_formatter || YAML.method(:dump)
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :diff_style
|
16
|
+
|
17
|
+
def diff(existing_data, pending_data)
|
18
|
+
existing = format(existing_data) + "\n"
|
19
|
+
pending = format(pending_data) + "\n"
|
20
|
+
diff = Diffy::Diff.new(existing, pending).to_s(diff_style)
|
21
|
+
diff unless diff =~ /\A\s*\Z/
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def format(data)
|
27
|
+
@data_formatter.call(data)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Stackup
|
2
|
+
|
3
|
+
# Parameters to a CloudFormation template.
|
4
|
+
#
|
5
|
+
class Parameters
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def new(arg)
|
10
|
+
arg = hashify(arg) unless arg.is_a?(Hash)
|
11
|
+
super(arg)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def hashify(parameters)
|
17
|
+
{}.tap do |result|
|
18
|
+
parameters.each do |p|
|
19
|
+
p_struct = ParameterStruct.new(p)
|
20
|
+
result[p_struct.key] = p_struct.value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(parameter_hash)
|
28
|
+
@parameter_hash = parameter_hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash
|
32
|
+
@parameter_hash.dup
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_a
|
36
|
+
@parameter_hash.map do |key, value|
|
37
|
+
{ :parameter_key => key }.tap do |record|
|
38
|
+
if value == :use_previous_value
|
39
|
+
record[:use_previous_value] = true
|
40
|
+
else
|
41
|
+
record[:parameter_value] = value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class ParameterStruct
|
50
|
+
|
51
|
+
def initialize(attributes)
|
52
|
+
attributes.each do |name, value|
|
53
|
+
if name.respond_to?(:gsub)
|
54
|
+
name = name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
55
|
+
end
|
56
|
+
public_send("#{name}=", value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_accessor :parameter_key
|
61
|
+
attr_accessor :parameter_value
|
62
|
+
attr_accessor :use_previous_value
|
63
|
+
|
64
|
+
alias_method :key, :parameter_key
|
65
|
+
|
66
|
+
def value
|
67
|
+
if use_previous_value
|
68
|
+
:use_previous_value
|
69
|
+
else
|
70
|
+
parameter_value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/lib/stackup/rake_tasks.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require "rake/tasklib"
|
2
|
+
require "tempfile"
|
3
|
+
require "yaml"
|
2
4
|
|
3
5
|
module Stackup
|
4
6
|
|
@@ -10,6 +12,7 @@ module Stackup
|
|
10
12
|
attr_accessor :stack
|
11
13
|
attr_accessor :template
|
12
14
|
attr_accessor :parameters
|
15
|
+
attr_accessor :tags
|
13
16
|
|
14
17
|
alias_method :namespace=, :name=
|
15
18
|
|
@@ -23,24 +26,24 @@ module Stackup
|
|
23
26
|
define
|
24
27
|
end
|
25
28
|
|
29
|
+
# path to the "stackup" executable
|
30
|
+
STACKUP_CLI = File.expand_path("../../../bin/stackup", __FILE__)
|
31
|
+
|
26
32
|
def stackup(*rest)
|
27
|
-
sh "
|
33
|
+
sh STACKUP_CLI, "-Y", stack, *rest
|
28
34
|
end
|
29
35
|
|
30
36
|
def define
|
31
37
|
namespace(name) do
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
if
|
37
|
-
up_args += ["-p", parameters]
|
38
|
-
up_deps += [parameters]
|
39
|
-
end
|
39
|
+
data_options = DataOptions.new
|
40
|
+
data_options["--template"] = template
|
41
|
+
data_options["--parameters"] = parameters if parameters
|
42
|
+
data_options["--tags"] = tags if tags
|
40
43
|
|
41
44
|
desc "Update #{stack} stack"
|
42
|
-
task "up" =>
|
43
|
-
stackup "up", *
|
45
|
+
task "up" => data_options.files do
|
46
|
+
stackup "up", *data_options.to_a
|
44
47
|
end
|
45
48
|
|
46
49
|
desc "Cancel update of #{stack} stack"
|
@@ -49,8 +52,8 @@ module Stackup
|
|
49
52
|
end
|
50
53
|
|
51
54
|
desc "Show pending changes to #{stack} stack"
|
52
|
-
task "diff" =>
|
53
|
-
stackup "diff", *
|
55
|
+
task "diff" => data_options.files do
|
56
|
+
stackup "diff", *data_options.to_a
|
54
57
|
end
|
55
58
|
|
56
59
|
desc "Show #{stack} stack outputs and resources"
|
@@ -66,6 +69,41 @@ module Stackup
|
|
66
69
|
end
|
67
70
|
end
|
68
71
|
|
72
|
+
# Options to "stackup up".
|
73
|
+
#
|
74
|
+
class DataOptions
|
75
|
+
|
76
|
+
def initialize
|
77
|
+
@options = {}
|
78
|
+
end
|
79
|
+
|
80
|
+
def []=(option, file_or_value)
|
81
|
+
@options[option] = file_or_value
|
82
|
+
end
|
83
|
+
|
84
|
+
def files
|
85
|
+
@options.values.grep(String)
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_a
|
89
|
+
[].tap do |result|
|
90
|
+
@options.each do |option, file_or_data|
|
91
|
+
result << option
|
92
|
+
result << maybe_tempfile(file_or_data, option[2..-1])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def maybe_tempfile(file_or_data, type)
|
98
|
+
return file_or_data if file_or_data.is_a?(String)
|
99
|
+
tempfile = Tempfile.new([type, ".yml"])
|
100
|
+
tempfile.write(YAML.dump(file_or_data))
|
101
|
+
tempfile.close
|
102
|
+
tempfile.path.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
69
107
|
end
|
70
108
|
|
71
109
|
end
|
data/lib/stackup/stack.rb
CHANGED
@@ -2,6 +2,7 @@ require "aws-sdk-resources"
|
|
2
2
|
require "logger"
|
3
3
|
require "multi_json"
|
4
4
|
require "stackup/error_handling"
|
5
|
+
require "stackup/parameters"
|
5
6
|
require "stackup/stack_watcher"
|
6
7
|
|
7
8
|
module Stackup
|
@@ -65,8 +66,11 @@ module Stackup
|
|
65
66
|
# @option options [String] :on_failure (ROLLBACK)
|
66
67
|
# if stack creation fails: DO_NOTHING, ROLLBACK, or DELETE
|
67
68
|
# @option options [Hash, Array<Hash>] :parameters
|
68
|
-
# stack parameters, either as a Hash, or
|
69
|
+
# stack parameters, either as a Hash, or an Array of
|
69
70
|
# +Aws::CloudFormation::Types::Parameter+ structures
|
71
|
+
# @option options [Hash, Array<Hash>] :tags
|
72
|
+
# stack tags, either as a Hash, or an Array of
|
73
|
+
# +Aws::CloudFormation::Types::Tag+ structures
|
70
74
|
# @option options [Array<String>] :resource_types
|
71
75
|
# resource types that you have permissions to work with
|
72
76
|
# @option options [Hash] :stack_policy
|
@@ -100,7 +104,10 @@ module Stackup
|
|
100
104
|
options[:template_body] = MultiJson.dump(template_data)
|
101
105
|
end
|
102
106
|
if (parameters = options[:parameters])
|
103
|
-
options[:parameters] =
|
107
|
+
options[:parameters] = Parameters.new(parameters).to_a
|
108
|
+
end
|
109
|
+
if (tags = options[:tags])
|
110
|
+
options[:tags] = normalize_tags(tags)
|
104
111
|
end
|
105
112
|
if (policy_data = options.delete(:stack_policy))
|
106
113
|
options[:stack_policy_body] = MultiJson.dump(policy_data)
|
@@ -188,13 +195,16 @@ module Stackup
|
|
188
195
|
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
189
196
|
#
|
190
197
|
def parameters
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
+
extract_hash(:parameters, :parameter_key, :parameter_value)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Get the current tags.
|
202
|
+
#
|
203
|
+
# @return [Hash] current stack tags
|
204
|
+
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
205
|
+
#
|
206
|
+
def tags
|
207
|
+
extract_hash(:tags, :key, :value)
|
198
208
|
end
|
199
209
|
|
200
210
|
# Get stack outputs.
|
@@ -203,13 +213,7 @@ module Stackup
|
|
203
213
|
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
204
214
|
#
|
205
215
|
def outputs
|
206
|
-
|
207
|
-
{}.tap do |h|
|
208
|
-
cf_stack.outputs.each do |o|
|
209
|
-
h[o.output_key] = o.output_value
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
216
|
+
extract_hash(:outputs, :output_key, :output_value)
|
213
217
|
end
|
214
218
|
|
215
219
|
# Get stack outputs.
|
@@ -219,13 +223,7 @@ module Stackup
|
|
219
223
|
# @raise [Stackup::NoSuchStack] if the stack doesn't exist
|
220
224
|
#
|
221
225
|
def resources
|
222
|
-
|
223
|
-
{}.tap do |h|
|
224
|
-
cf_stack.resource_summaries.each do |r|
|
225
|
-
h[r.logical_resource_id] = r.physical_resource_id
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
226
|
+
extract_hash(:resource_summaries, :logical_resource_id, :physical_resource_id)
|
229
227
|
end
|
230
228
|
|
231
229
|
def watch(zero = true)
|
@@ -268,6 +266,7 @@ module Stackup
|
|
268
266
|
fail StackUpdateError, "stack update failed" unless status == "UPDATE_COMPLETE"
|
269
267
|
status
|
270
268
|
rescue NoUpdateRequired
|
269
|
+
logger.info "No update required"
|
271
270
|
nil
|
272
271
|
end
|
273
272
|
|
@@ -279,7 +278,8 @@ module Stackup
|
|
279
278
|
def event_handler
|
280
279
|
@event_handler ||= lambda do |e|
|
281
280
|
fields = [e.logical_resource_id, e.resource_status, e.resource_status_reason]
|
282
|
-
|
281
|
+
time = e.timestamp.localtime.strftime("%H:%M:%S")
|
282
|
+
logger.info("[#{time}] #{fields.compact.join(' - ')}")
|
283
283
|
end
|
284
284
|
end
|
285
285
|
|
@@ -302,33 +302,35 @@ module Stackup
|
|
302
302
|
end
|
303
303
|
end
|
304
304
|
|
305
|
-
def
|
306
|
-
if
|
307
|
-
|
308
|
-
{
|
309
|
-
:parameter_key => key,
|
310
|
-
:parameter_value => value
|
311
|
-
}
|
305
|
+
def normalize_tags(tags)
|
306
|
+
if tags.is_a?(Hash)
|
307
|
+
tags.map do |key, value|
|
308
|
+
{ :key => key, :value => value }
|
312
309
|
end
|
313
|
-
|
314
|
-
|
315
|
-
normalise_parameter_keys(parameter)
|
310
|
+
else
|
311
|
+
tags
|
316
312
|
end
|
317
313
|
end
|
318
314
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
315
|
+
# Extract data from a collection attribute of the stack.
|
316
|
+
#
|
317
|
+
# @param [Symbol] collection_name collection attribute name
|
318
|
+
# @param [Symbol] key_name name of item attribute that provides key
|
319
|
+
# @param [Symbol] value_name name of item attribute that provides value
|
320
|
+
# @return [Hash<String, String>] mapping of collection
|
321
|
+
#
|
322
|
+
def extract_hash(collection_name, key_name, value_name)
|
323
|
+
handling_validation_error do
|
324
|
+
{}.tap do |result|
|
325
|
+
cf_stack.public_send(collection_name).each do |item|
|
326
|
+
key = item.public_send(key_name)
|
327
|
+
value = item.public_send(value_name)
|
328
|
+
result[key] = value
|
329
|
+
end
|
323
330
|
end
|
324
331
|
end
|
325
332
|
end
|
326
333
|
|
327
|
-
def normalise_parameter_key(s)
|
328
|
-
return s if s.is_a?(Symbol)
|
329
|
-
s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
330
|
-
end
|
331
|
-
|
332
334
|
end
|
333
335
|
|
334
336
|
end
|
data/lib/stackup/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,149 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "stackup/parameters"
|
4
|
+
|
5
|
+
describe Stackup::Parameters do
|
6
|
+
|
7
|
+
context "constructed with a Hash" do
|
8
|
+
|
9
|
+
let(:input_hash) do
|
10
|
+
{
|
11
|
+
"Ami" => "ami-123",
|
12
|
+
"VpcId" => "vpc-456"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
subject(:parameters) { Stackup::Parameters.new(input_hash) }
|
17
|
+
|
18
|
+
describe "#to_hash" do
|
19
|
+
|
20
|
+
it "returns the original Hash" do
|
21
|
+
expect(parameters.to_hash).to eql(input_hash)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#to_a" do
|
27
|
+
|
28
|
+
it "returns an array of parameter records" do
|
29
|
+
expected = [
|
30
|
+
{ :parameter_key => "Ami", :parameter_value => "ami-123" },
|
31
|
+
{ :parameter_key => "VpcId", :parameter_value => "vpc-456" }
|
32
|
+
]
|
33
|
+
expect(parameters.to_a).to eql(expected)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with a :use_previous_value" do
|
39
|
+
|
40
|
+
let(:input_hash) do
|
41
|
+
{
|
42
|
+
"Ami" => :use_previous_value
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#to_hash" do
|
47
|
+
|
48
|
+
it "returns the original Hash" do
|
49
|
+
expect(parameters.to_hash).to eql(input_hash)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#to_a" do
|
55
|
+
|
56
|
+
it "returns a record with :use_previous_value true" do
|
57
|
+
expected = [
|
58
|
+
{ :parameter_key => "Ami", :use_previous_value => true }
|
59
|
+
]
|
60
|
+
expect(parameters.to_a).to eql(expected)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
context "constructed with an array of parameter records" do
|
70
|
+
|
71
|
+
context "with symbol keys" do
|
72
|
+
|
73
|
+
let(:input_records) do
|
74
|
+
[
|
75
|
+
{ :parameter_key => "Ami", :parameter_value => "ami-123" },
|
76
|
+
{ :parameter_key => "VpcId", :parameter_value => "vpc-456" }
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
subject(:parameters) { Stackup::Parameters.new(input_records) }
|
81
|
+
|
82
|
+
describe "#to_hash" do
|
83
|
+
|
84
|
+
it "returns an equivalent Hash" do
|
85
|
+
expected = {
|
86
|
+
"Ami" => "ami-123",
|
87
|
+
"VpcId" => "vpc-456"
|
88
|
+
}
|
89
|
+
expect(parameters.to_hash).to eql(expected)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#to_a" do
|
95
|
+
|
96
|
+
it "returns the array form" do
|
97
|
+
expect(parameters.to_a).to eql(input_records)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
context "with :use_previous_value" do
|
103
|
+
|
104
|
+
let(:input_records) do
|
105
|
+
[
|
106
|
+
{ :parameter_key => "Ami", :use_previous_value => true }
|
107
|
+
]
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#to_hash" do
|
111
|
+
|
112
|
+
it "marks the corresponding parameter as :use_previous_value" do
|
113
|
+
expect(parameters.to_hash).to eql("Ami" => :use_previous_value)
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
context "with String keys" do
|
123
|
+
|
124
|
+
let(:input_records) do
|
125
|
+
[
|
126
|
+
{ "ParameterKey" => "Ami", "ParameterValue" => "ami-123" },
|
127
|
+
{ "ParameterKey" => "VpcId", "ParameterValue" => "vpc-456" }
|
128
|
+
]
|
129
|
+
end
|
130
|
+
|
131
|
+
subject(:parameters) { Stackup::Parameters.new(input_records) }
|
132
|
+
|
133
|
+
describe "#to_hash" do
|
134
|
+
|
135
|
+
it "returns an equivalent Hash" do
|
136
|
+
expected = {
|
137
|
+
"Ami" => "ami-123",
|
138
|
+
"VpcId" => "vpc-456"
|
139
|
+
}
|
140
|
+
expect(parameters.to_hash).to eql(expected)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
data/spec/stackup/stack_spec.rb
CHANGED
@@ -33,7 +33,7 @@ describe Stackup::Stack do
|
|
33
33
|
}
|
34
34
|
end
|
35
35
|
|
36
|
-
def stack_description(stack_status)
|
36
|
+
def stack_description(stack_status, details = {})
|
37
37
|
{
|
38
38
|
:stacks => [
|
39
39
|
{
|
@@ -41,7 +41,7 @@ describe Stackup::Stack do
|
|
41
41
|
:stack_id => unique_stack_id,
|
42
42
|
:stack_name => stack_name,
|
43
43
|
:stack_status => stack_status
|
44
|
-
}
|
44
|
+
}.merge(details)
|
45
45
|
]
|
46
46
|
}
|
47
47
|
end
|
@@ -166,11 +166,11 @@ describe Stackup::Stack do
|
|
166
166
|
}]
|
167
167
|
end
|
168
168
|
|
169
|
-
it "converts
|
169
|
+
it "converts the keys to aws-sdk form" do
|
170
170
|
expected_parameters = [
|
171
171
|
{
|
172
|
-
|
173
|
-
|
172
|
+
:parameter_key => "foo",
|
173
|
+
:parameter_value => "bar"
|
174
174
|
}
|
175
175
|
]
|
176
176
|
create_or_update
|
@@ -181,6 +181,44 @@ describe Stackup::Stack do
|
|
181
181
|
|
182
182
|
end
|
183
183
|
|
184
|
+
context "with :tags as Hash" do
|
185
|
+
|
186
|
+
before do
|
187
|
+
options[:tags] = { "foo" => "bar" }
|
188
|
+
end
|
189
|
+
|
190
|
+
it "converts them to an Array" do
|
191
|
+
expected_tags = [
|
192
|
+
{ :key => "foo", :value => "bar" }
|
193
|
+
]
|
194
|
+
create_or_update
|
195
|
+
expect(cf_client).to have_received(:create_stack) do |options|
|
196
|
+
expect(options[:tags]).to eq(expected_tags)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
context "with :tags in SDK form" do
|
203
|
+
|
204
|
+
before do
|
205
|
+
options[:tags] = [
|
206
|
+
{ :key => "foo", :value => "bar" }
|
207
|
+
]
|
208
|
+
end
|
209
|
+
|
210
|
+
it "passes them through" do
|
211
|
+
expected_tags = [
|
212
|
+
{ :key => "foo", :value => "bar" }
|
213
|
+
]
|
214
|
+
create_or_update
|
215
|
+
expect(cf_client).to have_received(:create_stack) do |options|
|
216
|
+
expect(options[:tags]).to eq(expected_tags)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
184
222
|
end
|
185
223
|
|
186
224
|
end
|
@@ -207,6 +245,26 @@ describe Stackup::Stack do
|
|
207
245
|
end
|
208
246
|
end
|
209
247
|
|
248
|
+
describe "#tags" do
|
249
|
+
|
250
|
+
let(:tags) do
|
251
|
+
[
|
252
|
+
{ :key => "foo", :value => "bar" }
|
253
|
+
]
|
254
|
+
end
|
255
|
+
|
256
|
+
let(:describe_stacks_responses) do
|
257
|
+
[
|
258
|
+
stack_description(stack_status, :tags => tags)
|
259
|
+
]
|
260
|
+
end
|
261
|
+
|
262
|
+
it "returns tags as a Hash" do
|
263
|
+
expect(stack.tags).to eq("foo" => "bar")
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
210
268
|
describe "#delete" do
|
211
269
|
|
212
270
|
context "if successful" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Williams
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-03-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk-resources
|
@@ -99,18 +99,22 @@ files:
|
|
99
99
|
- README.md
|
100
100
|
- Rakefile
|
101
101
|
- bin/stackup
|
102
|
-
- examples/
|
103
|
-
- examples/parameters.yml
|
102
|
+
- examples/Rakefile
|
103
|
+
- examples/parameters-terse.yml
|
104
|
+
- examples/parameters-verbose.json
|
104
105
|
- examples/template.json
|
105
106
|
- lib/stackup.rb
|
107
|
+
- lib/stackup/differ.rb
|
106
108
|
- lib/stackup/error_handling.rb
|
107
109
|
- lib/stackup/errors.rb
|
110
|
+
- lib/stackup/parameters.rb
|
108
111
|
- lib/stackup/rake_tasks.rb
|
109
112
|
- lib/stackup/service.rb
|
110
113
|
- lib/stackup/stack.rb
|
111
114
|
- lib/stackup/stack_watcher.rb
|
112
115
|
- lib/stackup/version.rb
|
113
116
|
- spec/spec_helper.rb
|
117
|
+
- spec/stackup/parameters_spec.rb
|
114
118
|
- spec/stackup/stack_spec.rb
|
115
119
|
- spec/stackup/stack_watcher_spec.rb
|
116
120
|
- stackup.gemspec
|
@@ -134,11 +138,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
138
|
version: '0'
|
135
139
|
requirements: []
|
136
140
|
rubyforge_project:
|
137
|
-
rubygems_version: 2.
|
141
|
+
rubygems_version: 2.6.1
|
138
142
|
signing_key:
|
139
143
|
specification_version: 4
|
140
144
|
summary: Manage CloudFormation stacks
|
141
145
|
test_files:
|
142
146
|
- spec/spec_helper.rb
|
147
|
+
- spec/stackup/parameters_spec.rb
|
143
148
|
- spec/stackup/stack_spec.rb
|
144
149
|
- spec/stackup/stack_watcher_spec.rb
|