humidifier-reservoir 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/docs/index.html CHANGED
@@ -215,6 +215,125 @@ if you&#39;re deploying from an instance. For more information, see the <a
215
215
  href="http://docs.aws.amazon.com/sdkforruby/api/">AWS docs</a> under the
216
216
  “Configuration” section.</p>
217
217
 
218
+ <h4 id="label--prefix">–prefix</h4>
219
+
220
+ <p>The <code>deploy</code> command also allows a <code>--prefix</code> command
221
+ line argument that will override the default prefix (if one is configured)
222
+ for the stack that is being deployed. This is especially useful when
223
+ you&#39;re deploying multiple copies of the same stack (for instance,
224
+ multiple autoscaling groups) that have different purposes or semantically
225
+ mean newer versions of resources.</p>
226
+
227
+ <h3 id="label-Shortcuts">Shortcuts</h3>
228
+
229
+ <p>A couple of convenient shortcuts are built into
230
+ <code>humidifier-reservoir</code> so that writing templates and mappers
231
+ both can be more concise.</p>
232
+
233
+ <h4 id="label-Automatic+id+properties">Automatic id properties</h4>
234
+
235
+ <p>There are a lot of properties in the AWS CloudFormation resource
236
+ specification that are simply pointers to other entities within the AWS
237
+ ecosystem. For example, an <code>AWS::EC2::VPCGatewayAttachment</code>
238
+ entity has a <code>VpcId</code> property that represents the ID of the
239
+ associated <code>AWS::EC2::VPC</code>.</p>
240
+
241
+ <p>Because this pattern is so common, <code>humidifier-reservoir</code>
242
+ detects all properties ending in <code>Id</code> and allows you to specify
243
+ them without the suffix. If you choose to use this format,
244
+ <code>humidifier-reservoir</code> will automatically turn that value into a
245
+ <a
246
+ href="http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html">CloudFormation
247
+ resource reference</a>.</p>
248
+
249
+ <h4 id="label-Anonymous+mappers">Anonymous mappers</h4>
250
+
251
+ <p>A lot of the time, mappers that you create will not be overly complicated,
252
+ especially if you&#39;re using the aforementioned automatic id properties.
253
+ Therefore, the <code>config.map</code> method takes a block, and allows you
254
+ to specify the mapper inline. This is recommended for mappers that
255
+ aren&#39;t too complicates as to warrant their own class (for instance, for
256
+ testing purposes). An example of this using the <code>UserMapper</code>
257
+ from above is below:</p>
258
+
259
+ <pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Humidifier.html" title="Humidifier (module)">Humidifier</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Humidifier/Reservoir.html" title="Humidifier::Reservoir (module)">Reservoir</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="Humidifier/Reservoir.html#configure-class_method" title="Humidifier::Reservoir.configure (method)">configure</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
260
+ <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='symbol'>:users</span><span class='comma'>,</span> <span class='label'>to:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>AWS::IAM::User</span><span class='tstring_end'>&#39;</span></span> <span class='kw'>do</span>
261
+ <span class='const'>GROUPS</span> <span class='op'>=</span> <span class='lbrace'>{</span>
262
+ <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>eng</span><span class='tstring_end'>&#39;</span></span> <span class='op'>=&gt;</span> <span class='qwords_beg'>%w[</span><span class='tstring_content'>Engineering</span><span class='words_sep'> </span><span class='tstring_content'>Testing</span><span class='words_sep'> </span><span class='tstring_content'>Deployment</span><span class='words_sep'>]</span><span class='comma'>,</span>
263
+ <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>admin</span><span class='tstring_end'>&#39;</span></span> <span class='op'>=&gt;</span> <span class='qwords_beg'>%w[</span><span class='tstring_content'>Management</span><span class='words_sep'> </span><span class='tstring_content'>Administration</span><span class='words_sep'>]</span>
264
+ <span class='rbrace'>}</span>
265
+
266
+ <span class='id identifier rubyid_defaults'>defaults</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_logical_name'>logical_name</span><span class='op'>|</span>
267
+ <span class='lbrace'>{</span> <span class='label'>path:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>/reservoir/</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>user_name:</span> <span class='id identifier rubyid_logical_name'>logical_name</span> <span class='rbrace'>}</span>
268
+ <span class='kw'>end</span>
269
+
270
+ <span class='id identifier rubyid_attribute'>attribute</span> <span class='symbol'>:group</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_group'>group</span><span class='op'>|</span>
271
+ <span class='id identifier rubyid_groups'>groups</span> <span class='op'>=</span> <span class='const'>GROUPS</span><span class='lbracket'>[</span><span class='id identifier rubyid_group'>group</span><span class='rbracket'>]</span>
272
+ <span class='id identifier rubyid_groups'>groups</span><span class='period'>.</span><span class='id identifier rubyid_any?'>any?</span> <span class='op'>?</span> <span class='lbrace'>{</span> <span class='label'>groups:</span> <span class='const'>GROUPS</span><span class='lbracket'>[</span><span class='id identifier rubyid_group'>group</span><span class='rbracket'>]</span> <span class='rbrace'>}</span> <span class='op'>:</span> <span class='lbrace'>{</span><span class='rbrace'>}</span>
273
+ <span class='kw'>end</span>
274
+ <span class='kw'>end</span>
275
+ <span class='kw'>end</span>
276
+ </code></pre>
277
+
278
+ <h4 id="label-Cross-stack+references">Cross-stack references</h4>
279
+
280
+ <p>AWS allows cross-stack references through the <a
281
+ href="http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html">intrinsic
282
+ Fn::ImportValue function</a>. You can take advantage of this with
283
+ <code>humidifier-reservoir</code> by using the <code>export: true</code>
284
+ option on resources in your stacks. For instance, if in one stack you have
285
+ a subnet that you need to reference in another, you could
286
+ (<code>stacks/vpc/subnets.yml</code>):</p>
287
+
288
+ <pre class="code ruby"><code class="ruby">ProductionPrivateSubnet2a:
289
+ vpc: ProductionVPC
290
+ cidr_block: 10.0.0.0/19
291
+ availability_zone: us-west-2a
292
+ export: true
293
+
294
+ ProductionPrivateSubnet2b:
295
+ vpc: ProductionVPC
296
+ cidr_block: 10.0.64.0/19
297
+ availability_zone: us-west-2b
298
+ export: true
299
+
300
+ ProductionPrivateSubnet2c:
301
+ vpc: ProductionVPC
302
+ cidr_block: 10.0.128.0/19
303
+ availability_zone: us-west-2c
304
+ export: true</code></pre>
305
+
306
+ <p>And then in another stack, you could reference those values
307
+ (<code>stacks/rds/db_subnets_groups.yml</code>):</p>
308
+
309
+ <pre class="code ruby"><code class="ruby">ProductionDBSubnetGroup:
310
+ db_subnet_group_description: Production DB private subnet group
311
+ subnets:
312
+ - ProductionPrivateSubnet2a
313
+ - ProductionPrivateSubnet2b
314
+ - ProductionPrivateSubnet2c</code></pre>
315
+
316
+ <p>Within the configuration, you would specify to use the
317
+ <code>Fn::ImportValue</code> function like so:</p>
318
+
319
+ <pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Humidifier.html" title="Humidifier (module)">Humidifier</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Humidifier/Reservoir.html" title="Humidifier::Reservoir (module)">Reservoir</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="Humidifier/Reservoir.html#configure-class_method" title="Humidifier::Reservoir.configure (method)">configure</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
320
+ <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_stack_path'>stack_path</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>stacks</span><span class='tstring_end'>&#39;</span></span>
321
+
322
+ <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='symbol'>:subnets</span><span class='comma'>,</span> <span class='label'>to:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>EC2::Subnet</span><span class='tstring_end'>&#39;</span></span>
323
+
324
+ <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='symbol'>:db_subnet_groups</span><span class='comma'>,</span> <span class='label'>to:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>RDS::DBSubnetGroup</span><span class='tstring_end'>&#39;</span></span> <span class='kw'>do</span>
325
+ <span class='id identifier rubyid_attribute'>attribute</span> <span class='symbol'>:subnets</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_subnet_names'>subnet_names</span><span class='op'>|</span>
326
+ <span class='id identifier rubyid_subnet_ids'>subnet_ids</span> <span class='op'>=</span>
327
+ <span class='id identifier rubyid_subnet_names'>subnet_names</span><span class='period'>.</span><span class='id identifier rubyid_map'>map</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_subnet_name'>subnet_name</span><span class='op'>|</span>
328
+ <span class='const'><span class='object_link'><a href="Humidifier.html" title="Humidifier (module)">Humidifier</a></span></span><span class='period'>.</span><span class='id identifier rubyid_fn'>fn</span><span class='period'>.</span><span class='id identifier rubyid_import_value'>import_value</span><span class='lparen'>(</span><span class='id identifier rubyid_subnet_name'>subnet_name</span><span class='rparen'>)</span>
329
+ <span class='kw'>end</span>
330
+
331
+ <span class='lbrace'>{</span> <span class='label'>subnet_ids:</span> <span class='id identifier rubyid_subnet_ids'>subnet_ids</span> <span class='rbrace'>}</span>
332
+ <span class='kw'>end</span>
333
+ <span class='kw'>end</span>
334
+ <span class='kw'>end</span>
335
+ </code></pre>
336
+
218
337
  <h2 id="label-Development">Development</h2>
