bora 1.6.0 → 1.7.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: f3b7fb07038e20354a749af9db174d21fca4a3ab
4
- data.tar.gz: 923412c975c6d3c2454d7dd616ed7b2339bb7646
3
+ metadata.gz: af9562b301a731f8fee40274923e356d3541ca98
4
+ data.tar.gz: 3a2cc26919d1502e0e272e16a47331426b38e2d3
5
5
  SHA512:
6
- metadata.gz: f89db4d0f3175157424d9ea86ddb3fa6ec8aacabac8a762d8a7ad1e8f08f217c489f92d8fe2a72dfc287dfb6c82cf88bee97d5674d4b6961b00ab01808f15272
7
- data.tar.gz: 039661e930401f3ac1c59bf122fd7aa67b60aaf79a5d129ec269a6dcb125d54f2667fdb6c626f0a8c35f1c90584a1fc111b9759a72907184af1a76f8a700bcc6
6
+ metadata.gz: 64fb26a2e2d65fe271346fdfbdb2f0f81f100135d149314d469b7d9b6b8f006cf25d13bfeefaaaa350110e2a9cb1a5d540919080b841c36532ccd41747c9f61f
7
+ data.tar.gz: c9de80ce38c97800dd0e03e84aa5f51be532350219915a61634fa6a4cdfbbda535a798afc6eee20997add492766e50c40c47e28377c38340300c0eb1c8c556ba
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /pkg/
10
10
  /spec/reports/
11
11
  /tmp/
12
+ .editorconfig
@@ -0,0 +1,21 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Metrics/AbcSize:
4
+ Enabled: false
5
+ Metrics/CyclomaticComplexity:
6
+ Enabled: false
7
+ Metrics/BlockLength:
8
+ Enabled: false
9
+ Metrics/ClassLength:
10
+ Enabled: false
11
+ Metrics/LineLength:
12
+ Enabled: false
13
+ Metrics/MethodLength:
14
+ CountComments: false
15
+ Max: 30
16
+ Metrics/PerceivedComplexity:
17
+ Enabled: false
18
+ Style/Documentation:
19
+ Enabled: false
20
+ Style/FrozenStringLiteralComment:
21
+ Enabled: false
data/README.md CHANGED
@@ -76,11 +76,14 @@ templates:
76
76
  app:
77
77
  # This template is a plain old CloudFormation JSON file
78
78
  template_file: app.json
79
-
80
- # Optional. An array of "capabilities" to be passed to the CloudFormation API
81
- # (see CloudFormation docs for more details)
82
- capabilities: [CAPABILITY_IAM]
83
-
79
+ # Tag Key / Value pairs for the Cloudformation stack
80
+ # Tags are inherited by stack resources
81
+ tags:
82
+ Name: my-app
83
+ # Optional create stack parameters
84
+ # See - http://docs.aws.amazon.com/sdkforruby/api/Aws/CloudFormation/Client.html#create_stack-instance_method
85
+ capabilities: [CAPABILITY_IAM] # An array of "capabilities" to be passed to the CloudFormation API
86
+ on_failure: DO_NOTHING # See CloudFormation API docs for valid values and their meanings; "disable_rollback" is also supported
84
87
  # Optional. The default region for all stacks in this template.
85
88
  # Overrides "default_region" at the global level.
86
89
  # See below for further information.
@@ -91,24 +94,27 @@ templates:
91
94
  stacks:
92
95
  # The "uat" stack
93
96
  uat:
94
- # The CloudFormation parameters to pass into the stack
97
+
95
98
  params:
96
99
  InstanceType: t2.micro
97
100
  AMI: ami-11032472
98
-
99
101
  # The "prod" stack
100
102
  prod:
103
+ # Overrides template level
104
+ on_failure: DELETE
105
+ # Tags here are merged with the template
106
+ tags:
107
+ Environment: uat
108
+ # The CloudFormation parameters to pass into the stack
101
109
  # Optional. The stack name to use in CloudFormation
102
110
  # If you don't supply this, the name will be the template
103
111
  # name concatenated with the stack name as defined in this file,
104
112
  # eg: "app-prod".
