elbas 0.27.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +25 -35
- data/elbas.gemspec +15 -12
- data/lib/elbas.rb +15 -9
- data/lib/elbas/aws/ami.rb +71 -0
- data/lib/elbas/aws/autoscale_group.rb +43 -0
- data/lib/elbas/aws/base.rb +39 -0
- data/lib/elbas/aws/instance.rb +24 -0
- data/lib/elbas/aws/instance_collection.rb +36 -0
- data/lib/elbas/aws/launch_template.rb +32 -0
- data/lib/elbas/aws/snapshot.rb +21 -0
- data/lib/elbas/aws/taggable.rb +18 -0
- data/lib/elbas/capistrano.rb +14 -12
- data/lib/elbas/errors/no_launch_template.rb +6 -0
- data/lib/elbas/logger.rb +10 -2
- data/lib/elbas/retryable.rb +34 -9
- data/lib/elbas/tasks/elbas.rake +19 -10
- data/lib/elbas/version.rb +1 -1
- data/spec/aws/ami_spec.rb +140 -0
- data/spec/aws/autoscale_group_spec.rb +53 -0
- data/spec/aws/instance_collection_spec.rb +28 -0
- data/spec/aws/instance_spec.rb +30 -0
- data/spec/aws/launch_template_spec.rb +52 -0
- data/spec/aws/taggable_spec.rb +53 -0
- data/spec/spec_helper.rb +14 -24
- data/spec/support/stubs/CreateLaunchTemplateVersion.200.xml +16 -0
- data/spec/support/stubs/DescribeAutoScalingGroups.200.xml +60 -0
- data/spec/support/stubs/DescribeImages.200.xml +36 -1
- data/spec/support/stubs/DescribeInstances.200.xml +109 -2
- metadata +60 -21
- data/lib/elbas/ami.rb +0 -56
- data/lib/elbas/aws/autoscaling.rb +0 -21
- data/lib/elbas/aws/credentials.rb +0 -20
- data/lib/elbas/aws/ec2.rb +0 -13
- data/lib/elbas/aws_resource.rb +0 -36
- data/lib/elbas/launch_configuration.rb +0 -64
- data/lib/elbas/taggable.rb +0 -9
- data/spec/ami_spec.rb +0 -10
- data/spec/elbas_spec.rb +0 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 001b685faad190688be4ec49c511781a58129cd94800172575d0a8d739755837
|
4
|
+
data.tar.gz: a7fa9684df82682256c2629bc294260d3aa6b6426663b17534613c26263bb782
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a5b2534c8f036a8f3044beb836d2c33b0bf74f092b28fc3f71ce7e129b3c586f6cad1594e41f84ed68d93de4f0c4270b34ef530e11aa8b1583e1af8a9ed947d
|
7
|
+
data.tar.gz: d9a71090a1a53139e2cbf80c3b8fff0a27e91f28640f649793d3f48c35d5d581e5e0a828bb90999cb9cabbc3703b738b515ce1e4774dee286ca4b9a6c5b62e36
|
data/README.md
CHANGED
@@ -1,60 +1,50 @@
|
|
1
|
-
|
1
|
+
*Versions < 3 of ELBAS are no longer being maintained. I will only be maintaining the current feature-set which relies on Launch Templates and AWS SDK v3.*
|
2
2
|
|
3
|
-
|
3
|
+
# Capistrano ELBAS (Elastic Load Balancer & AutoScaling)
|
4
|
+
|
5
|
+
ELBAS was written to ease the deployment of Rails applications to AWS AutoScale
|
6
|
+
groups. During your Capistrano deployment, ELBAS will:
|
4
7
|
|
5
8
|
- Deploy your code to each running instance connected to a given AutoScale group
|
6
9
|
- After deployment, create an AMI from one of the running instances
|
7
|
-
-
|
8
|
-
-
|
9
|
-
- Delete any old AMIs created by ELBAS
|
10
|
-
- Delete any old launch configurations created by ELBAS
|
11
|
-
|
12
|
-
This ensures that your current and future servers will be running the newly deployed code.
|
10
|
+
- Update the AutoScale group's launch template with the AMI ID
|
11
|
+
- Delete any outdated AMIs created by previous ELBAS deployments
|
13
12
|
|
14
13
|
## Installation
|
15
14
|
|
15
|
+
Add to Gemfile, then `bundle`:
|
16
|
+
|
16
17
|
`gem 'elbas'`
|
17
18
|
|
18
|
-
Add
|
19
|
+
Add to Capfile:
|
19
20
|
|
20
21
|
`require 'elbas/capistrano'`
|
21
22
|
|
22
23
|
## Configuration
|
23
24
|
|
24
|
-
|
25
|
+
Setup AWS credentials:
|
25
26
|
|
26
27
|
```ruby
|
27
|
-
set :
|
28
|
-
set :
|
29
|
-
set :aws_region,
|
30
|
-
|
31
|
-
set :aws_no_reboot_on_create_ami, true
|
32
|
-
set :aws_autoscale_instance_size, 'm1.small'
|
33
|
-
|
34
|
-
set :aws_launch_configuration_detailed_instance_monitoring, true
|
35
|
-
set :aws_launch_configuration_associate_public_ip, true
|
28
|
+
set :aws_access_key, ENV['AWS_ACCESS_KEY_ID']
|
29
|
+
set :aws_secret_key, ENV['AWS_SECRET_ACCESS_KEY']
|
30
|
+
set :aws_region, ENV['AWS_REGION']
|
36
31
|
```
|
37
32
|
|
38
33
|
## Usage
|
39
34
|
|
40
|
-
Instead of using Capistrano's `server` method, use `autoscale` instead in
|
41
|
-
|
42
|
-
hostname:
|
35
|
+
Instead of using Capistrano's `server` method, use `autoscale` instead in
|
36
|
+
`deploy/<environment>.rb` (replace <environment> with your environment). Provide
|
37
|
+
the name of your AutoScale group instead of a hostname:
|
43
38
|
|
44
39
|
```ruby
|
45
|
-
autoscale '
|
40
|
+
autoscale 'my-autoscale-group', user: 'apps', roles: [:app, :web, :db]
|
46
41
|
```
|
47
42
|
|
48
|
-
|
49
|
-
deployment:
|
43
|
+
Run `cap production deploy`.
|
50
44
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
"ELBAS: Attaching Launch Configuration to AutoScale Group"
|
58
|
-
"ELBAS: Deleting old launch configuration: elbas-lc-production-123456"
|
59
|
-
"ELBAS: Deleting old image: ami-999999"
|
60
|
-
```
|
45
|
+
**As of version 3, your AWS setup must use launch templates as opposed to launch
|
46
|
+
configurations.** This allows ELBAS to simply create a new launch template version
|
47
|
+
with the new AMI ID after a deployment. It no longer needs to update your
|
48
|
+
AutoScale group or mess around with network settings, instance sizes, etc., as
|
49
|
+
that information is all contained within the launch template. Failure to use a
|
50
|
+
launch template will result in a `Elbas::Errors::NoLaunchTemplate` error.
|
data/elbas.gemspec
CHANGED
@@ -4,26 +4,29 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'elbas/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'elbas'
|
8
8
|
spec.version = Elbas::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Logan Serman']
|
10
|
+
spec.email = ['loganserman@gmail.com']
|
11
11
|
spec.summary = 'Capistrano plugin for deploying to AWS AutoScale Groups.'
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = 'https://github.com/lserman/capistrano-elbas'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'webmock'
|
25
|
+
spec.add_development_dependency 'webmock-rspec-helper'
|
26
|
+
|
27
|
+
spec.add_dependency 'aws-sdk-autoscaling', '~> 1'
|
28
|
+
spec.add_dependency 'aws-sdk-ec2', '~> 1'
|
25
29
|
|
26
|
-
spec.add_dependency 'aws-sdk-v1', '~> 1'
|
27
30
|
spec.add_dependency 'capistrano', '> 3.0.0'
|
28
31
|
spec.add_dependency 'activesupport', '>= 4.0.0'
|
29
32
|
end
|
data/lib/elbas.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
|
-
require 'aws-sdk-v1'
|
2
1
|
require 'capistrano/all'
|
3
2
|
require 'active_support/concern'
|
4
3
|
|
4
|
+
require 'aws-sdk-autoscaling'
|
5
|
+
require 'aws-sdk-ec2'
|
6
|
+
|
5
7
|
require 'elbas/version'
|
6
|
-
require 'elbas/retryable'
|
7
|
-
require 'elbas/taggable'
|
8
8
|
require 'elbas/logger'
|
9
|
-
require 'elbas/
|
10
|
-
|
11
|
-
require 'elbas/
|
12
|
-
|
13
|
-
require 'elbas/
|
14
|
-
require 'elbas/
|
9
|
+
require 'elbas/retryable'
|
10
|
+
|
11
|
+
require 'elbas/errors/no_launch_template'
|
12
|
+
|
13
|
+
require 'elbas/aws/base'
|
14
|
+
require 'elbas/aws/taggable'
|
15
|
+
require 'elbas/aws/instance_collection'
|
16
|
+
require 'elbas/aws/instance'
|
17
|
+
require 'elbas/aws/autoscale_group'
|
18
|
+
require 'elbas/aws/launch_template'
|
19
|
+
require 'elbas/aws/ami'
|
20
|
+
require 'elbas/aws/snapshot'
|
15
21
|
|
16
22
|
module Elbas
|
17
23
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Elbas
|
2
|
+
module AWS
|
3
|
+
class AMI < Base
|
4
|
+
include Taggable
|
5
|
+
|
6
|
+
DEPLOY_ID_TAG = 'ELBAS-Deploy-id'.freeze
|
7
|
+
DEPLOY_GROUP_TAG = 'ELBAS-Deploy-group'.freeze
|
8
|
+
|
9
|
+
attr_reader :id, :snapshots
|
10
|
+
|
11
|
+
def initialize(id, snapshots = [])
|
12
|
+
@id = id
|
13
|
+
@aws_counterpart = ::Aws::EC2::Image.new id
|
14
|
+
|
15
|
+
@snapshots = snapshots.map do |snapshot|
|
16
|
+
Elbas::AWS::Snapshot.new snapshot&.ebs&.snapshot_id
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def deploy_id
|
21
|
+
tags[DEPLOY_ID_TAG]
|
22
|
+
end
|
23
|
+
|
24
|
+
def deploy_group
|
25
|
+
tags[DEPLOY_GROUP_TAG]
|
26
|
+
end
|
27
|
+
|
28
|
+
def ancestors
|
29
|
+
aws_amis_in_deploy_group.select { |aws_ami|
|
30
|
+
deploy_id_from_aws_tags(aws_ami.tags) != deploy_id
|
31
|
+
}.map { |aws_ami|
|
32
|
+
self.class.new aws_ami.image_id, aws_ami.block_device_mappings
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete
|
37
|
+
aws_client.deregister_image image_id: id
|
38
|
+
snapshots.each(&:delete)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.create(instance, no_reboot: true)
|
42
|
+
ami = instance.aws_counterpart.create_image({
|
43
|
+
name: "ELBAS-ami-#{Time.now.to_i}",
|
44
|
+
instance_id: instance.id,
|
45
|
+
no_reboot: no_reboot
|
46
|
+
})
|
47
|
+
|
48
|
+
new ami.id
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def aws_namespace
|
53
|
+
::Aws::EC2
|
54
|
+
end
|
55
|
+
|
56
|
+
def aws_amis_in_deploy_group
|
57
|
+
aws_client.describe_images({
|
58
|
+
owners: ['self'],
|
59
|
+
filters: [{
|
60
|
+
name: "tag:#{DEPLOY_GROUP_TAG}",
|
61
|
+
values: [deploy_group],
|
62
|
+
}]
|
63
|
+
}).images
|
64
|
+
end
|
65
|
+
|
66
|
+
def deploy_id_from_aws_tags(tags)
|
67
|
+
tags.detect { |tag| tag.key == DEPLOY_ID_TAG }&.value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Elbas
|
2
|
+
module AWS
|
3
|
+
class AutoscaleGroup < Base
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@aws_counterpart = query_autoscale_group_by_name(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def instance_ids
|
12
|
+
aws_counterpart.instances.map(&:instance_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def instances
|
16
|
+
InstanceCollection.new instance_ids
|
17
|
+
end
|
18
|
+
|
19
|
+
def launch_template
|
20
|
+
raise Elbas::Errors::NoLaunchTemplate unless aws_counterpart.launch_template
|
21
|
+
|
22
|
+
LaunchTemplate.new(
|
23
|
+
aws_counterpart.launch_template.launch_template_id,
|
24
|
+
aws_counterpart.launch_template.launch_template_name,
|
25
|
+
aws_counterpart.launch_template.version,
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def aws_namespace
|
31
|
+
::Aws::AutoScaling
|
32
|
+
end
|
33
|
+
|
34
|
+
def query_autoscale_group_by_name(name)
|
35
|
+
aws_client
|
36
|
+
.describe_auto_scaling_groups(auto_scaling_group_names: [name])
|
37
|
+
.auto_scaling_groups
|
38
|
+
.first
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Elbas
|
2
|
+
module AWS
|
3
|
+
class Base
|
4
|
+
include Capistrano::DSL
|
5
|
+
|
6
|
+
attr_reader :aws_counterpart
|
7
|
+
|
8
|
+
def aws_client(namespace = aws_namespace)
|
9
|
+
@aws_client ||= begin
|
10
|
+
options = {}
|
11
|
+
options[:region] = aws_region if aws_region
|
12
|
+
options[:credentials] = aws_credentials if aws_credentials.set?
|
13
|
+
|
14
|
+
namespace::Client.new options
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def aws_credentials
|
19
|
+
fetch :aws_credentials, ::Aws::Credentials.new(aws_access_key, aws_secret_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
def aws_access_key
|
23
|
+
fetch :aws_access_key
|
24
|
+
end
|
25
|
+
|
26
|
+
def aws_secret_key
|
27
|
+
fetch :aws_secret_key
|
28
|
+
end
|
29
|
+
|
30
|
+
def aws_region
|
31
|
+
fetch :aws_region
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.aws_client(namespace = aws_namespace)
|
35
|
+
Base.new.aws_client namespace
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Elbas
|
2
|
+
module AWS
|
3
|
+
class Instance
|
4
|
+
STATE_RUNNING = 16.freeze
|
5
|
+
|
6
|
+
attr_reader :aws_counterpart, :id, :state
|
7
|
+
|
8
|
+
def initialize(id, public_dns, state)
|
9
|
+
@id = id
|
10
|
+
@public_dns = public_dns
|
11
|
+
@state = state
|
12
|
+
@aws_counterpart = ::Aws::EC2::Instance.new id
|
13
|
+
end
|
14
|
+
|
15
|
+
def hostname
|
16
|
+
@public_dns
|
17
|
+
end
|
18
|
+
|
19
|
+
def running?
|
20
|
+
state == STATE_RUNNING
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Elbas
|
2
|
+
module AWS
|
3
|
+
class InstanceCollection < Base
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :instances
|
7
|
+
|
8
|
+
def initialize(ids)
|
9
|
+
@ids = ids
|
10
|
+
@instances = query_instances_by_ids(ids).map do |i|
|
11
|
+
Instance.new(i.instance_id, i.public_dns_name, i.state.code)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def running
|
16
|
+
select(&:running?)
|
17
|
+
end
|
18
|
+
|
19
|
+
def each(&block)
|
20
|
+
instances.each(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def aws_namespace
|
25
|
+
::Aws::EC2
|
26
|
+
end
|
27
|
+
|
28
|
+
def query_instances_by_ids(ids)
|
29
|
+
aws_client
|
30
|
+
.describe_instances(instance_ids: @ids)
|
31
|
+
.reservations[0]
|
32
|
+
.instances
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Elbas
|
2
|
+
module AWS
|
3
|
+
class LaunchTemplate < Base
|
4
|
+
attr_reader :id, :name, :version
|
5
|
+
|
6
|
+
def initialize(id, name, version)
|
7
|
+
@id = id
|
8
|
+
@name = name
|
9
|
+
@version = version
|
10
|
+
end
|
11
|
+
|
12
|
+
def update(ami)
|
13
|
+
latest = aws_client.create_launch_template_version({
|
14
|
+
launch_template_data: { image_id: ami.id },
|
15
|
+
launch_template_id: self.id,
|
16
|
+
source_version: self.version
|
17
|
+
}).launch_template_version
|
18
|
+
|
19
|
+
self.class.new(
|
20
|
+
latest&.launch_template_id,
|
21
|
+
latest&.launch_template_name,
|
22
|
+
latest&.version_number
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def aws_namespace
|
28
|
+
::Aws::EC2
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Elbas
|
2
|
+
module AWS
|
3
|
+
class Snapshot < Base
|
4
|
+
attr_reader :id
|
5
|
+
|
6
|
+
def initialize(id)
|
7
|
+
@id = id
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete
|
11
|
+
return unless id
|
12
|
+
aws_client.delete_snapshot snapshot_id: id
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def aws_namespace
|
17
|
+
::Aws::EC2
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|