219
338
 
220
339
  <p>After checking out the repo, run <code>bin/setup</code> to install
@@ -241,9 +360,9 @@ href="http://opensource.org/licenses/MIT">MIT License</a>.</p>
241
360
  </div></div>
242
361
 
243
362
  <div id="footer">
244
- Generated on Sun Oct 1 09:25:22 2017 by
363
+ Generated on Wed Nov 8 13:26:15 2017 by
245
364
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
246
- 0.9.9 (ruby-2.4.1).
365
+ 0.9.9 (ruby-2.4.2).
247
366
  </div>
248
367
 
249
368
  </div>
@@ -45,6 +45,14 @@
45
45
 
46
46
 
47
47
  <li class="odd ">
48
+ <div class="item">
49
+ <span class='object_link'><a href="Humidifier/Reservoir/Stack/Export.html#attribute-instance_method" title="Humidifier::Reservoir::Stack::Export#attribute (method)">#attribute</a></span>
50
+ <small>Humidifier::Reservoir::Stack::Export</small>
51
+ </div>
52
+ </li>
53
+
54
+
55
+ <li class="even ">
48
56
  <div class="item">
49
57
  <span class='object_link'><a href="Humidifier/Reservoir/BaseMapper.html#attribute-class_method" title="Humidifier::Reservoir::BaseMapper.attribute (method)">attribute</a></span>