105
113
  cfn_stack_name: prod-application-stack
106
-
107
114
  # Optional. Default region for this stack.
108
115
  # Overrides "default_region" at the template level.
109
116
  # See below for further information.
110
117
  default_region: ap-southeast-2
111
-
112
118
  params:
113
119
  InstanceType: m4.xlarge
114
120
  AMI: ami-11032472
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new
5
7
 
6
- task :default => :spec
8
+ task default: :spec
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "bora"
3
+ require 'bundler/setup'
4
+ require 'bora'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "bora"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start
@@ -4,30 +4,31 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'bora/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "bora"
7
+ spec.name = 'bora'
8
8
  spec.version = Bora::VERSION
9
- spec.authors = ["Charles Blaxland"]
10
- spec.email = ["charles.blaxland@gmail.com"]
9
+ spec.authors = ['Charles Blaxland']
10
+ spec.email = ['charles.blaxland@gmail.com']
11
11
 
12
- spec.summary = %q{A tool (including rake tasks) for working with cloudformation stacks}
13
- spec.homepage = "https://github.com/ampedandwired/bora"
12
+ spec.summary = 'A tool (including rake tasks) for working with cloudformation stacks'
13
+ spec.homepage = 'https://github.com/ampedandwired/bora'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
- spec.bindir = "exe"
16
+ spec.bindir = 'exe'
17
17
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ['lib']
19
19
  spec.required_ruby_version = '>= 2.1.0'
20
20
 
21
- spec.add_dependency "aws-sdk", "~> 2.0"
22
- spec.add_dependency "cfndsl", "~> 0.4"
23
- spec.add_dependency "colorize", "~> 0.7"
24
- spec.add_dependency "diffy", "~> 3.0"
25
- spec.add_dependency "rake", "~> 10.0"
26
- spec.add_dependency "thor", "~> 0.19"
27
- spec.add_dependency "hashie", "~> 3.4.6"
21
+ spec.add_dependency 'aws-sdk', '~> 2.0'
22
+ spec.add_dependency 'cfndsl', '~> 0.4'
23
+ spec.add_dependency 'colorize', '~> 0.7'
24
+ spec.add_dependency 'diffy', '~> 3.0'
25
+ spec.add_dependency 'rake', '~> 10.0'
26
+ spec.add_dependency 'thor', '~> 0.19'
27
+ spec.add_dependency 'hashie', '~> 3.4.6'
28
28
 
29
- spec.add_development_dependency "bundler", "~> 1.11"
30
- spec.add_development_dependency "rspec", "~> 3.0"
31
- spec.add_development_dependency "simplecov", "~> 0.12"
32
- spec.add_development_dependency "pry"
29
+ spec.add_development_dependency 'bundler', '~> 1.11'
30
+ spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.add_development_dependency 'simplecov', '~> 0.12'
32
+ spec.add_development_dependency 'rubocop'
33
+ spec.add_development_dependency 'pry'
33
34
  end
@@ -1,18 +1,18 @@
1
- require "yaml"
2
- require "colorize"
3
- require "bora/version"
4
- require "bora/template"
5
- require "bora/tasks"
1
+ require 'yaml'
2
+ require 'colorize'
3
+ require 'bora/version'
4
+ require 'bora/template'
5
+ require 'bora/tasks'
6
6
 
7
7
  class Bora
8
- DEFAULT_CONFIG_FILE = "bora.yml"
9
- INHERITABLE_PROPERTIES = ["default_region"]
8
+ DEFAULT_CONFIG_FILE = 'bora.yml'.freeze
9
+ INHERITABLE_PROPERTIES = ['default_region'].freeze
10
10
 
11
11
  def initialize(config_file_or_hash: DEFAULT_CONFIG_FILE, override_config: {}, colorize: true)
12
12
  @templates = {}
13
13
  config = load_config(config_file_or_hash)
14
14
  String.disable_colorization = !colorize
15
- raise "No templates defined" if !config['templates']
15
+ raise 'No templates defined' unless config['templates']
16
16
  config['templates'].each do |template_name, template_config|
17
17
  resolved_config = resolve_template_config(config, template_config, override_config)
