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
@@ -1,56 +0,0 @@
1
- module Elbas
2
- class AMI < AWSResource
3
- include Taggable
4
-
5
- def self.create(&block)
6
- ami = new
7
- ami.cleanup do
8
- ami.save
9
- ami.tag 'Deployed-with' => 'ELBAS'
10
- ami.tag 'ELBAS-Deploy-group' => ami.autoscale_group_name
11
- yield ami
12
- end
13
- end
14
-
15
- def save
16
- info "Creating EC2 AMI from EC2 Instance: #{base_ec2_instance.id}"
17
- @aws_counterpart = ec2.images.create \
18
- name: name,
19
- instance_id: base_ec2_instance.id,
20
- no_reboot: fetch(:aws_no_reboot_on_create_ami, true)
21
- end
22
-
23
- def destroy(images = [])
24
- images.each do |i|
25
- info "Deleting old AMI: #{i.id}"
26
- snapshots = snapshots_attached_to i
27
- i.delete
28
- delete_snapshots snapshots
29
- end
30
- end
31
-
32
- private
33
- def name
34
- timestamp "#{environment}-AMI"
35
- end
36
-
37
- def trash
38
- ec2.images.with_owner('self').to_a.select do |ami|
39
- deployed_with_elbas? ami
40
- end
41
- end
42
-
43
- def snapshots_attached_to(image)
44
- image.block_device_mappings.map do |_, values|
45
- ec2.snapshots[values[:snapshot_id]]
46
- end
47
- end
48
-
49
- def delete_snapshots(snapshots)
50
- snapshots.each do |snapshot|
51
- info "Deleting snapshot: #{snapshot.id}"
52
- snapshot.delete if snapshot.exists?
53
- end
54
- end
55
- end
56
- end
@@ -1,21 +0,0 @@
1
- module Elbas
2
- module AWS
3
- module AutoScaling
4
- extend ActiveSupport::Concern
5
- include Elbas::AWS::Credentials
6
- include Capistrano::DSL
7
-
8
- def autoscaling
9
- @_autoscaling ||= ::AWS::AutoScaling.new(credentials)
10
- end
11
-
12
- def autoscale_group
13
- @_autoscale_group ||= autoscaling.groups[autoscale_group_name]
14
- end
15
-
16
- def autoscale_group_name
17
- fetch(:aws_autoscale_group)
18
- end
19
- end
20
- end
21
- end
@@ -1,20 +0,0 @@
1
- module Elbas
2
- module AWS
3
- module Credentials
4
- extend ActiveSupport::Concern
5
- include Capistrano::DSL
6
-
7
- def credentials
8
- @_credentials ||= begin
9
- _credentials = {
10
- access_key_id: fetch(:aws_access_key_id, ENV['AWS_ACCESS_KEY_ID']),
11
- secret_access_key: fetch(:aws_secret_access_key, ENV['AWS_SECRET_ACCESS_KEY'])
12
- }
13
-
14
- _credentials.merge! region: fetch(:aws_region) if fetch(:aws_region)
15
- _credentials
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,13 +0,0 @@
1
- module Elbas
2
- module AWS
3
- module EC2
4
- extend ActiveSupport::Concern
5
- include Elbas::AWS::Credentials
6
- include Capistrano::DSL
7
-
8
- def ec2
9
- @_ec2 ||= ::AWS::EC2.new(credentials)
10
- end
11
- end
12
- end
13
- end
@@ -1,36 +0,0 @@
1
- module Elbas
2
- class AWSResource
3
- include Capistrano::DSL
4
- include Elbas::AWS::AutoScaling
5
- include Elbas::AWS::EC2
6
- include Elbas::Retryable
7
- include Logger
8
-
9
- attr_reader :aws_counterpart
10
-
11
- def cleanup(&block)
12
- items = trash || []
13
- yield
14
- destroy items
15
- self
16
- end
17
-
18
- private
19
- def base_ec2_instance
20
- @_base_ec2_instance ||= autoscale_group.ec2_instances.filter('instance-state-name', 'running').first
21
- end
22
-
23
- def environment
24
- fetch(:rails_env, 'production')
25
- end
26
-
27
- def timestamp(str)
28
- "#{str}-#{Time.now.to_i}"
29
- end
30
-
31
- def deployed_with_elbas?(resource)
32
- resource.tags['Deployed-with'] == 'ELBAS' &&
33
- resource.tags['ELBAS-Deploy-group'] == autoscale_group_name
34
- end
35
- end
36
- end
@@ -1,64 +0,0 @@
1
- module Elbas
2
- class LaunchConfiguration < AWSResource
3
-
4
- def self.create(ami, &block)
5
- lc = new
6
- lc.cleanup do
7
- lc.save(ami)
8
- yield lc
9
- end
10
- end
11
-
12
- def save(ami)
13
- info "Creating an EC2 Launch Configuration for AMI: #{ami.aws_counterpart.id}"
14
- with_retry do
15
- @aws_counterpart = autoscaling.launch_configurations.create(name, ami.aws_counterpart.id, instance_size, create_options)
16
- end
17
- end
18
-
19
- def attach_to_autoscale_group!
20
- info 'Attaching Launch Configuration to AutoScale Group'
21
- autoscale_group.update(launch_configuration: aws_counterpart)
22
- end
23
-
24
- def destroy(launch_configurations = [])
25
- launch_configurations.each do |lc|
26
- info "Deleting old launch configuration: #{lc.name}"
27
- lc.delete
28
- end
29
- end
30
-
31
- private
32
- def name
33
- timestamp "ELBAS-#{environment}-#{autoscale_group_name}-LC"
34
- end
35
-
36
- def instance_size
37
- fetch(:aws_autoscale_instance_size, 'm1.small')
38
- end
39
-
40
- def create_options
41
- options = {
42
- security_groups: base_ec2_instance.security_groups.to_a,
43
- detailed_instance_monitoring: fetch(:aws_launch_configuration_detailed_instance_monitoring, true),
44
- associate_public_ip_address: fetch(:aws_launch_configuration_associate_public_ip, true)
45
- }
46
-
47
- if user_data = fetch(:aws_launch_configuration_user_data, nil)
48
- options.merge user_data: user_data
49
- end
50
-
51
- options
52
- end
53
-
54
- def deployed_with_elbas?(lc)
55
- lc.name.include? "ELBAS-#{environment}-#{autoscale_group_name}-LC"
56
- end
57
-
58
- def trash
59
- autoscaling.launch_configurations.to_a.select do |lc|
60
- deployed_with_elbas? lc
61
- end
62
- end
63
- end
64
- end
@@ -1,9 +0,0 @@
1
- module Elbas
2
- module Taggable
3
- def tag(tags = {})
4
- with_retry do
5
- tags.each { |k, v| aws_counterpart.tags[k] = v }
6
- end
7
- end
8
- end
9
- end
@@ -1,10 +0,0 @@
1
- describe Elbas::AMI do
2
- describe '#tag' do
3
- subject { Elbas::AMI.new }
4
-
5
- it 'retries the tag 3 times' do
6
- expect(subject).to receive(:aws_counterpart).exactly(3).times { fail RuntimeError }
7
- subject.tag 'Test' => true
8
- end
9
- end
10
- end
@@ -1,69 +0,0 @@
1
- describe 'ELBAS' do
2
-
3
- before do
4
- allow_any_instance_of(Elbas::AWSResource).to receive(:autoscale_group_name) { 'production' }
5
- webmock :get, /security-credentials/ => 'security-credentials.200.json'
6
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'DescribeImages.200.xml') { Hash[body: /Action=DescribeImages/] }
7
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'DescribeTags.200.xml') { Hash[body: /Action=DescribeTags/] }
8
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'DescribeInstances.200.xml') { Hash[body: /Action=DescribeInstances/] }
9
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'CreateImage.200.xml') { Hash[body: /Action=CreateImage/] }
10
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'DeregisterImage.200.xml') { Hash[body: /Action=DeregisterImage/] }
11
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'CreateTags.200.xml') { Hash[body: /Action=CreateTags/] }
12
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'DescribeSnapshots.200.xml') { Hash[body: /Action=DescribeSnapshots/] }
13
- webmock(:post, %r{ec2.(.*).amazonaws.com\/\z} => 'DeleteSnapshot.200.xml') { Hash[body: /Action=DeleteSnapshot/] }
14
- webmock(:post, %r{autoscaling.(.*).amazonaws.com\/\z} => 'DescribeLaunchConfigurations.200.xml') { Hash[body: /Action=DescribeLaunchConfigurations/] }
15
- webmock(:post, %r{autoscaling.(.*).amazonaws.com\/\z} => 'CreateLaunchConfiguration.200.xml') { Hash[body: /Action=CreateLaunchConfiguration/] }
16
- webmock(:post, %r{autoscaling.(.*).amazonaws.com\/\z} => 'DeleteLaunchConfiguration.200.xml') { Hash[body: /Action=DeleteLaunchConfiguration/] }
17
- webmock(:post, %r{autoscaling.(.*).amazonaws.com\/\z} => 'UpdateAutoScalingGroup.200.xml') { Hash[body: /Action=UpdateAutoScalingGroup/] }
18
- end
19
-
20
- let!(:ami) do
21
- _ami = nil
22
- Elbas::AMI.create { |ami| _ami = ami }
23
- _ami
24
- end
25
-
26
- describe 'AMI creation & cleanup' do
27
- it 'creates a new AMI on Amazon' do
28
- expect(ami.aws_counterpart.id).to eq 'ami-4fa54026'
29
- end
30
-
31
- it 'deletes any AMIs tagged with Deployed-with=ELBAS' do
32
- expect(WebMock).to have_requested(:post, /ec2.(.*).amazonaws.com\/\z/).with(body: /Action=DeregisterImage&ImageId=ami-1a2b3c4d/)
33
- end
34
-
35
- it 'deletes snapshots that are associated with the AMI' do
36
- expect(WebMock).to have_requested(:post, /ec2.(.*).amazonaws.com\/\z/).with(body: /Action=DeleteSnapshot&SnapshotId=snap-1a2b3c4d/)
37
- end
38
-
39
- it 'tags the new AMI with Deployed-with=ELBAS' do
40
- expect(WebMock).to have_requested(:post, /ec2.(.*).amazonaws.com\/\z/).with(body: /Action=CreateTags&ResourceId.1=ami-4fa54026&Tag.1.Key=Deployed-with&Tag.1.Value=ELBAS/)
41
- end
42
-
43
- it 'tags the new AMI with ELBAS-Deploy-group=<autoscale group name>' do
44
- expect(WebMock).to have_requested(:post, /ec2.(.*).amazonaws.com\/\z/).with(body: /Action=CreateTags&ResourceId.1=ami-4fa54026&Tag.1.Key=ELBAS-Deploy-group&Tag.1.Value=production/)
45
- end
46
- end
47
-
48
- describe 'Launch configuration creation & cleanup' do
49
- let!(:launch_configuration) do
50
- _lc = nil
51
- Elbas::LaunchConfiguration.create(ami) { |lc| _lc = lc }
52
- _lc
53
- end
54
-
55
- it 'creates a new Launch Configuration on AWS' do
56
- expect(WebMock).to have_requested(:post, /autoscaling.(.*).amazonaws.com\/\z/).
57
- with(body: /Action=CreateLaunchConfiguration&AssociatePublicIpAddress=true&ImageId=ami-4fa54026&InstanceMonitoring.Enabled=true&InstanceType=m1.small&LaunchConfigurationName=ELBAS-production/)
58
- end
59
-
60
- it 'deletes any LCs with name =~ ELBAS-production' do
61
- expect(WebMock).to have_requested(:post, /autoscaling.(.*).amazonaws.com\/\z/).with(body: /Action=DeleteLaunchConfiguration&LaunchConfigurationName=ELBAS-production-production-LC-1234567890/)
62
- end
63
-
64
- it 'attaches the LC to the autoscale group' do
65
- launch_configuration.attach_to_autoscale_group!
66
- expect(WebMock).to have_requested(:post, /autoscaling.(.*).amazonaws.com\/\z/).with(body: /Action=UpdateAutoScalingGroup&AutoScalingGroupName=production&LaunchConfigurationName=ELBAS-production-production-LC-\d{10,}/)
67
- end
68
- end
69
- end