50
58
  <small>Humidifier::Reservoir::BaseMapper</small>
@@ -52,7 +60,7 @@
52
60
  </li>
53
61
 
54
62
 
55
- <li class="even ">
63
+ <li class="odd ">
56
64
  <div class="item">
57
65
  <span class='object_link'><a href="Humidifier/Reservoir/BaseMapper.html#attribute_methods-class_method" title="Humidifier::Reservoir::BaseMapper.attribute_methods (method)">attribute_methods</a></span>
58
66
  <small>Humidifier::Reservoir::BaseMapper</small>
@@ -60,6 +68,14 @@
60
68
  </li>
61
69
 
62
70
 
71
+ <li class="even ">
72
+ <div class="item">
73
+ <span class='object_link'><a href="Humidifier/Reservoir/CLI.html#change-instance_method" title="Humidifier::Reservoir::CLI#change (method)">#change</a></span>
74
+ <small>Humidifier::Reservoir::CLI</small>
75
+ </div>
76
+ </li>
77
+
78
+
63
79
  <li class="odd ">
64
80
  <div class="item">
65
81
  <span class='object_link'><a href="Humidifier/Reservoir/Mapping.html#clazz-instance_method" title="Humidifier::Reservoir::Mapping#clazz (method)">#clazz</a></span>
