elbas 0.27.0 → 3.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/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
|