bora 1.1.1 → 1.2.0

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