@@ -85,6 +101,14 @@
85
101
 
86
102
 
87
103
  <li class="even ">
104
+ <div class="item">
105
+ <span class='object_link'><a href="Humidifier/Reservoir/Stack.html#create_change_set-instance_method" title="Humidifier::Reservoir::Stack#create_change_set (method)">#create_change_set</a></span>
106
+ <small>Humidifier::Reservoir::Stack</small>
107
+ </div>
108
+ </li>
109
+
110
+
111
+ <li class="odd ">
88
112
  <div class="item">
89
113
  <span class='object_link'><a href="Humidifier/Reservoir/BaseMapper.html#defaults-class_method" title="Humidifier::Reservoir::BaseMapper.defaults (method)">defaults</a></span>
90
114
  <small>Humidifier::Reservoir::BaseMapper</small>
@@ -92,7 +116,7 @@
92
116
  </li>
93
117
 
94
118
 
95
- <li class="odd ">
119
+ <li class="even ">
96
120
  <div class="item">
97
121
  <span class='object_link'><a href="Humidifier/Reservoir/CLI.html#deploy-instance_method" title="Humidifier::Reservoir::CLI#deploy (method)">#deploy</a></span>
98
122
  <small>Humidifier::Reservoir::CLI</small>
@@ -100,7 +124,7 @@
100
124
  </li>
101
125
 
102
126
 
103
- <li class="even ">
127
+ <li class="odd ">
104
128
  <div class="item">
105
129
  <span class='object_link'><a href="Humidifier/Reservoir/Stack.html#deploy-instance_method" title="Humidifier::Reservoir::Stack#deploy (method)">#deploy</a></span>
106
130
  <small>Humidifier::Reservoir::Stack</small>
@@ -108,7 +132,7 @@
108
132
  </li>
109
133
 
110
134
 
111
- <li class="odd ">
135
+ <li class="even ">
112
136
  <div class="item">
113
137
  <span class='object_link'><a href="Humidifier/Reservoir/CLI.html#display-instance_method" title="Humidifier::Reservoir::CLI#display (method)">#display</a></span>
114
138
  <small>Humidifier::Reservoir::CLI</small>
@@ -116,6 +140,14 @@
116
140
  </li>
117
141
 
118
142
 
143
+ <li class="odd ">
144
+ <div class="item">
145
+ <span class='object_link'><a href="Humidifier/Reservoir/Stack.html#exports-instance_method" title="Humidifier::Reservoir::Stack#exports (method)">#exports</a></span>
146
+ <small>Humidifier::Reservoir::Stack</small>
147
+ </div>
148
+ </li>
149
+
150
+
119
151
  <li class="even ">
120
152
  <div class="item">
121
153
  <span class='object_link'><a href="Humidifier/Reservoir/Config.html#files_for-instance_method" title="Humidifier::Reservoir::Config#files_for (method)">#files_for</a></span>
@@ -205,6 +237,14 @@
205
237
 
206
238
 
207
239
  <li class="odd ">
240
+ <div class="item">
241
+ <span class='object_link'><a href="Humidifier/Reservoir/Stack/Export.html#name-instance_method" title="Humidifier::Reservoir::Stack::Export#name (method)">#name</a></span>
242
+ <small>Humidifier::Reservoir::Stack::Export</small>
243
+ </div>
244
+ </li>
245
+
246
+
247
+ <li class="even ">
208
248
  <div class="item">
209
249
  <span class='object_link'><a href="Humidifier/Reservoir/Stack.html#pattern-instance_method" title="Humidifier::Reservoir::Stack#pattern (method)">#pattern</a></span>
