stackup 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|