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.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +25 -35
  3. data/elbas.gemspec +15 -12
  4. data/lib/elbas.rb +15 -9
  5. data/lib/elbas/aws/ami.rb +71 -0
  6. data/lib/elbas/aws/autoscale_group.rb +43 -0
  7. data/lib/elbas/aws/base.rb +39 -0
  8. data/lib/elbas/aws/instance.rb +24 -0
  9. data/lib/elbas/aws/instance_collection.rb +36 -0
  10. data/lib/elbas/aws/launch_template.rb +32 -0
  11. data/lib/elbas/aws/snapshot.rb +21 -0
  12. data/lib/elbas/aws/taggable.rb +18 -0
  13. data/lib/elbas/capistrano.rb +14 -12
  14. data/lib/elbas/errors/no_launch_template.rb +6 -0
  15. data/lib/elbas/logger.rb +10 -2
  16. data/lib/elbas/retryable.rb +34 -9
  17. data/lib/elbas/tasks/elbas.rake +19 -10
  18. data/lib/elbas/version.rb +1 -1
  19. data/spec/aws/ami_spec.rb +140 -0
  20. data/spec/aws/autoscale_group_spec.rb +53 -0
  21. data/spec/aws/instance_collection_spec.rb +28 -0
  22. data/spec/aws/instance_spec.rb +30 -0
  23. data/spec/aws/launch_template_spec.rb +52 -0
  24. data/spec/aws/taggable_spec.rb +53 -0
  25. data/spec/spec_helper.rb +14 -24
  26. data/spec/support/stubs/CreateLaunchTemplateVersion.200.xml +16 -0
  27. data/spec/support/stubs/DescribeAutoScalingGroups.200.xml +60 -0
  28. data/spec/support/stubs/DescribeImages.200.xml +36 -1
  29. data/spec/support/stubs/DescribeInstances.200.xml +109 -2
  30. metadata +60 -21
  31. data/lib/elbas/ami.rb +0 -56
  32. data/lib/elbas/aws/autoscaling.rb +0 -21
  33. data/lib/elbas/aws/credentials.rb +0 -20
  34. data/lib/elbas/aws/ec2.rb +0 -13
  35. data/lib/elbas/aws_resource.rb +0 -36
  36. data/lib/elbas/launch_configuration.rb +0 -64
  37. data/lib/elbas/taggable.rb +0 -9
  38. data/spec/ami_spec.rb +0 -10
  39. data/spec/elbas_spec.rb +0 -69
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ba77ed5f65f5066df79abab27f088a338a0f1f46
4
- data.tar.gz: 3fcf047a084ec476facacedf96dc41882b2ba90c
2
+ SHA256:
3
+ metadata.gz: 001b685faad190688be4ec49c511781a58129cd94800172575d0a8d739755837
4
+ data.tar.gz: a7fa9684df82682256c2629bc294260d3aa6b6426663b17534613c26263bb782
5
5
  SHA512:
6
- metadata.gz: 8602effc5044cbeaae866c39805db27e737471b015157f6a44b07a47e70be987624486c63d0919b8a428fde6c1f79666b9417e683fd6bbfa8264884f5316a17d
7
- data.tar.gz: f3b212fc1e38e2015d7884e464d2679f9a271c4166b8c650743e75063a26789907e09d0e6df59156bf078107af49fabfc2a12358cd89a97f162532360ecabfd8
6
+ metadata.gz: 3a5b2534c8f036a8f3044beb836d2c33b0bf74f092b28fc3f71ce7e129b3c586f6cad1594e41f84ed68d93de4f0c4270b34ef530e11aa8b1583e1af8a9ed947d
7
+ data.tar.gz: d9a71090a1a53139e2cbf80c3b8fff0a27e91f28640f649793d3f48c35d5d581e5e0a828bb90999cb9cabbc3703b738b515ce1e4774dee286ca4b9a6c5b62e36
data/README.md CHANGED
@@ -1,60 +1,50 @@
1
- # ELBAS (Elastic Load Balancer & AutoScaling)
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
- ELBAS was written to ease the deployment of Rails applications to AWS AutoScale groups. ELBAS will:
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
- - Attach the AMI with the new code to a new AWS Launch Configuration
8
- - Update your AutoScale group to use the new launch configuration
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 this statement to your Capfile:
19
+ Add to Capfile:
19
20
 
20
21
  `require 'elbas/capistrano'`
21
22
 
22
23
  ## Configuration
23
24
 
24
- Below are the Capistrano configuration options with their defaults:
25
+ Setup AWS credentials:
25
26
 
26
27
  ```ruby
27
- set :aws_access_key_id, ENV['AWS_ACCESS_KEY_ID']
28
- set :aws_secret_access_key, ENV['AWS_SECRET_ACCESS_KEY']
29
- set :aws_region, ENV['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 `deploy/production.rb` (or
41
- whichever environment you're deploying to). Provide the name of your AutoScale group instead of a
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 'production', user: 'apps', roles: [:app, :web, :db]
40
+ autoscale 'my-autoscale-group', user: 'apps', roles: [:app, :web, :db]
46
41
  ```
47
42
 
48
- That's it! Run `cap production deploy`. ELBAS will print the following log statements during your
49
- deployment:
43
+ Run `cap production deploy`.
50
44
 
51
- ```
52
- "ELBAS: Adding server: ec2-XX-XX-XX-XXX.compute-1.amazonaws.com"
53
- "ELBAS: Creating EC2 AMI from i-123abcd"
54
- "ELBAS: Created AMI: ami-123456"
55
- "ELBAS: Creating an EC2 Launch Configuration for AMI: ami-123456"
56
- "ELBAS: Created Launch Configuration: elbas-lc-ENVIRONMENT-UNIX_TIMESTAMP"
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.
@@ -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 = "elbas"
7
+ spec.name = 'elbas'
8
8
  spec.version = Elbas::VERSION
9
- spec.authors = ["Logan Serman"]
10
- spec.email = ["logan.serman@metova.com"]
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 = "#{spec.summary} Deploys to all instances in a group, creates a fresh AMI post-deploy, and attaches the AMI to your AutoScale Group."
13
- spec.homepage = "http://github.com/metova/elbas"
14
- spec.license = "MIT"
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 = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
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"
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
@@ -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/aws/credentials'
10
- require 'elbas/aws/autoscaling'
11
- require 'elbas/aws/ec2'
12
- require 'elbas/aws_resource'
13
- require 'elbas/ami'
14
- require 'elbas/launch_configuration'
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