210
250
  <small>Humidifier::Reservoir::Stack</small>
@@ -212,6 +252,14 @@
212
252
  </li>
213
253
 
214
254
 
255
+ <li class="odd ">
256
+ <div class="item">
257
+ <span class='object_link'><a href="Humidifier/Reservoir/Stack.html#prefix-instance_method" title="Humidifier::Reservoir::Stack#prefix (method)">#prefix</a></span>
258
+ <small>Humidifier::Reservoir::Stack</small>
259
+ </div>
260
+ </li>
261
+
262
+
215
263
  <li class="even ">
216
264
  <div class="item">
217
265
  <span class='object_link'><a href="Humidifier/Reservoir/BaseMapper.html#resource_for-instance_method" title="Humidifier::Reservoir::BaseMapper#resource_for (method)">#resource_for</a></span>
@@ -100,9 +100,9 @@
100
100
  </div>
101
101
 
102
102
  <div id="footer">
103
- Generated on Sun Oct 1 09:25:22 2017 by
103
+ Generated on Wed Nov 8 13:26:15 2017 by
104
104
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
- 0.9.9 (ruby-2.4.1).
105
+ 0.9.9 (ruby-2.4.2).
106
106
  </div>
107
107
 
108
108
  </div>
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  lib = File.expand_path('../lib', __FILE__)
4
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
3
  require 'humidifier/reservoir/version'
@@ -28,7 +26,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
28
26
 
29
27
  spec.add_dependency 'aws-sdk-cloudformation', '~> 1.2'
30
28
  spec.add_dependency 'aws-sdk-s3', '~> 1.4'
31
- spec.add_dependency 'humidifier', '~> 1.7', '>= 1.7'
29
+ spec.add_dependency 'humidifier', '~> 1.8', '>= 1.7'
32
30
  spec.add_dependency 'thor', '~> 0.19'
33
31
  spec.add_dependency 'thor-hollaback', '~> 0.1'
34
32
 
@@ -6,6 +6,10 @@ module Humidifier
6
6
  # class provides an easy-to-extend DSL that allows for default attributes
7
7
  # specifying custom attributes.
8
8
  class BaseMapper
9
+ # The list of attributes that are common to all resources that need to be
10
+ # handles separately.
11
+ COMMON_ATTRIBUTES = Resource::COMMON_ATTRIBUTES.values
12
+
9
13
  class << self
10
14
  # Defines a custom attribute. The given block will receive the
11
15
  # user-provided value for the attribute. The block should return a hash
@@ -47,17 +51,36 @@ module Humidifier
47
51
  # the logical name for the resource, and the user-specified attributes.
48
52
  def resource_for(clazz, name, attributes)
49
53
  mapped = respond_to?(:attribute_defaults) ? attribute_defaults(name) : {}
50
- attributes.each { |key, value| mapped.merge!(mapped_from(clazz, key, value)) }
51
- clazz.new(mapped)
54
+
55
+ attributes.each do |key, value|
56
+ mapped.merge!(mapped_from(clazz, key, value))
57
+ end
58
+
59
+ common_attributes = common_attributes_from(mapped)
60
+
61
+ resource = clazz.new(mapped)
62
+ resource.update_attributes(common_attributes)
63
+ resource
52
64
  end
53
65
 
54
66
  private
55
67
 
56
- def mapped_from(clazz, key, value)
68
+ def common_attributes_from(mapped)
69
+ COMMON_ATTRIBUTES.each_with_object({}) do |common_attribute, extract|
70
+ extracted = mapped.delete(common_attribute)
71
+ extract[common_attribute] = extracted if extracted
72
+ end
73
+ end
74
+
75
+ def mapped_from(clazz, key, value) # rubocop:disable Metrics/MethodLength
57
76
  if self.class.attribute_methods.include?(key.to_sym)
58
77
  public_send(:"attribute_#{key}", value)
59
78
  elsif clazz.prop?(key)
60
79
  { key.to_sym => value }
