cf_deployer 1.6.0 → 2.0.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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +29 -0
  3. data/.github/workflows/publish.yml +40 -0
  4. data/.rspec +4 -0
  5. data/ChangeLog.md +7 -0
  6. data/QUICKSTART.md +1 -1
  7. data/Rakefile +1 -1
  8. data/cf_deployer.gemspec +16 -10
  9. data/fixtures/vcr_cassettes/aws_call_count/driver/auto_scaling_group/healthy_instance_count.yml +2 -2
  10. data/lib/cf_deployer/cli.rb +1 -1
  11. data/lib/cf_deployer/config_loader.rb +2 -2
  12. data/lib/cf_deployer/config_validation.rb +1 -1
  13. data/lib/cf_deployer/driver/auto_scaling_group.rb +22 -26
  14. data/lib/cf_deployer/driver/cloud_formation_driver.rb +28 -18
  15. data/lib/cf_deployer/driver/elb_driver.rb +11 -4
  16. data/lib/cf_deployer/driver/instance.rb +4 -3
  17. data/lib/cf_deployer/driver/route53_driver.rb +17 -9
  18. data/lib/cf_deployer/logger.rb +10 -11
  19. data/lib/cf_deployer/stack.rb +18 -30
  20. data/lib/cf_deployer/status_presenter.rb +1 -1
  21. data/lib/cf_deployer/version.rb +1 -1
  22. data/lib/cf_deployer.rb +10 -7
  23. data/spec/aws_call_count_spec_helper.rb +14 -2
  24. data/spec/fakes/instance.rb +5 -4
  25. data/spec/fakes/route53_client.rb +4 -7
  26. data/spec/spec_helper.rb +11 -2
  27. data/spec/unit/application_spec.rb +9 -9
  28. data/spec/unit/component_spec.rb +3 -3
  29. data/spec/unit/config_loader_spec.rb +70 -69
  30. data/spec/unit/deployment_strategy/auto_scaling_group_swap_spec.rb +10 -10
  31. data/spec/unit/deployment_strategy/base_spec.rb +17 -18
  32. data/spec/unit/deployment_strategy/cname_swap_spec.rb +9 -9
  33. data/spec/unit/deployment_strategy/create_or_update_spec.rb +8 -8
  34. data/spec/unit/driver/auto_scaling_group_spec.rb +75 -65
  35. data/spec/unit/driver/aws_sdk_v3_spec.rb +163 -0
  36. data/spec/unit/driver/cloud_formation_spec.rb +19 -17
  37. data/spec/unit/driver/dry_run_spec.rb +8 -8
  38. data/spec/unit/driver/elb_spec.rb +10 -4
  39. data/spec/unit/driver/instance_spec.rb +1 -7
  40. data/spec/unit/driver/route53_spec.rb +34 -25
  41. data/spec/unit/hook_spec.rb +3 -3
  42. data/spec/unit/stack_spec.rb +22 -21
  43. metadata +134 -56
  44. data/.travis.yml +0 -20
@@ -21,7 +21,9 @@ module CfDeployer
21
21
  capabilities = @context[:capabilities] || []
22
22
  notify = @context[:notify] || []
23
23
  tags = @context[:tags] || {}
24
- params = to_str(@context[:inputs].select{|key, value| @context[:defined_parameters].keys.include?(key)})
24
+ params = @context[:inputs].select{|key, value| @context[:defined_parameters].keys.include?(key)}
25
+ .map { |key, value| { parameter_key: key.to_s, parameter_value: value.to_s } }
26
+
25
27
  CfDeployer::Driver::DryRun.guard "Skipping deploy" do
26
28
  if exists?
27
29
  override_policy_json = nil
@@ -56,7 +58,7 @@ module CfDeployer
56
58
  def find_output key
57
59
  begin
58
60
  @cf_driver.query_output(key)
59
- rescue AWS::CloudFormation::Errors::ValidationError => e
61
+ rescue Aws::CloudFormation::Errors::ValidationError => e
60
62
  raise ResourceNotInReadyState.new("Resource stack not in ready state yet, perhaps you should provision it first?")
61
63
  end
62
64
  end
@@ -88,20 +90,18 @@ module CfDeployer
88
90
  end
89
91
 
90
92
  def resource_statuses
91
- AWS.memoize do
92
- resources = @cf_driver.resource_statuses.merge( { :asg_instances => {}, :instances => {} } )
93
- if resources['AWS::AutoScaling::AutoScalingGroup']
94
- resources['AWS::AutoScaling::AutoScalingGroup'].keys.each do |asg_name|
95
- resources[:asg_instances][asg_name] = CfDeployer::Driver::AutoScalingGroup.new(asg_name).instance_statuses
96
- end
93
+ resources = @cf_driver.resource_statuses.merge( { :asg_instances => {}, :instances => {} } )
94
+ if resources['AWS::AutoScaling::AutoScalingGroup']
95
+ resources['AWS::AutoScaling::AutoScalingGroup'].keys.each do |asg_name|
96
+ resources[:asg_instances][asg_name] = CfDeployer::Driver::AutoScalingGroup.new(asg_name).instance_statuses
97
97
  end
98
- if resources['AWS::EC2::Instance']
99
- resources['AWS::EC2::Instance'].keys.each do |instance_id|
100
- resources[:instances][instance_id] = CfDeployer::Driver::Instance.new(instance_id).status
101
- end
98
+ end
99
+ if resources['AWS::EC2::Instance']
100
+ resources['AWS::EC2::Instance'].keys.each do |instance_id|
101
+ resources[:instances][instance_id] = CfDeployer::Driver::Instance.new(instance_id).status
102
102
  end
103
- resources
104
103
  end
104
+ resources
105
105
  end
106
106
 
107
107
  def name
@@ -114,10 +114,6 @@ module CfDeployer
114
114
 
115
115
  private
116
116
 
117
- def to_str(hash)
118
- hash.each { |k,v| hash[k] = v.to_s }
119
- end
120
-
121
117
  def update_stack(template, params, capabilities, tags, override_policy_json)
122
118
  Log.info "Updating stack #{@stack_name}..."
123
119
  args = {
@@ -136,7 +132,7 @@ module CfDeployer
136
132
  args = {
137
133
  :disable_rollback => true,
138
134
  :capabilities => capabilities,
139
- :notify => notify,
135
+ :notification_arns => notify,
140
136
  :tags => reformat_tags(tags),
141
137
  :parameters => params
142
138
  }
@@ -148,7 +144,7 @@ module CfDeployer
148
144
  end
149
145
 
150
146
  def stack_status
151
- @cf_driver.stack_status
147
+ @cf_driver.stack_status || :does_not_exist
152
148
  end
153
149
 
154
150
  def wait_for_stack_op_terminate
@@ -164,22 +160,14 @@ module CfDeployer
164
160
  def wait_for_stack_to_delete
165
161
  Timeout::timeout(900){
166
162
  while exists?
167
- begin
168
- Log.info "current status: #{stack_status}"
169
- sleep 15
170
- rescue AWS::CloudFormation::Errors::ValidationError => e
171
- if e.message =~ /does not exist/
172
- break # This is what we wanted anyways
173
- else
174
- raise e
175
- end
176
- end
163
+ Log.info "current status: #{stack_status}"
164
+ sleep 15
177
165
  end
178
166
  }
179
167
  end
180
168
 
181
169
  def reformat_tags tags_hash
182
- tags_hash.keys.map { |key| { 'Key' => key.to_s, 'Value' => tags_hash[key].to_s } }
170
+ tags_hash.keys.map { |key| { :key => key.to_s, :value => tags_hash[key].to_s } }
183
171
  end
184
172
  end
185
173
  end
@@ -155,7 +155,7 @@ module CfDeployer
155
155
  col1[i] ||= ''
156
156
  col2[i] ||= ''
157
157
 
158
- line = ''
158
+ line = +''
159
159
  line << col1[i].ljust(UNPADDED_TABLE_CELL_WIDTH + PAD.size + invisible_length(col1[i]))
160
160
  line << '|'
161
161
  line << col2[i]
@@ -1,3 +1,3 @@
1
1
  module CfDeployer
2
- VERSION = "1.6.0"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/cf_deployer.rb CHANGED
@@ -2,11 +2,17 @@ require 'digest'
2
2
  require 'set'
3
3
  require 'time'
4
4
  require 'json'
5
+ require 'yaml'
5
6
  require 'timeout'
6
- require 'aws-sdk'
7
+ require 'aws-sdk-autoscaling'
8
+ require 'aws-sdk-ec2'
9
+ require 'aws-sdk-core'
10
+ require 'aws-sdk-cloudformation'
11
+ require 'aws-sdk-elasticloadbalancing'
12
+ require 'aws-sdk-route53'
7
13
  require 'erb'
8
14
  require 'fileutils'
9
- require 'log4r'
15
+ require 'logger'
10
16
  require 'pp'
11
17
  require 'forwardable'
12
18
 
@@ -38,7 +44,7 @@ require_relative 'cf_deployer/defaults'
38
44
 
39
45
  module CfDeployer
40
46
 
41
- AWS.config(:max_retries => 20)
47
+ Aws.config.update(retry_limit: 20)
42
48
 
43
49
  def self.config opts
44
50
  config = self.parseconfig opts, false
@@ -50,19 +56,16 @@ module CfDeployer
50
56
 
51
57
  def self.deploy opts
52
58
  config = self.parseconfig opts
53
- # AWS.config(:logger => Logger.new($stdout))
54
59
  Application.new(config).deploy
55
60
  end
56
61
 
57
62
  def self.runhook opts
58
63
  config = self.parseconfig opts
59
- # AWS.config(:logger => Logger.new($stdout))
60
64
  Application.new(config).run_hook opts[:component].first, opts[:hook_name]
61
65
  end
62
66
 
63
67
  def self.destroy opts
64
68
  config = self.parseconfig opts, false
65
- # AWS.config(:logger => Logger.new($stdout))
66
69
  Application.new(config).destroy
67
70
  end
68
71
 
@@ -97,7 +100,7 @@ module CfDeployer
97
100
  private
98
101
 
99
102
  def self.parseconfig options, validate_inputs = true
100
- AWS.config(:region => options[:region]) if options[:region]
103
+ Aws.config.update({region: options[:region]}) if options[:region]
101
104
  options[:cli_overrides] = {:settings => options.delete(:settings), :inputs => options.delete(:inputs)}
102
105
  config = ConfigLoader.new.load options
103
106
  ConfigValidation.new.validate config, validate_inputs
@@ -11,8 +11,20 @@ def override_aws_environment options = {}
11
11
  options[:AWS_REGION] ||= 'us-east-1'
12
12
  options[:AWS_ACCESS_KEY_ID] ||= 'someId'
13
13
  options[:AWS_SECRET_ACCESS_KEY] ||= 'secretKey'
14
-
15
- override_environment_variables(options) { yield }
14
+ options[:AWS_EC2_METADATA_DISABLED] ||= 'true'
15
+ options[:AWS_SDK_CONFIG_OPT_OUT] ||= '1'
16
+
17
+ override_environment_variables(options) do
18
+ previous_aws_config = Aws.config.dup
19
+ Aws.config.update(
20
+ region: options[:AWS_REGION],
21
+ credentials: Aws::Credentials.new(options[:AWS_ACCESS_KEY_ID], options[:AWS_SECRET_ACCESS_KEY])
22
+ )
23
+ yield
24
+ ensure
25
+ Aws.config.clear
26
+ Aws.config.update(previous_aws_config)
27
+ end
16
28
  end
17
29
 
18
30
  def override_environment_variables options = {}
@@ -1,10 +1,10 @@
1
1
  module Fakes
2
2
  class Instance
3
3
 
4
- SIMPLE_ATTRIBS = [ :id, :status, :public_ip_address, :private_ip_address, :image_id ]
4
+ SIMPLE_ATTRIBS = [ :id, :public_ip_address, :private_ip_address, :image_id ]
5
5
 
6
6
  attr_reader *SIMPLE_ATTRIBS
7
- attr_reader :key_pair
7
+ attr_reader :state, :key_name
8
8
 
9
9
  def initialize(options)
10
10
  defaults = {
@@ -20,7 +20,8 @@ module Fakes
20
20
  instance_variable_set "@#{attrib}", (options[attrib] || defaults[attrib])
21
21
  end
22
22
 
23
- @key_pair = ::AWS::EC2::KeyPair.new (options[:key_pair] || defaults[:key_pair] )
23
+ @state = Aws::EC2::Types::InstanceState.new(name: (options[:status] || defaults[:status]).to_s)
24
+ @key_name = options[:key_pair] || defaults[:key_pair]
24
25
  end
25
26
 
26
27
  def inspect
@@ -29,4 +30,4 @@ module Fakes
29
30
  alias_method :to_s, :inspect
30
31
 
31
32
  end
32
- end
33
+ end
@@ -2,19 +2,16 @@ module Fakes
2
2
  class AWSRoute53
3
3
  attr_reader :fail_counter, :hosted_zones, :client
4
4
  def initialize(opts = {})
5
- @client = AWSRoute53Client.new(opts)
6
5
  @hosted_zones = opts[:hosted_zones]
7
- @fail_counter = 0
8
- end
9
- end
10
6
 
11
- class AWSRoute53Client
12
- attr_reader :fail_counter
13
- def initialize(opts = {})
14
7
  @times_to_fail = opts[:times_to_fail]
15
8
  @fail_counter = 0
16
9
  end
17
10
 
11
+ def list_hosted_zones_by_name(*args)
12
+ Struct.new(:hosted_zones).new(hosted_zones)
13
+ end
14
+
18
15
  def change_resource_record_sets(*args)
19
16
  @fail_counter = @fail_counter + 1
20
17
  raise 'Error' if @fail_counter <= @times_to_fail
data/spec/spec_helper.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  require_relative '../lib/cf_deployer'
2
2
  Dir.glob("#{File.dirname File.absolute_path(__FILE__)}/fakes/*.rb") { |file| require file }
3
3
 
4
- CfDeployer::Log.log.outputters = nil
4
+ CfDeployer::Log.log = Logger.new(File::NULL)
5
5
 
6
6
  RSPEC_LOG = Logger.new(STDOUT)
7
7
  RSPEC_LOG.level = Logger::WARN
8
8
 
9
9
  if ENV['DEBUG']
10
10
  RSPEC_LOG.level = Logger::DEBUG
11
- AWS.config :logger => RSPEC_LOG
11
+ # AWS.config :logger => RSPEC_LOG
12
12
  end
13
13
 
14
14
  def puts *args
@@ -20,3 +20,12 @@ def ignore_errors
20
20
  rescue => e
21
21
  RSPEC_LOG.debug "Intentionally ignoring error: #{e.message}"
22
22
  end
23
+
24
+ RSpec.configure do |config|
25
+ # These two settings work together to allow you to limit a spec run
26
+ # to individual examples or groups you care about by tagging them with
27
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
28
+ # get run.
29
+ config.filter_run :focus
30
+ config.run_all_when_everything_filtered = true
31
+ end
@@ -28,11 +28,11 @@ describe "application" do
28
28
  end
29
29
 
30
30
  it "application should get all components" do
31
- @app.components.length.should eq(4)
32
- @base.should_not be_nil
33
- @db.should_not be_nil
34
- @queue.should_not be_nil
35
- @web.should_not be_nil
31
+ expect(@app.components.length).to eq(4)
32
+ expect(@base).not_to be_nil
33
+ expect(@db).not_to be_nil
34
+ expect(@queue).not_to be_nil
35
+ expect(@web).not_to be_nil
36
36
  end
37
37
 
38
38
  context "order components by dependencies" do
@@ -154,7 +154,7 @@ describe "application" do
154
154
  context "deploy all components" do
155
155
  it "should deploy all components if no component specified" do
156
156
  @app.deploy
157
- @log.should eq("base db queue web ")
157
+ expect(@log).to eq("base db queue web ")
158
158
  end
159
159
  end
160
160
 
@@ -162,7 +162,7 @@ describe "application" do
162
162
  it "should deploy specified components" do
163
163
  @context[:targets] = ['web', 'db']
164
164
  @app.deploy
165
- @log.should eq("db web ")
165
+ expect(@log).to eq("db web ")
166
166
  end
167
167
  end
168
168
  end
@@ -179,13 +179,13 @@ describe "application" do
179
179
 
180
180
  it 'should get json templates for all components' do
181
181
  @app.json
182
- @log.should eq("base db queue web ")
182
+ expect(@log).to eq("base db queue web ")
183
183
  end
184
184
 
185
185
  it 'should get json templates for components specified' do
186
186
  @context[:targets] = ['web', 'db']
187
187
  @app.json
188
- @log.should eq('db web ')
188
+ expect(@log).to eq('db web ')
189
189
  end
190
190
  end
191
191
  end
@@ -98,7 +98,7 @@ describe "component" do
98
98
 
99
99
  it "should ask strategy if component exists" do
100
100
  expect(@strategy).to receive(:exists?){ true }
101
- @web.exists?.should eq(true)
101
+ expect(@web.exists?).to eq(true)
102
102
  end
103
103
 
104
104
  it "should find direct dependencies" do
@@ -106,7 +106,7 @@ describe "component" do
106
106
  base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
107
107
  web.dependencies << base
108
108
 
109
- web.depends_on?(base).should eq(true)
109
+ expect(web.depends_on?(base)).to eq(true)
110
110
  end
111
111
 
112
112
  it "should find transitive dependencies" do
@@ -117,7 +117,7 @@ describe "component" do
117
117
  haproxy.dependencies << base
118
118
  web.dependencies << haproxy
119
119
 
120
- web.depends_on?(base).should eq(true)
120
+ expect(web.depends_on?(base)).to eq(true)
121
121
  end
122
122
 
123
123
  it "should find cyclic dependency" do