elbas 0.27.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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