80
+ elsif clazz.prop?("#{key}_id")
81
+ { :"#{key}_id" => Humidifier.ref(value) }
82
+ elsif COMMON_ATTRIBUTES.include?(key.to_sym)
83
+ { key.to_sym => value }
61
84
  else
62
85
  raise Error, "Invalid attribute: #{key}"
63
86
  end
@@ -8,14 +8,27 @@ module Humidifier
8
8
  class_option :debug, desc: 'Sets up debug mode', aliases: ['-d']
9
9
  class_around :safe_execute
10
10
 
11
+ desc 'change [?stack]', 'Create changesets for one or all stacks'
12
+ def change(name = nil)
13
+ stack_names = stack_names_from(name)
14
+ authorize
15
+
16
+ stack_names.each do |stack_name|
17
+ stack = Stack.new(stack_name)
18
+ puts "Creating a changeset for #{stack.stack_name}"
19
+ stack.create_change_set
20
+ end
21
+ end
22
+
11
23
  desc 'deploy [?stack]', 'Update one or all stacks'
12
24
  option :wait, desc: 'Wait for the stack to create/update', type: :boolean, default: false
25
+ option :prefix, desc: 'The prefix to use for the stack'
13
26
  def deploy(name = nil)
14
- stack_names = name ? [name] : Reservoir.stacks
27
+ stack_names = stack_names_from(name)
15
28
  authorize
16
29
 
17
30
  stack_names.each do |stack_name|
18
- stack = Stack.new(stack_name)
31
+ stack = Stack.new(stack_name, prefix: options[:prefix])
19
32
  puts "Deploying #{stack.stack_name}"
20
33
  stack.deploy(options[:wait])
21
34
  end
@@ -23,12 +36,12 @@ module Humidifier
23
36
 
24
37
  desc 'display [stack] [?pattern]', 'Display the CloudFormation JSON for a given stack'
25
38
  def display(name, pattern = nil)
