bora 1.1.1 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac9d90b2d54df77a725e4a46b058487d8d9f7b8a
4
- data.tar.gz: fae956da14fb179a49272ac1bdbcb38819c16126
3
+ metadata.gz: 741e43489dd8279e5b9a2fa73068bd45bbfcc7de
4
+ data.tar.gz: 708c036c31038bec8dc57a61541af3065f4bf2c9
5
5
  SHA512:
6
- metadata.gz: 7197598d07836d355d9f3b26686bcb282585ea31597c77da5d6a60e4d6fc2cec353211c95e150866131a93fedf346b2574e0ef894e5c04176ad7dcdf39032fa6
7
- data.tar.gz: a0bcdd446b1107014fc8f17e12c4d03b227ed3a57856fc54d18d55711b7b8e10dc135cc4fad7f1653d823bc1b0ff99827c455db44e887a4845e423986b739ae9
6
+ metadata.gz: 85b831a26bb9876db1a32a00238b1d489582a5f87adcb105d5103414d1274d175f53bc8123287a603a811e0f43ee2e336b0704e71107d85698507cbd84e176ac
7
+ data.tar.gz: 957e597af2294f73976630840ef64dbcdb2f078ea2085b39e0aa9d4d2e9950168ac7c292b1aa67fc9d394ee52abcade685573070d7e7ff419c7a5fda6349eb11
data/README.md CHANGED
@@ -102,7 +102,7 @@ templates:
102
102
  # If you don't supply this, the name will be the template
103
103
  # name concatenated with the stack name as defined in this file,
104
104
  # eg: "app-prod".
105
- stack_name: prod-application-stack
105
+ cfn_stack_name: prod-application-stack
106
106
 
107
107
  # Optional. Default region for this stack.
108
108
  # Overrides "default_region" at the template level.
@@ -219,6 +219,17 @@ There are a number of resolvers that come with Bora (documented below),
219
219
  or you can write your own.
220
220
 
221
221
 
222
+ ### Parameter Lookup
223
+ Any substitution that does not specify a "scheme" is treated as a reference to another parameter value.
224
+ For example:
225
+
226
+ ```yaml
227
+ params:
228
+ domain: example.com
229
+ url: http://${domain}/foo
230
+ ```
231
+
232
+
222
233
  ### Stack Output Lookup
223
234
 
224
235
  You can look up outputs from stacks in the same region.