18
18
  @templates[template_name] = Template.new(template_name, resolved_config, override_config)
@@ -28,7 +28,7 @@ class Bora
28
28
  end
29
29
 
30
30
  def stack(stack_name)
31
- t = @templates.find { |_, template| template.stack(stack_name) != nil }
31
+ t = @templates.find { |_, template| !template.stack(stack_name).nil? }
32
32
  t ? t[1].stack(stack_name) : nil
33
33
  end
34
34
 
@@ -36,18 +36,16 @@ class Bora
36
36
  @templates.each { |_, t| t.rake_tasks }
37
37
  end
38
38
 
39
-
40
39
  protected
41
40
 
42
41
  def load_config(config)
43
42
  if config.class == String
44
- return YAML.load_file(config)
43
+ YAML.load_file(config)
45
44
  elsif config.class == Hash
46
- return config
45
+ config
47
46
  end
48
47
  end
49
48
 
50
-
51
49
  private
52
50
 
53
51
  def resolve_template_config(bora_config, template_config, override_config)
@@ -57,5 +55,4 @@ class Bora
57
55
  def inheritable_properties(config)
58
56
  config.select { |k| INHERITABLE_PROPERTIES.include?(k) }
59
57
  end
60
-
61
58
  end
@@ -1,4 +1,4 @@
1
- require "bora/cfn/change_set_action"
1
+ require 'bora/cfn/change_set_action'
2
2
 
3
3
  class Bora
4
4
  module Cfn
@@ -1,5 +1,5 @@
1
- require "bora/cfn/change"
2
- require "bora/cfn/status"
1
+ require 'bora/cfn/change'
2
+ require 'bora/cfn/status'
3
3
 
4
4
  class Bora
5
5
  module Cfn
@@ -24,19 +24,19 @@ class Bora
24
24
  status_success? || status_failure?
25
25
  end
26
26
 
27
- def has_changes?
28
- @status.success? && @changes.size > 0
27
+ def changes?
28
+ @status.success? && !@changes.empty?
29
29
  end
30
30
 
31
31
  def to_s(changes_only: false)
32
- reason = @change_set.status_reason ? " (#{@change_set.status_reason})" : ""
33
- description = @change_set.description ? " - #{@change_set.description}" : ""
34
- changes_str = !@is_summary ? @changes.map(&:to_s).join("\n") : ""
32
+ reason = @change_set.status_reason ? " (#{@change_set.status_reason})" : ''
33
+ description = @change_set.description ? " - #{@change_set.description}" : ''
34
+ changes_str = !@is_summary ? @changes.map(&:to_s).join("\n") : ''
35
35
  if changes_only
36
36
  s = changes_str
37
37
  else
38
38
  s = "#{@change_set.change_set_name.bold} - #{@change_set.creation_time.getlocal} - #{@status}#{reason} - #{@execution_status}#{description}"
39
- s += "\n#{changes_str}" if !changes_str.empty?
39
+ s += "\n#{changes_str}" unless changes_str.empty?
40
40
  end
41
41
  s
42
42
  end
@@ -10,27 +10,26 @@ class Bora
10
10
 
11
11
  def to_s
12
12
  action_str = @action
13
- if @action == "Modify"
14
- action_str = case @replacement
15
- when "True" then "Replace"
16
- when "Conditional" then "Replace (conditional)"
13
+ if @action == 'Modify'
14
+ action_str =
15
+ case @replacement
16
+ when 'True' then 'Replace'
17
+ when 'Conditional' then 'Replace (conditional)'
17
18
  else action_str
18
- end
19
+ end
19
20
  end
20
21
  action_str.colorize(color)
21
22
  end
22
23
 
23
-
24
24
  private
25
25
 
26
26
  def color
27
27
  case @action
28
- when "Add" then :green
29
- when "Remove" then :red
30
- else :yellow
28
+ when 'Add' then :green
29
+ when 'Remove' then :red
30
+ else :yellow
31
31
  end
32
32
  end
33
33
  end
34
-
35
34
  end
36
35
  end
@@ -2,15 +2,23 @@ require 'bora/cfn/status'
2
2
 
3
3
  class Bora