26
- puts Stack.new(name, pattern && /#{pattern}/i).to_cf
39
+ puts Stack.new(name, pattern: pattern && /#{pattern}/i).to_cf
27
40
  end
28
41
 
29
42
  desc 'validate [?stack]', 'Validate that one or all stacks are valid with CloudFormation'
30
43
  def validate(name = nil)
31
- stack_names = name ? [name] : Reservoir.stacks
44
+ stack_names = stack_names_from(name)
32
45
  authorize
33
46
 
34
47
  print 'Validating... '
@@ -48,6 +61,10 @@ module Humidifier
48
61
  puts error.message
49
62
  exit 1
50
63
  end
64
+
65
+ def stack_names_from(name)
66
+ name ? [name] : Reservoir.stacks
67
+ end
51
68
  end
52
69
  end
53
70
  end
@@ -14,8 +14,8 @@ module Humidifier
14
14
  Dir["#{stack_path}/#{name}/*.yml"]
15
15
  end
16
16
 
17
- def map(type, opts = {})
18
- mappings[type.to_sym] = Mapping.new(opts)
17
+ def map(type, opts = {}, &block)
18
+ mappings[type.to_sym] = Mapping.new(opts, &block)
19
19
  end
20
20
 
21
21
  def mapping_for(type)
@@ -6,16 +6,36 @@ module Humidifier
6
6
  class Mapping
7
7
  attr_reader :clazz, :mapper
8
8
 
9
- def initialize(opts = {})
10
- @clazz = Humidifier[opts[:to]]
9
+ def initialize(opts = {}, &block)
10
+ @clazz = Humidifier[normalized(opts[:to])]
11
11
  raise Error, "Invalid resource: #{opts[:to].inspect}" if @clazz.nil?
12
12
 
13
- @mapper = opts.fetch(:using, BaseMapper).new
13
+ if opts[:using] && block_given?
14
+ raise Error, 'Cannot specify :using and provide an anonymous mapper'
15
+ end
16
+
17
+ @mapper = mapper_from(opts, &block)
14
18
  end
15
19
 
16
20
  def resource_for(name, attributes)
17
21
  mapper.resource_for(clazz, name, attributes)
18
22
  end
23
+
24
+ private
25
+
26
+ def mapper_from(opts, &block)
27
+ if opts[:using]
28
+ opts[:using].new
29
+ elsif block_given?
30
+ Class.new(BaseMapper, &block).new
31
+ else
32
+ BaseMapper.new
33
+ end
34
+ end
35
+
36
+ def normalized(to)
37
+ to.start_with?('AWS') ? to : "AWS::#{to}"
38
+ end
19
39
  end
20
40
  end
21
41
  end
@@ -4,18 +4,38 @@ module Humidifier
4
4
  # interfacing with humidifier to deploy stacks, validate them, and display
5
5
  # them.
6
6
  class Stack
7
- attr_reader :name, :pattern
7
+ # Represents an exported resource in a stack for use in cross-stack
8
+ # references.
9
+ Export =
10
+ Struct.new(:name, :attribute) do
11
+ def value
12
+ if attribute.is_a?(String)
13
+ Humidifier.fn.get_att([name, attribute])
14
+ else
15
+ Humidifier.ref(name)
16
+ end
17
+ end
18
+ end
19
+
20
+ attr_reader :name, :pattern, :prefix, :exports
8
21
 
9
- def initialize(name, pattern = nil)
22
+ def initialize(name, pattern: nil, prefix: nil)
10
23
  @name = name
11
24
  @pattern = pattern
25
+ @prefix = prefix
26
+ @exports = []
27
+ end
28
+
29
+ def create_change_set
30
+ return unless ensure_resources
31
+ valid?
32
+
33
+ opts = { capabilities: %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM] }
34
+ humidifier_stack.create_change_set(opts)
12
35
  end
13
36
 
14
37
  def deploy(wait = false)
15
- if humidifier_stack.resources.empty?
16
- puts "Refusing to deploy stack #{humidifier_stack.name} with no resources"
17
- return
18
- end
38
+ return unless ensure_resources
19
39
  valid?
20
40
 
21
41
  opts = { capabilities: %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM] }
@@ -29,7 +49,7 @@ module Humidifier
29
49
  end
30
50
 
31
51
  def stack_name
32
- @stack_name ||= "#{Reservoir.stack_prefix}#{name}"
52
+ @stack_name ||= "#{prefix || Reservoir.stack_prefix}#{name}"
33
53
  end
34
54
 
35
55
  def to_cf
@@ -47,14 +67,31 @@ MSG
47
67
 
48
68
  private
49
69
 
70
+ def ensure_resources
71
+ return true if humidifier_stack.resources.any?
72
+ puts "Refusing to deploy stack #{humidifier_stack.name} with no resources"
73
+ false
74
+ end
75
+
50
76
  def humidifier_stack
51
77
  Humidifier::Stack.new(
52
78
  name: stack_name,
53
79
  description: "Resources for #{stack_name}",
54
- resources: resources
80
+ resources: resources,
81
+ outputs: outputs
55
82
  )
56
83
  end
57
84
 
85
+ def outputs
86
+ exports.each_with_object({}) do |export, exported|
87
+ exported[export.name] =
88
+ Humidifier::Output.new(
89
+ value: export.value,
90
+ export_name: export.name
91
+ )
92
+ end
93
+ end
94
+
58
95
  def parse(filepath, type)
59
96
  mapping = Reservoir.mapping_for(type)
60
97
  return {} if mapping.nil?
@@ -64,6 +101,10 @@ MSG
64
101
 
65
102
  loaded.each_with_object({}) do |(name, attributes), resources|
66
103
  next if pattern && name !~ pattern
104
+
105
+ attribute = attributes.delete('export')
106
+ exports << Export.new(name, attribute) if attribute
107
+
67
108
  resources[name] = mapping.resource_for(name, attributes)
68
109
  end
69
110
  end