bora 1.6.0 → 1.7.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: 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