4
4
  module Cfn
5
-
6
5
  class Event
7
6
  def initialize(event)
8
7
  @event = event
9
8
  @status = Status.new(@event.resource_status)
10
9
  end
11
10
 
12
- def method_missing(sym, *args, &block)
13
- @event.send(sym, *args, &block)
11
+ def respond_to_missing?(method_name, include_private = false)
12
+ return false if method_name == :to_ary
13
+ super
14
+ end
15
+
16
+ def method_missing(method_name, *args, &block)
17
+ if method_name.to_s =~ /(.*)/
18
+ @event.send(Regexp.last_match[1], *args, &block)
19
+ else
20
+ super
21
+ end
14
22
  end
15
23
 
16
24
  def status_success?
@@ -26,10 +34,9 @@ class Bora
26
34
  end
27
35
 
28
36
  def to_s
29
- status_reason = @event.resource_status_reason ? " - #{@event.resource_status_reason}" : ""
37
+ status_reason = @event.resource_status_reason ? " - #{@event.resource_status_reason}" : ''
30
38
  "#{@event.timestamp.getlocal} - #{@event.resource_type} - #{@event.logical_resource_id} - #{@status}#{status_reason}"
31
39
  end
32
40
  end
33
-
34
41
  end
35
42
  end
@@ -1,6 +1,5 @@
1
1
  class Bora
2
2
  module Cfn
3
-
4
3
  class Output
5
4
  def initialize(output)
6
5
  @output = output
@@ -15,10 +14,9 @@ class Bora
15
14
  end
16
15
 
17
16
  def to_s
18
- desc = @output.description ? " (#{@output.description})" : ""
17
+ desc = @output.description ? " (#{@output.description})" : ''
19
18
  "#{@output.output_key} - #{@output.output_value} #{desc}"
20
19
  end
21
20
  end
22
-
23
21
  end
24
22
  end
@@ -17,6 +17,5 @@ class Bora
17
17
  "#{key} - #{value}"
18
18
  end
19
19
  end
20
-
21
20
  end
22
21
  end
@@ -9,9 +9,8 @@ require 'bora/cfn/parameter'
9
9
 
10
10
  class Bora
11
11
  module Cfn
12
-
13
12
  class Stack
14
- NO_UPDATE_MESSAGE = "No updates are to be performed"
13
+ NO_UPDATE_MESSAGE = 'No updates are to be performed'.freeze
15
14
 
16
15
  def initialize(stack_name, region = nil)
17
16
  @stack_name = stack_name
@@ -24,7 +23,10 @@ class Bora
24
23
  end
25
24
 
26
25
  def update(options, &block)
27
- call_cfn_action(:update_stack, options, &block)
26
+ # Parameters that are not valid for the update_stack api
27
+ invalid_update_stack_options = %i(on_failure disable_rollback)
28
+ update_options = options.select { |key| !invalid_update_stack_options.include?(key) }
29
+ call_cfn_action(:update_stack, update_options, &block)
28
30
  end
29
31
 
30
32
  def create_or_update(options, &block)
@@ -33,7 +35,7 @@ class Bora
33
35
 
34
36
  def recreate(options, &block)
35
37
  delete(&block) if exists?
36
- create(options, &block) if !exists?
38
+ create(options, &block) unless exists?
37
39
  end
38
40
 
39
41
  def delete(&block)
@@ -41,24 +43,24 @@ class Bora
41
43
  end
42
44
 
43
45
  def events
44
- return if !exists?
45
- events = cloudformation.describe_stack_events({stack_name: underlying_stack.stack_id}).stack_events
46
+ return unless exists?
47
+ events = cloudformation.describe_stack_events(stack_name: underlying_stack.stack_id).stack_events
46
48
  events.reverse.map { |e| Event.new(e) }
47
49
  end
48
50
 
49
51
  def outputs
50
- return if !exists?
52
+ return unless exists?
51
53
  underlying_stack.outputs.map { |output| Output.new(output) }
52
54
  end
53
55
 
54
56
  def parameters
55
- return if !exists?
57
+ return unless exists?
56
58
  underlying_stack.parameters.map { |parameter| Parameter.new(parameter) }