@@ -280,6 +291,30 @@ $ rake web-uat:apply[instance_type=t2.micro,ami=ami-11032472]
280
291
  ```
281
292
 
282
293
 
294
+ ## Creating Multiple Instances of a Stack
295
+ Sometimes it can be useful to create multiple instances of a single stack.
296
+ For example, you may define a single "qa" stack with all the settings for a testing environment.
297
+ Then you might want to stand up this stack multiple times so you can have multiple testing environments,
298
+ eg "qa1", "qa2", etc.
299
+
300
+ Bora makes this possible by allowing you to override the name of the stack that gets created in CloudFormation.
301
+ For example:
302
+
303
+ ```bash
304
+ $ bora apply web-qa --cfn-stack-name "web-qa-1"
305
+ $ bora apply web-qa --cfn-stack-name "web-qa-2"
306
+ ```
307
+
308
+ Remember that if you use this functionality you must remember to pass in the stack name to *every* command or you will get unexepected results.
309
+
310
+ ```bash
311
+ $ bora outputs web-qa --cfn-stack-name "web-qa-1"
312
+ ```
313
+
314
+ Work is underway to improve how Bora handles this use case.
315
+ If this is of interest to you, please have a look at the [GitHub issue](https://github.com/ampedandwired/bora/issues/10) for this functionality.
316
+
317
+
283
318
  ## Related Projects
284
319
  The following projects provided inspiration for Bora:
285
320
  * [CfnDsl](https://github.com/stevenjack/cfndsl) - A Ruby DSL for CloudFormation templates
@@ -15,6 +15,12 @@ class Bora
15
15
  default: nil,
16
16
  desc: "The region to use for the stack operation. Overrides any regions specified in the Bora config file."
17
17
 
18
+ class_option "cfn-stack-name",
19
+ type: :string,
20
+ aliases: :c,
21
+ default: nil,
22
+ desc: "The name to give the stack in CloudFormation. Overrides any CFN stack name setting in the Bora config file."
23
+
18
24
  desc "list", "Lists the available stacks"
19
25
  def list
20
26
  templates = bora(options.file).templates
@@ -84,7 +90,12 @@ class Bora
84
90
 
85
91
  def stack(config_file, stack_name)
86
92
  region = options.region
87
- override_config = region ? {"default_region" => region} : {}
93
+ cfn_stack_name = options["cfn-stack-name"]
94
+
95
+ override_config = {}
96
+ override_config["default_region"] = region if region
97
+ override_config["cfn_stack_name"] = cfn_stack_name if cfn_stack_name
98
+
88
99
  bora = bora(config_file, override_config)
89
100
  stack = bora.stack(stack_name)
90
101
  if !stack
@@ -3,6 +3,10 @@ require 'bora/parameter_resolver_loader'
3
3
 
4
4
  class Bora
5
5
  class ParameterResolver
6
+ UnresolvedSubstitutionError = Class.new(StandardError)
7
+
8
+ PLACEHOLDER_REGEX = /\${[^}]+}/
9
+
6
10
  def initialize(stack)
7
11
  @stack = stack
8
12
  @loader = ParameterResolverLoader.new
@@ -10,26 +14,49 @@ class Bora
10
14
  end
11
15
 
12
16
  def resolve(params)
13
- params.map { |k, v| [k, process_param_substitutions(v)] }.to_h
17
+ unresolved_placeholders_still_remain = true
18
+ while unresolved_placeholders_still_remain
19
+ unresolved_placeholders_still_remain = false
20
+ placeholders_were_substituted = false
21
+ params.each do |k, v|
22
+ resolved_value = process_param_substitutions(v, params)
23
+ unresolved_placeholders_still_remain ||= has_unresolved_placeholder?(resolved_value)
24
+ placeholders_were_substituted ||= resolved_value != v
25
+ params[k] = resolved_value
26
+ end
27
+ if unresolved_placeholders_still_remain && !placeholders_were_substituted
28
+ raise UnresolvedSubstitutionError, "Parameter substitutions could not be resolved:\n#{unresolved_placeholders_as_string(params)}"
29
+ end
30
+ end
31
+ params
14
32
  end
15
33
 
16
34
 
17
35
  private
18
36
 
19
- def process_param_substitutions(val)
37
+ def process_param_substitutions(val, params)
20
38
  return val unless val.is_a? String
21
- old_val = nil
22
- while old_val != val
23
- old_val = val
24
- val = val.sub(/\${[^}]+}/) do |m|
25
- token = m[2..-2]
26
- uri = parse_uri(token)
27
- resolver_name = uri.scheme
28
- resolver = @resolver_cache[resolver_name] || @loader.load_resolver(resolver_name).new(@stack)
29
- resolver.resolve(uri)
30
- end
39
+ val.gsub(PLACEHOLDER_REGEX) do |placeholder|
40
+ process_placeholder(placeholder, params)
41
+ end
42
+ end
43
+
44
+ def process_placeholder(placeholder, params)
45
+ uri = parse_uri(placeholder[2..-2])
46
+ if !uri.scheme
47
+ # This token refers to another parameter, rather than a resolver
48
+ value_to_substitute = params[uri.path]
49
+ !value_to_substitute || has_unresolved_placeholder?(value_to_substitute) ? placeholder : value_to_substitute
50
+ else
51
+ # This token needs to be resolved by a resolver
52
+ resolver_name = uri.scheme
53
+ resolver = @resolver_cache[resolver_name] || @loader.load_resolver(resolver_name).new(@stack)
54
+ resolver.resolve(uri)
31
55
  end
32
- val
56
+ end
57
+
58
+ def has_unresolved_placeholder?(val)
59
+ val =~ PLACEHOLDER_REGEX
33
60
  end
34
61
 
35
62
  def parse_uri(s)
@@ -40,9 +67,12 @@ class Bora
40
67
  if !uri.scheme && uri.path && uri.path.count("/") == 2
41
68
  uri = URI("cfn://#{s}")
42
69
  end
43
-
44
70
  uri
45
71
  end
46
72
 
73
+ def unresolved_placeholders_as_string(params)
74
+ params.select { |k, v| has_unresolved_placeholder?(v) }.to_a.map { |k, v| "#{k}: #{v}" }.join("\n")
75
+ end
76
+
47
77
  end
48
78
  end
@@ -18,7 +18,10 @@ class Bora
18
18
 
19
19
  def initialize(stack_name, template_file, stack_config)
20
20
  @stack_name = stack_name
21
- @cfn_stack_name = stack_config['stack_name'] || @stack_name
21
+ @cfn_stack_name = stack_config['cfn_stack_name'] || stack_config['stack_name'] || @stack_name
22
+ if stack_config['stack_name']
23
+ puts "DEPRECATED: The 'stack_name' setting is deprecated. Please use 'cfn_stack_name' instead."
24
+ end
22
25
  @template_file = template_file
23
26
  @stack_config = stack_config
24
27
  @region = @stack_config['default_region']
@@ -2,16 +2,20 @@ require 'bora/stack'
2
2
 
3
3
  class Bora
4
4
  class Template
5
+ # These are properties that you can define on the template, but which can also be defined in the stack
5
6
  INHERITABLE_PROPERTIES = ["capabilities", "default_region"]
6
7
 
8
+ # These are properties that can be passed in from the command line to override what's defined inthe stack
9
+ OVERRIDABLE_PROPERTIES = ["cfn_stack_name"]
10
+
7
11
  def initialize(template_name, template_config, override_config = {})
8
12
  @template_name = template_name
9
13
  @template_config = template_config
10
14
  @stacks = {}
11
15
  template_config['stacks'].each do |stack_name, stack_config|
12
16
  stack_name = "#{template_name}-#{stack_name}"
13
- stack_config = resolve_stack_config(template_config, stack_config, override_config)
14
- @stacks[stack_name] = Stack.new(stack_name, template_config['template_file'], stack_config)
17
+ resolved_config = resolve_stack_config(template_config, stack_config, override_config)
18
+ @stacks[stack_name] = Stack.new(stack_name, template_config['template_file'], resolved_config)
15
19
  end
16
20
  end
17
21
 
@@ -31,12 +35,16 @@ class Bora
31
35
  private
32
36
 
33
37
  def resolve_stack_config(template_config, stack_config, override_config)
34
- inheritable_properties(template_config).merge(stack_config).merge(inheritable_properties(override_config))
38
+ inheritable_properties(template_config).merge(stack_config).merge(overridable_properties(override_config))
35
39
  end
36
40
 
37
41
  def inheritable_properties(config)
38
42
  config.select { |k| INHERITABLE_PROPERTIES.include?(k) }
39
43
  end
40
44
 
45
+ def overridable_properties(config)
46
+ config.select { |k| INHERITABLE_PROPERTIES.include?(k) || OVERRIDABLE_PROPERTIES.include?(k) }
47
+ end
48
+
41
49
  end
42
50
  end
@@ -1,3 +1,3 @@
1
1
  class Bora
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bora
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Blaxland
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-10-12 00:00:00.000000000 Z
11
+ date: 2016-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk