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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +29 -0
- data/.github/workflows/publish.yml +40 -0
- data/.rspec +4 -0
- data/ChangeLog.md +7 -0
- data/QUICKSTART.md +1 -1
- data/Rakefile +1 -1
- data/cf_deployer.gemspec +16 -10
- data/fixtures/vcr_cassettes/aws_call_count/driver/auto_scaling_group/healthy_instance_count.yml +2 -2
- data/lib/cf_deployer/cli.rb +1 -1
- data/lib/cf_deployer/config_loader.rb +2 -2
- data/lib/cf_deployer/config_validation.rb +1 -1
- data/lib/cf_deployer/driver/auto_scaling_group.rb +22 -26
- data/lib/cf_deployer/driver/cloud_formation_driver.rb +28 -18
- data/lib/cf_deployer/driver/elb_driver.rb +11 -4
- data/lib/cf_deployer/driver/instance.rb +4 -3
- data/lib/cf_deployer/driver/route53_driver.rb +17 -9
- data/lib/cf_deployer/logger.rb +10 -11
- data/lib/cf_deployer/stack.rb +18 -30
- data/lib/cf_deployer/status_presenter.rb +1 -1
- data/lib/cf_deployer/version.rb +1 -1
- data/lib/cf_deployer.rb +10 -7
- data/spec/aws_call_count_spec_helper.rb +14 -2
- data/spec/fakes/instance.rb +5 -4
- data/spec/fakes/route53_client.rb +4 -7
- data/spec/spec_helper.rb +11 -2
- data/spec/unit/application_spec.rb +9 -9
- data/spec/unit/component_spec.rb +3 -3
- data/spec/unit/config_loader_spec.rb +70 -69
- data/spec/unit/deployment_strategy/auto_scaling_group_swap_spec.rb +10 -10
- data/spec/unit/deployment_strategy/base_spec.rb +17 -18
- data/spec/unit/deployment_strategy/cname_swap_spec.rb +9 -9
- data/spec/unit/deployment_strategy/create_or_update_spec.rb +8 -8
- data/spec/unit/driver/auto_scaling_group_spec.rb +75 -65
- data/spec/unit/driver/aws_sdk_v3_spec.rb +163 -0
- data/spec/unit/driver/cloud_formation_spec.rb +19 -17
- data/spec/unit/driver/dry_run_spec.rb +8 -8
- data/spec/unit/driver/elb_spec.rb +10 -4
- data/spec/unit/driver/instance_spec.rb +1 -7
- data/spec/unit/driver/route53_spec.rb +34 -25
- data/spec/unit/hook_spec.rb +3 -3
- data/spec/unit/stack_spec.rb +22 -21
- metadata +134 -56
- data/.travis.yml +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e1556ed67ab725b4ff0aea77a7c5ded196cc314f1654dd7724d2fe3f501604ef
|
|
4
|
+
data.tar.gz: 5e343a49b896d720227e69dbe85c6702456dfc0a95ce8d83b5b7c5b3686d4183
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4048ce2d730fe916f6b895950af91023be72e7bae0bfbfbcb536fd86e20f5c78801575cb681d212ca9df8749bc595dadd1fcec56abb67576b047e555c8e18c8c
|
|
7
|
+
data.tar.gz: 7ef7a7ed4210aa1c46d57a0a1add255d84b8f921b798aedb58381dc3420476f670d5dea115d000ffc32d72ef0f4a8477a0757ffa9d7021b87f2cf0539a8d6409
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ${{ vars.GHA_ENTERPRISE_LABELS || 'cai-standard-x86-linux' }}
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
ruby-version: ['3.3', '3.4', '4.0']
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Setup Ruby
|
|
23
|
+
uses: ruby/setup-ruby@v1
|
|
24
|
+
with:
|
|
25
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
26
|
+
bundler-cache: true
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: bundle exec rake
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ${{ vars.GHA_ENTERPRISE_LABELS || 'cai-standard-x86-linux' }}
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Setup Ruby
|
|
18
|
+
uses: ruby/setup-ruby@v1
|
|
19
|
+
with:
|
|
20
|
+
ruby-version: '3.3'
|
|
21
|
+
bundler-cache: true
|
|
22
|
+
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: bundle exec rake
|
|
25
|
+
|
|
26
|
+
- name: Verify tag matches gem version
|
|
27
|
+
run: |
|
|
28
|
+
version=$(ruby -r ./lib/cf_deployer/version -e 'puts CfDeployer::VERSION')
|
|
29
|
+
if [ "${GITHUB_REF_NAME}" != "v${version}" ]; then
|
|
30
|
+
echo "Tag ${GITHUB_REF_NAME} does not match CfDeployer::VERSION v${version}"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
- name: Build gem
|
|
35
|
+
run: gem build cf_deployer.gemspec
|
|
36
|
+
|
|
37
|
+
- name: Push gem to RubyGems
|
|
38
|
+
env:
|
|
39
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
40
|
+
run: gem push cf_deployer-*.gem
|
data/.rspec
ADDED
data/ChangeLog.md
CHANGED
|
@@ -63,3 +63,10 @@ version 1.6.0
|
|
|
63
63
|
- Automatically rollback on failures for AutoScalingGroup based deployments (See: https://github.com/manheim/cf_deployer/issues/39)
|
|
64
64
|
- Improve error message when no stack is exists on ASG-based switch (See: https://github.com/manheim/cf_deployer/issues/50)
|
|
65
65
|
- Reduce AWS calls during healthy_instance_count (See: https://github.com/manheim/cf_deployer/issues/48)
|
|
66
|
+
|
|
67
|
+
version 2.0.0
|
|
68
|
+
- Requires Ruby 3.3 or newer
|
|
69
|
+
- Replaced aws-sdk v1 with the modular AWS SDK v3 gems (aws-sdk-autoscaling, aws-sdk-cloudformation, aws-sdk-ec2, aws-sdk-elasticloadbalancing, aws-sdk-route53)
|
|
70
|
+
- Replaced log4r with the standard library Logger (log output format is unchanged)
|
|
71
|
+
- Removed the json runtime dependency (json ships with Ruby)
|
|
72
|
+
- Handle non-existent stacks gracefully
|
data/QUICKSTART.md
CHANGED
|
@@ -36,7 +36,7 @@ Note that you can use ERB and settings/inputs from the cf_deployer.yml file whil
|
|
|
36
36
|
==================
|
|
37
37
|
### Setting up your project to use CFDeployer
|
|
38
38
|
|
|
39
|
-
* **CFDeployer is a Ruby gem so you'll need a working version of Ruby.** CFDeployer
|
|
39
|
+
* **CFDeployer is a Ruby gem so you'll need a working version of Ruby.** CFDeployer requires Ruby 3.3 or newer and uses the official AWS SDK for Ruby (v3) under the covers.
|
|
40
40
|
|
|
41
41
|
* **The use of the [Bundler](http://bundler.io/) gem is strongly encouraged.** Put "gem 'cf_deployer'" into your Gemfile.
|
|
42
42
|
|
data/Rakefile
CHANGED
|
@@ -5,7 +5,7 @@ require 'bundler/gem_tasks'
|
|
|
5
5
|
require 'rspec/core/rake_task'
|
|
6
6
|
|
|
7
7
|
namespace :spec do
|
|
8
|
-
RSPEC_OPTS = ["--format", "
|
|
8
|
+
RSPEC_OPTS = ["--format", "documentation", "--format", "html", "--out", "spec_result.html", "--colour"]
|
|
9
9
|
|
|
10
10
|
RSpec::Core::RakeTask.new(:file) do |t|
|
|
11
11
|
t.rspec_opts = RSPEC_OPTS
|
data/cf_deployer.gemspec
CHANGED
|
@@ -8,18 +8,24 @@ Gem::Specification.new do |gem|
|
|
|
8
8
|
gem.summary = %q{Support multiple components deployment using CloudFormation templates with multiple blue green strategies.}
|
|
9
9
|
gem.homepage = "http://github.com/manheim/cf_deployer"
|
|
10
10
|
gem.license = 'MIT'
|
|
11
|
+
gem.required_ruby_version = '>= 3.3'
|
|
11
12
|
|
|
12
|
-
gem.add_runtime_dependency 'aws-sdk','1.
|
|
13
|
-
gem.add_runtime_dependency '
|
|
14
|
-
gem.add_runtime_dependency '
|
|
15
|
-
gem.add_runtime_dependency '
|
|
13
|
+
gem.add_runtime_dependency 'aws-sdk-autoscaling', '~> 1.86'
|
|
14
|
+
gem.add_runtime_dependency 'aws-sdk-cloudformation', '~> 1.76'
|
|
15
|
+
gem.add_runtime_dependency 'aws-sdk-core', '~> 3.170'
|
|
16
|
+
gem.add_runtime_dependency 'aws-sdk-ec2', '~> 1.365'
|
|
17
|
+
gem.add_runtime_dependency 'aws-sdk-elasticloadbalancing', '~> 1.42'
|
|
18
|
+
gem.add_runtime_dependency 'aws-sdk-route53', '~> 1.71'
|
|
16
19
|
gem.add_runtime_dependency 'diffy'
|
|
17
|
-
gem.
|
|
18
|
-
gem.
|
|
19
|
-
gem.
|
|
20
|
-
gem.add_development_dependency '
|
|
21
|
-
gem.add_development_dependency '
|
|
22
|
-
gem.add_development_dependency '
|
|
20
|
+
gem.add_runtime_dependency 'logger', '~> 1.6'
|
|
21
|
+
gem.add_runtime_dependency 'rainbow'
|
|
22
|
+
gem.add_runtime_dependency 'thor'
|
|
23
|
+
gem.add_development_dependency 'pry', '~> 0.13'
|
|
24
|
+
gem.add_development_dependency 'rake', '~> 13.0'
|
|
25
|
+
gem.add_development_dependency 'rspec', '~> 3.13'
|
|
26
|
+
gem.add_development_dependency 'vcr', '~> 6.0'
|
|
27
|
+
gem.add_development_dependency 'webmock', '~> 3.11'
|
|
28
|
+
gem.add_development_dependency 'yard', '~> 0.9', '>= 0.9.44'
|
|
23
29
|
|
|
24
30
|
gem.files = `git ls-files`.split($\).reject {|f| f =~ /^samples\// }
|
|
25
31
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
data/fixtures/vcr_cassettes/aws_call_count/driver/auto_scaling_group/healthy_instance_count.yml
CHANGED
|
@@ -45,7 +45,7 @@ http_interactions:
|
|
|
45
45
|
\ </member>\n </AutoScalingGroups>\n </DescribeAutoScalingGroupsResult>\n
|
|
46
46
|
\ <ResponseMetadata>\n <RequestId>12345</RequestId>\n
|
|
47
47
|
\ </ResponseMetadata>\n</DescribeAutoScalingGroupsResponse>\n"
|
|
48
|
-
http_version:
|
|
48
|
+
http_version:
|
|
49
49
|
recorded_at: Tue, 27 Sep 2016 05:47:40 GMT
|
|
50
50
|
- request:
|
|
51
51
|
method: post
|
|
@@ -65,7 +65,7 @@ http_interactions:
|
|
|
65
65
|
\ </member>\n </AutoScalingInstances>\n </DescribeAutoScalingInstancesResult>\n
|
|
66
66
|
\ <ResponseMetadata>\n <RequestId>12346</RequestId>\n
|
|
67
67
|
\ </ResponseMetadata>\n</DescribeAutoScalingInstancesResponse>\n"
|
|
68
|
-
http_version:
|
|
68
|
+
http_version:
|
|
69
69
|
recorded_at: Tue, 27 Sep 2016 05:47:40 GMT
|
|
70
70
|
- request:
|
|
71
71
|
method: post
|
data/lib/cf_deployer/cli.rb
CHANGED
|
@@ -94,7 +94,7 @@ module CfDeployer
|
|
|
94
94
|
def set_log_level
|
|
95
95
|
if options[:'log-level'] == 'aws-debug'
|
|
96
96
|
CfDeployer::Log.level = 'debug'
|
|
97
|
-
|
|
97
|
+
Aws.config.update(:logger => Logger.new($stdout))
|
|
98
98
|
else
|
|
99
99
|
CfDeployer::Log.level = options[:'log-level']
|
|
100
100
|
end
|
|
@@ -3,7 +3,7 @@ module CfDeployer
|
|
|
3
3
|
|
|
4
4
|
def self.erb_to_json filename, config
|
|
5
5
|
json_file = File.join(config[:config_dir], "#{filename}.json")
|
|
6
|
-
raise ApplicationError.new("#{json_file} is missing") unless File.
|
|
6
|
+
raise ApplicationError.new("#{json_file} is missing") unless File.exist?(json_file)
|
|
7
7
|
CfDeployer::Log.info "ERBing JSON for #{filename}"
|
|
8
8
|
ERB.new(File.read(json_file)).result(binding)
|
|
9
9
|
rescue RuntimeError,TypeError,NoMethodError => e
|
|
@@ -39,7 +39,7 @@ module CfDeployer
|
|
|
39
39
|
|
|
40
40
|
def load_yaml(text)
|
|
41
41
|
YAML.load text
|
|
42
|
-
rescue Psych::SyntaxError => e
|
|
42
|
+
rescue ::Psych::SyntaxError => e
|
|
43
43
|
error_document text
|
|
44
44
|
raise e
|
|
45
45
|
rescue
|
|
@@ -79,7 +79,7 @@ module CfDeployer
|
|
|
79
79
|
file_name = component[hook_name][:file]
|
|
80
80
|
return unless file_name
|
|
81
81
|
path = File.join(component[:config_dir], file_name)
|
|
82
|
-
@errors << "File '#{path}' does not exist, which is required by hook '#{hook_name}'" unless File.
|
|
82
|
+
@errors << "File '#{path}' does not exist, which is required by hook '#{hook_name}'" unless File.exist?(path)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def check_environments
|
|
@@ -3,7 +3,7 @@ module CfDeployer
|
|
|
3
3
|
class AutoScalingGroup
|
|
4
4
|
extend Forwardable
|
|
5
5
|
|
|
6
|
-
def_delegators :aws_group, :
|
|
6
|
+
def_delegators :aws_group, :instances, :desired_capacity, :load_balancer_names
|
|
7
7
|
|
|
8
8
|
attr_reader :group_name, :group
|
|
9
9
|
|
|
@@ -22,7 +22,7 @@ module CfDeployer
|
|
|
22
22
|
Log.info "warming up auto scaling group #{group_name} to #{desired}"
|
|
23
23
|
|
|
24
24
|
CfDeployer::Driver::DryRun.guard "Skipping ASG warmup" do
|
|
25
|
-
aws_group.set_desired_capacity desired
|
|
25
|
+
aws_group.set_desired_capacity({desired_capacity: desired})
|
|
26
26
|
wait_for_desired_capacity
|
|
27
27
|
end
|
|
28
28
|
end
|
|
@@ -38,14 +38,14 @@ module CfDeployer
|
|
|
38
38
|
Log.info "Cooling down #{group_name}"
|
|
39
39
|
CfDeployer::Driver::DryRun.guard "Skipping ASG cooldown" do
|
|
40
40
|
aws_group.update :min_size => 0, :max_size => 0
|
|
41
|
-
aws_group.set_desired_capacity 0
|
|
41
|
+
aws_group.set_desired_capacity({desired_capacity: 0})
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def instance_statuses
|
|
46
46
|
instance_info = {}
|
|
47
|
-
|
|
48
|
-
instance_info[instance.id] = CfDeployer::Driver::Instance.new(instance).status
|
|
47
|
+
instances.each do |instance|
|
|
48
|
+
instance_info[instance.id] = CfDeployer::Driver::Instance.new(instance.id).status
|
|
49
49
|
end
|
|
50
50
|
instance_info
|
|
51
51
|
end
|
|
@@ -59,38 +59,30 @@ module CfDeployer
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def desired_capacity_reached?
|
|
62
|
+
aws_group.reload
|
|
62
63
|
healthy_instance_count >= desired_capacity
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
def healthy_instance_ids
|
|
66
|
-
|
|
67
|
+
_instances = instances.select do |instance|
|
|
67
68
|
'HEALTHY'.casecmp(instance.health_status) == 0
|
|
68
69
|
end
|
|
69
|
-
|
|
70
|
+
_instances.map(&:id)
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
def in_service_instance_ids
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.to_a
|
|
79
|
-
.collect { |elb_healths|
|
|
80
|
-
elb_healths.select { |health| health[:state] == 'InService' }
|
|
81
|
-
.map { |health| health[:instance].id }
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
ids.inject(:&)
|
|
74
|
+
elb_names = load_balancer_names
|
|
75
|
+
|
|
76
|
+
return [] if elb_names.empty?
|
|
77
|
+
|
|
78
|
+
elb_driver.in_service_instance_ids elb_names
|
|
85
79
|
end
|
|
86
80
|
|
|
87
81
|
def healthy_instance_count
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
instances.count
|
|
93
|
-
end
|
|
82
|
+
instances = healthy_instance_ids
|
|
83
|
+
instances &= in_service_instance_ids unless load_balancer_names.empty?
|
|
84
|
+
Log.info "Healthy instance count: #{instances.count}"
|
|
85
|
+
instances.count
|
|
94
86
|
rescue => e
|
|
95
87
|
Log.error "Unable to determine healthy instance count due to error: #{e.message}"
|
|
96
88
|
-1
|
|
@@ -99,7 +91,11 @@ module CfDeployer
|
|
|
99
91
|
private
|
|
100
92
|
|
|
101
93
|
def aws_group
|
|
102
|
-
@my_group ||=
|
|
94
|
+
@my_group ||= Aws::AutoScaling::AutoScalingGroup.new(group_name)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def elb_driver
|
|
98
|
+
@elb_driver ||= Elb.new
|
|
103
99
|
end
|
|
104
100
|
end
|
|
105
101
|
end
|
|
@@ -7,22 +7,22 @@ module CfDeployer
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def stack_exists?
|
|
10
|
-
aws_stack.
|
|
10
|
+
!aws_stack.nil?
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def create_stack template, opts
|
|
14
14
|
CfDeployer::Driver::DryRun.guard "Skipping create_stack" do
|
|
15
|
-
cloud_formation.
|
|
15
|
+
cloud_formation.create_stack(opts.merge(stack_name: @stack_name, template_body: template))
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def update_stack template, opts
|
|
20
20
|
begin
|
|
21
21
|
CfDeployer::Driver::DryRun.guard "Skipping update_stack" do
|
|
22
|
-
|
|
22
|
+
cloud_formation.update_stack(opts.merge(stack_name: @stack_name, template_body: template))
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
rescue
|
|
25
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
|
26
26
|
if e.message =~ /No updates are to be performed/
|
|
27
27
|
Log.info e.message
|
|
28
28
|
return false
|
|
@@ -35,29 +35,31 @@ module CfDeployer
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def stack_status
|
|
38
|
-
aws_stack
|
|
38
|
+
aws_stack&.stack_status&.downcase&.to_sym
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def outputs
|
|
42
|
-
|
|
43
|
-
memo[o.
|
|
42
|
+
existing_stack.outputs.inject({}) do |memo, o|
|
|
43
|
+
memo[o.output_key] = o.output_value
|
|
44
44
|
memo
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def parameters
|
|
49
|
-
|
|
49
|
+
existing_stack.parameters.each_with_object({}) do |p, memo|
|
|
50
|
+
memo[p.parameter_key] = p.parameter_value
|
|
51
|
+
end
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
def query_output key
|
|
53
|
-
output =
|
|
54
|
-
output && output.
|
|
55
|
+
output = existing_stack.outputs.find { |o| o.output_key == key }
|
|
56
|
+
output && output.output_value
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
def delete_stack
|
|
58
60
|
if stack_exists?
|
|
59
61
|
CfDeployer::Driver::DryRun.guard "Skipping create_stack" do
|
|
60
|
-
|
|
62
|
+
cloud_formation.delete_stack(stack_name: @stack_name)
|
|
61
63
|
end
|
|
62
64
|
else
|
|
63
65
|
Log.info "Stack #{@stack_name} does not exist!"
|
|
@@ -66,28 +68,36 @@ module CfDeployer
|
|
|
66
68
|
|
|
67
69
|
def resource_statuses
|
|
68
70
|
resources = {}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
cloud_formation.list_stack_resources(stack_name: @stack_name).each do |page|
|
|
72
|
+
page.stack_resource_summaries.each do |rs|
|
|
73
|
+
resources[rs[:resource_type]] ||= {}
|
|
74
|
+
resources[rs[:resource_type]][rs[:physical_resource_id]] = rs[:resource_status]
|
|
75
|
+
end
|
|
72
76
|
end
|
|
73
77
|
resources
|
|
74
78
|
end
|
|
75
79
|
|
|
76
80
|
def template
|
|
77
|
-
|
|
81
|
+
cloud_formation.get_template(stack_name: @stack_name).template_body
|
|
78
82
|
end
|
|
79
83
|
|
|
80
84
|
private
|
|
81
85
|
|
|
82
86
|
def cloud_formation
|
|
83
|
-
|
|
87
|
+
@cloud_formation ||= Aws::CloudFormation::Client.new
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
def aws_stack
|
|
87
|
-
cloud_formation.
|
|
91
|
+
cloud_formation.describe_stacks(stack_name: @stack_name).stacks.first
|
|
92
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
|
93
|
+
raise unless e.message =~ /does not exist/
|
|
94
|
+
nil
|
|
88
95
|
end
|
|
89
96
|
|
|
90
|
-
|
|
97
|
+
def existing_stack
|
|
98
|
+
aws_stack || raise(Aws::CloudFormation::Errors::ValidationError.new(nil, "Stack with id #{@stack_name} does not exist"))
|
|
99
|
+
end
|
|
91
100
|
|
|
101
|
+
end
|
|
92
102
|
end
|
|
93
103
|
end
|
|
@@ -2,16 +2,23 @@ module CfDeployer
|
|
|
2
2
|
module Driver
|
|
3
3
|
class Elb
|
|
4
4
|
def find_dns_and_zone_id elb_id
|
|
5
|
-
elb = elb_driver.
|
|
6
|
-
{ :canonical_hosted_zone_name_id => elb
|
|
5
|
+
elb = elb_driver.describe_load_balancers({:load_balancer_names => [elb_id]})&.load_balancer_descriptions&.first
|
|
6
|
+
{ :canonical_hosted_zone_name_id => elb&.canonical_hosted_zone_name_id, :dns_name => elb&.dns_name }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def in_service_instance_ids elb_ids
|
|
10
|
+
elb_ids.collect do |elb_id|
|
|
11
|
+
elb_driver.describe_instance_health({:load_balancer_name => elb_id}).instance_states
|
|
12
|
+
.collect{|instance| instance.state == 'InService' ? instance.instance_id : nil }.compact
|
|
13
|
+
end.inject(:&)
|
|
7
14
|
end
|
|
8
15
|
|
|
9
16
|
private
|
|
10
17
|
|
|
11
18
|
def elb_driver
|
|
12
|
-
|
|
19
|
+
@elb_driver ||= Aws::ElasticLoadBalancing::Client.new
|
|
13
20
|
end
|
|
14
21
|
|
|
15
22
|
end
|
|
16
23
|
end
|
|
17
|
-
end
|
|
24
|
+
end
|
|
@@ -14,15 +14,16 @@ module CfDeployer
|
|
|
14
14
|
|
|
15
15
|
def status
|
|
16
16
|
instance_info = { }
|
|
17
|
-
[:
|
|
17
|
+
[:public_ip_address, :private_ip_address, :image_id].each do |stat|
|
|
18
18
|
instance_info[stat] = aws_instance.send(stat)
|
|
19
19
|
end
|
|
20
|
-
instance_info[:
|
|
20
|
+
instance_info[:status] = aws_instance.state.name.tr('-', '_').to_sym
|
|
21
|
+
instance_info[:key_pair] = aws_instance.key_name
|
|
21
22
|
instance_info
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def aws_instance
|
|
25
|
-
@instance_obj ||=
|
|
26
|
+
@instance_obj ||= Aws::EC2::Instance.new(@id)
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
end
|
|
@@ -2,7 +2,7 @@ module CfDeployer
|
|
|
2
2
|
module Driver
|
|
3
3
|
class Route53
|
|
4
4
|
def initialize(aws_route53 = nil)
|
|
5
|
-
@aws_route53 = aws_route53 ||
|
|
5
|
+
@aws_route53 = aws_route53 || Aws::Route53::Client.new
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def find_alias_target(hosted_zone_name, target_host_name)
|
|
@@ -15,9 +15,8 @@ module CfDeployer
|
|
|
15
15
|
|
|
16
16
|
def set_alias_target(hosted_zone_name, target_host_name, elb_hosted_zone_id, elb_dnsname)
|
|
17
17
|
Log.info "set alias target --Hosted Zone: #{hosted_zone_name} --Host Name: #{target_host_name} --ELB DNS Name: #{elb_dnsname} --ELB Zone ID: #{elb_hosted_zone_id}"
|
|
18
|
-
hosted_zone_name = trailing_dot(hosted_zone_name)
|
|
19
18
|
target_host_name = trailing_dot(target_host_name)
|
|
20
|
-
hosted_zone =
|
|
19
|
+
hosted_zone = get_hosted_zone(hosted_zone_name)
|
|
21
20
|
raise ApplicationError.new('Target zone not found!') if hosted_zone.nil?
|
|
22
21
|
|
|
23
22
|
change = {
|
|
@@ -34,7 +33,7 @@ module CfDeployer
|
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
batch = {
|
|
37
|
-
hosted_zone_id: hosted_zone.
|
|
36
|
+
hosted_zone_id: hosted_zone.id,
|
|
38
37
|
change_batch: {
|
|
39
38
|
changes: [change]
|
|
40
39
|
}
|
|
@@ -49,10 +48,15 @@ module CfDeployer
|
|
|
49
48
|
hosted_zone = get_hosted_zone(hosted_zone_name)
|
|
50
49
|
return unless hosted_zone
|
|
51
50
|
record_set = get_record_set(hosted_zone, target_host_name)
|
|
51
|
+
return unless record_set
|
|
52
52
|
CfDeployer::Driver::DryRun.guard "Skipping Route53 DNS delete" do
|
|
53
|
-
|
|
53
|
+
@aws_route53.change_resource_record_sets(
|
|
54
|
+
hosted_zone_id: hosted_zone.id,
|
|
55
|
+
change_batch: { changes: [{ action: 'DELETE', resource_record_set: record_set.to_h }] }
|
|
56
|
+
)
|
|
54
57
|
end
|
|
55
58
|
end
|
|
59
|
+
|
|
56
60
|
private
|
|
57
61
|
|
|
58
62
|
def change_resource_record_sets_with_retry(batch)
|
|
@@ -60,7 +64,7 @@ module CfDeployer
|
|
|
60
64
|
while attempts < 20
|
|
61
65
|
begin
|
|
62
66
|
attempts = attempts + 1
|
|
63
|
-
@aws_route53.
|
|
67
|
+
@aws_route53.change_resource_record_sets(batch)
|
|
64
68
|
return
|
|
65
69
|
rescue Exception => e
|
|
66
70
|
Log.info "Failed to update alias target, trying again in 20 seconds."
|
|
@@ -72,11 +76,15 @@ module CfDeployer
|
|
|
72
76
|
end
|
|
73
77
|
|
|
74
78
|
def get_hosted_zone(zone_name)
|
|
75
|
-
|
|
79
|
+
zone_name = trailing_dot(zone_name.downcase)
|
|
80
|
+
@aws_route53.list_hosted_zones_by_name(dns_name: zone_name).hosted_zones.find { |z| z.name == zone_name }
|
|
76
81
|
end
|
|
77
|
-
|
|
82
|
+
|
|
78
83
|
def get_record_set(hosted_zone, target_host_name)
|
|
79
|
-
|
|
84
|
+
record_name = trailing_dot(target_host_name.downcase)
|
|
85
|
+
@aws_route53.list_resource_record_sets(hosted_zone_id: hosted_zone.id, start_record_name: record_name)
|
|
86
|
+
.resource_record_sets
|
|
87
|
+
.find { |r| r.name == record_name }
|
|
80
88
|
end
|
|
81
89
|
|
|
82
90
|
def trailing_dot(text)
|
data/lib/cf_deployer/logger.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module CfDeployer
|
|
2
2
|
class Log
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
FORMATTER = proc do |severity, time, progname, message|
|
|
4
|
+
"#{time.strftime('%y-%m-%d %H:%M:%S')} [#{severity}] (#{progname}) #{message}\n"
|
|
5
|
+
end
|
|
5
6
|
|
|
6
7
|
def self.debug(message)
|
|
7
8
|
log.debug message
|
|
@@ -16,22 +17,20 @@ module CfDeployer
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def self.log
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@log
|
|
24
|
-
@log.level = Log4r::INFO
|
|
25
|
-
@log
|
|
20
|
+
@log ||= ::Logger.new($stdout, progname: 'cf_deployer', level: ::Logger::INFO, formatter: FORMATTER)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.log=(logger)
|
|
24
|
+
@log = logger
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
def self.level=(trace_level)
|
|
29
28
|
trace_level ||= 'info'
|
|
30
29
|
case trace_level.downcase
|
|
31
30
|
when 'debug'
|
|
32
|
-
log.level =
|
|
31
|
+
log.level = ::Logger::DEBUG
|
|
33
32
|
else
|
|
34
|
-
log.level =
|
|
33
|
+
log.level = ::Logger::INFO
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
end
|