57
59
  end
58
60
 
59
61
  def template
60
- return if !exists?
61
- cloudformation.get_template({stack_name: @stack_name}).template_body
62
+ return unless exists?
63
+ cloudformation.get_template(stack_name: @stack_name).template_body
62
64
  end
63
65
 
64
66
  def validate(options)
@@ -79,11 +81,11 @@ class Bora
79
81
  change_set_name: change_set_name
80
82
  }
81
83
  cloudformation.create_change_set(change_set_options.merge(options))
82
- begin
84
+ loop do
83
85
  change_set = ChangeSet.new(cloudformation.describe_change_set(change_set_options))
84
- sleep 5 unless change_set.status_complete?
85
- end until change_set.status_complete?
86
- change_set
86
+ return change_set if change_set.status_complete?
87
+ sleep 5
88
+ end
87
89
  end
88
90
 
89
91
  def list_change_sets
@@ -100,10 +102,9 @@ class Bora
100
102
  end
101
103
 
102
104
  def execute_change_set(change_set_name, &block)
103
- call_cfn_action(:execute_change_set, {change_set_name: change_set_name}, &block)
105
+ call_cfn_action(:execute_change_set, { change_set_name: change_set_name }, &block)
104
106
  end
105
107
 
106
-
107
108
  # =============================================================================================
108
109
  private
109
110
 
@@ -118,7 +119,7 @@ class Bora
118
119
  return true if action == :delete_stack && !exists?
119
120
  @previous_event_time = last_event_time
120
121
  begin
121
- action_options = {stack_name: @stack_name}.merge(options)
122
+ action_options = { stack_name: @stack_name }.merge(options)
122
123
  cloudformation.method(action.to_s.downcase).call(action_options)
123
124
  wait_for_completion(&block)
124
125
  rescue Aws::CloudFormation::Errors::ValidationError => e
@@ -129,21 +130,22 @@ class Bora
129
130
  end
130
131
 
131
132
  def wait_for_completion
132
- begin
133
+ loop do
133
134
  events = unprocessed_events
134
135
  events.each { |e| yield e } if block_given?
135
136
  finished = events.find do |e|
136
137
  e.resource_type == 'AWS::CloudFormation::Stack' && e.logical_resource_id == @stack_name && e.status_complete?
137
138
  end
138
- sleep 10 unless finished
139
- end until finished
139
+ break if finished
140
+ sleep 10
141
+ end
140
142
  underlying_stack(refresh: true)
141
143
  end
142
144
 
143
145
  def underlying_stack(refresh: false)
144
146
  if !@_stack || refresh
145
147
  begin
146
- response = cloudformation.describe_stacks({stack_name: @stack_name})
148
+ response = cloudformation.describe_stacks(stack_name: @stack_name)
147
149
  @_stack = response.stacks[0]
148
150
  rescue Aws::CloudFormation::Errors::ValidationError
149
151
  @_stack = nil
@@ -153,8 +155,8 @@ class Bora
153
155
  end
154
156
 
155
157
  def unprocessed_events
156
- return [] if !underlying_stack
157
- events = cloudformation.describe_stack_events({stack_name: underlying_stack.stack_id}).stack_events
158
+ return [] unless underlying_stack
159
+ events = cloudformation.describe_stack_events(stack_name: underlying_stack.stack_id).stack_events
158
160
  unprocessed_events = events.select do |event|
159
161
  !@processed_events.include?(event.event_id) && @previous_event_time < event.timestamp
160
162
  end
@@ -163,13 +165,10 @@ class Bora
163
165
  end
164
166
 
165
167
  def last_event_time
166
- return Time.at(0) if !underlying_stack
167
- events = cloudformation.describe_stack_events({stack_name: @stack_name}).stack_events
168
- events.length > 0 ? events[0].timestamp : Time.at(0)
168
+ return Time.at(0) unless underlying_stack
169
+ events = cloudformation.describe_stack_events(stack_name: @stack_name).stack_events
170
+ !events.empty? ? events[0].timestamp : Time.at(0)
169
171
  end
170
-
171
172
  end
172
-
173
-
174
173
  end
175
174
  end