aerosol 1.7.0 → 1.9.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: bcb31cfba1584cdce2dfa12015e9fa229b0cc399
4
- data.tar.gz: 827738fea59874eb7bf54961a7d3ff9327d9c6be
2
+ SHA256:
3
+ metadata.gz: 6d4984e8f3a4ad49c8fcf43ee3f8a46afc0a8ff5a03bc17e0e2a7c5ae51d5ff9
4
+ data.tar.gz: 6a02a6faf80b6a0bc3ccb317df748dac95d994f4a0fb146724ed844240727451
5
5
  SHA512:
6
- metadata.gz: 8a1de687a21e221f2a221a132cc570254f6637fe2c51ba1686baa460dc6eff70a1b454034bc907d05d2bfc253eb663a1640351d3f4b49dcd9be8eac3abe84cd6
7
- data.tar.gz: 4a566f867c011016eefabd719c27d289eacf737edbc553f88e1a80359d4272c0c80d8ce8bb3d561328a9b648eab62a0f9e1fb061c4b240dcaedf3cef05e0175b
6
+ metadata.gz: ddb94dbd6f44e8cb419d6051fc0e5855a8bc5bb9ce45de11e35da6600831d0a8f8928990d0429aa6f415b80a91381c4c198a5c1d8a2a341f7ea47992b2417178
7
+ data.tar.gz: 5f2488d1ec6336cb2a4a3aea9dadd7ab030b788bfb72fffb8723369857e875ccba832d88ecba070098c750d208de76c74ac5ee9c80cb4879f497e9f829435e0a
data/.travis.yml CHANGED
@@ -2,10 +2,9 @@ sudo: false
2
2
  dist: trusty
3
3
  language: ruby
4
4
  rvm:
5
- - 1.9.3
6
- - 2.0
7
5
  - 2.1
8
6
  - 2.2
7
+ - 2.7
9
8
  cache: bundler
10
- before_install: gem install bundler
9
+ before_install: gem install bundler -v 1.17.3
11
10
  script: CI=true bundle exec rake
data/aerosol.gemspec CHANGED
@@ -18,7 +18,10 @@ Gem::Specification.new do |gem|
18
18
  gem.add_dependency 'activerecord', '>= 3.2.0'
19
19
  gem.add_dependency 'clamp', '~> 0.6'
20
20
  gem.add_dependency 'excon'
21
- gem.add_dependency 'aws-sdk', '~> 2.0'
21
+ gem.add_dependency 'aws-sdk-core', '~> 3'
22
+ gem.add_dependency 'aws-sdk-s3', '~> 1'
23
+ gem.add_dependency 'aws-sdk-ec2', '~> 1'
24
+ gem.add_dependency 'aws-sdk-autoscaling', '~> 1'
22
25
  gem.add_dependency 'minigit', '~> 0.0.4'
23
26
  gem.add_dependency 'net-ssh'
24
27
  gem.add_dependency 'net-ssh-gateway'
data/lib/aerosol.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  require 'yaml'
2
- require 'aws-sdk'
2
+ require 'aws-sdk-autoscaling'
3
+ require 'aws-sdk-core'
4
+ require 'aws-sdk-s3'
5
+ require 'aws-sdk-ec2'
3
6
  require 'dockly/util'
4
7
  require 'base64'
5
8
 
@@ -10,6 +13,7 @@ module Aerosol
10
13
  require 'aerosol/util'
11
14
  require 'aerosol/aws_model'
12
15
  require 'aerosol/launch_configuration'
16
+ require 'aerosol/launch_template'
13
17
  require 'aerosol/auto_scaling'
14
18
  require 'aerosol/instance'
15
19
  require 'aerosol/connection'
@@ -58,6 +62,7 @@ module Aerosol
58
62
  :auto_scalings => Aerosol::AutoScaling.instances,
59
63
  :deploys => Aerosol::Deploy.instances,
60
64
  :launch_configurations => Aerosol::LaunchConfiguration.instances,
65
+ :launch_templates => Aerosol::LaunchTemplate.instances,
61
66
  :sshs => Aerosol::Connection.instances,
62
67
  :envs => Aerosol::Env.instances
63
68
  }
@@ -67,6 +72,7 @@ module Aerosol
67
72
  :auto_scaling => Aerosol::AutoScaling,
68
73
  :deploy => Aerosol::Deploy,
69
74
  :launch_configuration => Aerosol::LaunchConfiguration,
75
+ :launch_template => Aerosol::LaunchTemplate,
70
76
  :ssh => Aerosol::Connection,
71
77
  :env => Aerosol::Env
72
78
  }.each do |method, klass|
@@ -79,15 +85,15 @@ module Aerosol
79
85
  end
80
86
  end
81
87
 
82
- [:auto_scalings, :deploys, :launch_configurations, :sshs, :envs].each do |method|
88
+ [:auto_scalings, :deploys, :launch_configurations, :launch_templates, :sshs, :envs].each do |method|
83
89
  define_method(method) do
84
90
  inst[method]
85
91
  end
86
92
  end
87
93
 
88
94
  module_function :inst, :load_inst, :setup, :load_file, :load_file=,
89
- :auto_scaling, :launch_configuration, :deploy, :ssh, :git_sha,
90
- :auto_scalings, :launch_configurations, :deploys, :sshs,
95
+ :auto_scaling, :launch_configuration, :launch_template, :deploy, :ssh, :git_sha,
96
+ :auto_scalings, :launch_configurations, :launch_templates, :deploys, :sshs,
91
97
  :namespace, :env, :envs, :region
92
98
  end
93
99
 
@@ -7,6 +7,12 @@ class Aerosol::AutoScaling
7
7
  :desired_capacity, :health_check_grace_period, :health_check_type, :load_balancer_names,
8
8
  :placement_group, :tags, :created_time, :vpc_zone_identifier, :target_group_arns
9
9
  aws_class_attribute :launch_configuration, Aerosol::LaunchConfiguration
10
+ aws_class_attribute(
11
+ :launch_template,
12
+ Aerosol::LaunchTemplate,
13
+ proc { |argument| argument.fetch(:launch_template, {})[:launch_template_name] }
14
+ )
15
+
10
16
  primary_key :auto_scaling_group_name
11
17
 
12
18
  def initialize(options={}, &block)
@@ -38,21 +44,30 @@ class Aerosol::AutoScaling
38
44
  end
39
45
 
40
46
  def create!
41
- ensure_present! :launch_configuration, :max_size, :min_size
47
+ ensure_present! :max_size, :min_size
42
48
  raise 'availability_zones or vpc_zone_identifier must be set' if [availability_zones, vpc_zone_identifier].none?
49
+ raise 'launch_configuration or launch_template must be set' unless launch_details
43
50
 
44
51
  info "creating auto scaling group"
45
- launch_configuration.create
52
+ launch_details = create_launch_details
53
+
46
54
  info self.inspect
47
55
 
48
56
  conn.create_auto_scaling_group({
49
57
  auto_scaling_group_name: auto_scaling_group_name,
50
58
  availability_zones: [*availability_zones],
51
- launch_configuration_name: launch_configuration.launch_configuration_name,
52
59
  max_size: max_size,
53
60
  min_size: min_size
54
- }.merge(create_options))
55
- sleep 10
61
+ }
62
+ .merge(create_options)
63
+ .merge(launch_details)
64
+ )
65
+
66
+ conn.wait_until(:group_in_service, auto_scaling_group_names: [auto_scaling_group_name]) do |waiter|
67
+ waiter.before_wait do |attempt_count, _response|
68
+ info "Wait count #{attempt_count} of #{waiter.max_attempts} for #{auto_scaling_group_name} to be in service"
69
+ end
70
+ end
56
71
  end
57
72
 
58
73
  def destroy!
@@ -60,9 +75,9 @@ class Aerosol::AutoScaling
60
75
  conn.delete_auto_scaling_group(auto_scaling_group_name: auto_scaling_group_name, force_delete: true)
61
76
  begin
62
77
  (0..2).each { break if deleting?; sleep 1 }
63
- launch_configuration.destroy
78
+ launch_details.destroy
64
79
  rescue => ex
65
- info "Launch Config: #{launch_configuration} for #{auto_scaling_group_name} was not deleted."
80
+ info "Launch Details: #{launch_details} for #{auto_scaling_group_name} were not deleted."
66
81
  info ex.message
67
82
  end
68
83
  end
@@ -78,7 +93,11 @@ class Aerosol::AutoScaling
78
93
  def all_instances
79
94
  conn.describe_auto_scaling_groups(auto_scaling_group_names: [*auto_scaling_group_name])
80
95
  .auto_scaling_groups.first
81
- .instances.map { |instance| Aerosol::Instance.from_hash(instance) }
96
+ .instances.map { |instance| Aerosol::Instance.from_hash(instance.to_hash) }
97
+ end
98
+
99
+ def launch_details
100
+ launch_configuration || launch_template
82
101
  end
83
102
 
84
103
  def tag(val)
@@ -147,6 +166,22 @@ private
147
166
  Aerosol::AWS.auto_scaling
148
167
  end
149
168
 
169
+ def create_launch_details
170
+ if launch_configuration
171
+ launch_configuration.create
172
+ { launch_configuration_name: launch_configuration.launch_configuration_name }
173
+ elsif launch_template
174
+ launch_template.create
175
+ {
176
+ launch_template:
177
+ {
178
+ launch_template_name: launch_template.launch_template_name,
179
+ version: '$Latest'
180
+ }
181
+ }
182
+ end
183
+ end
184
+
150
185
  def create_options
151
186
  {
152
187
  default_cooldown: default_cooldown,
@@ -47,13 +47,15 @@ module Aerosol::AWSModel
47
47
  aws_attributes.merge(attrs)
48
48
  end
49
49
 
50
- def aws_class_attribute(name, klass)
50
+ def aws_class_attribute(name, klass, primary_key_proc = nil)
51
51
  unless klass.ancestors.include?(Aerosol::AWSModel) || (klass == self)
52
52
  raise '.aws_class_attribute requires a Aerosol::AWSModel that is not the current class.'
53
53
  end
54
54
 
55
+ primary_key_proc ||= proc { |hash| hash[klass.primary_key] }
56
+
55
57
  dsl_class_attribute(name, klass)
56
- aws_class_attributes.merge!({ name => klass })
58
+ aws_class_attributes.merge!({ name => [klass, primary_key_proc] })
57
59
  end
58
60
 
59
61
  def exists?(key)
@@ -67,9 +69,9 @@ module Aerosol::AWSModel
67
69
 
68
70
  def from_hash(hash)
69
71
  raise 'To use .from_hash, you must specify a primary_key' if primary_key.nil?
70
- refs = Hash[aws_class_attributes.map do |name, klass|
72
+ refs = Hash[aws_class_attributes.map do |name, (klass, primary_key_proc)|
71
73
  value = klass.instances.values.find do |inst|
72
- inst.send(klass.primary_key).to_s == hash[klass.primary_key].to_s unless inst.send(klass.primary_key).nil?
74
+ inst.send(klass.primary_key).to_s == primary_key_proc.call(hash).to_s unless inst.send(klass.primary_key).nil?
73
75
  end
74
76
  [name, value]
75
77
  end].reject { |k, v| v.nil? }
@@ -5,6 +5,11 @@ class Aerosol::Instance
5
5
 
6
6
  aws_attribute :availability_zone, :health_status, :instance_id, :lifecycle_state
7
7
  aws_class_attribute :launch_configuration, Aerosol::LaunchConfiguration
8
+ aws_class_attribute(
9
+ :launch_template,
10
+ Aerosol::LaunchTemplate,
11
+ proc { |argument| argument.fetch(:launch_template, {})[:launch_template_name] }
12
+ )
8
13
  primary_key :instance_id
9
14
 
10
15
  def live?
@@ -5,7 +5,7 @@ class Aerosol::LaunchConfiguration
5
5
  logger_prefix '[aerosol launch_configuration]'
6
6
  aws_attribute :launch_configuration_name, :image_id, :instance_type, :security_groups, :user_data,
7
7
  :iam_instance_profile, :kernel_id, :key_name, :spot_price, :created_time,
8
- :associate_public_ip_address, :block_device_mappings
8
+ :associate_public_ip_address, :block_device_mappings, :ebs_optimized
9
9
  dsl_attribute :meta_data
10
10
 
11
11
  primary_key :launch_configuration_name
@@ -89,7 +89,8 @@ class Aerosol::LaunchConfiguration
89
89
  "key_name" => "#{key_name}", \
90
90
  "spot_price" => "#{spot_price}", \
91
91
  "created_time" => "#{created_time}", \
92
- "block_device_mappings" => #{block_device_mappings}" \
92
+ "block_device_mappings" => #{block_device_mappings}", \
93
+ "ebs_optimized" => #{ebs_optimized} \
93
94
  }}
94
95
  end
95
96
 
@@ -109,7 +110,8 @@ private
109
110
  spot_price: spot_price,
110
111
  user_data: corrected_user_data,
111
112
  associate_public_ip_address: associate_public_ip_address,
112
- block_device_mappings: block_device_mappings
113
+ block_device_mappings: block_device_mappings,
114
+ ebs_optimized: ebs_optimized
113
115
  }.reject { |k, v| v.nil? }
114
116
  end
115
117
 
@@ -0,0 +1,127 @@
1
+ class Aerosol::LaunchTemplate
2
+ include Aerosol::AWSModel
3
+ include Dockly::Util::Logger::Mixin
4
+
5
+ logger_prefix '[aerosol launch_template]'
6
+ aws_attribute :launch_template_name, :launch_template_id, :latest_version_number,
7
+ :image_id, :instance_type, :security_groups, :user_data,
8
+ :iam_instance_profile, :kernel_id, :key_name, :spot_price, :created_time,
9
+ :network_interfaces, :block_device_mappings, :ebs_optimized
10
+ dsl_attribute :meta_data
11
+
12
+ primary_key :launch_template_name
13
+ default_value(:security_groups) { [] }
14
+ default_value(:meta_data) { {} }
15
+
16
+ def launch_template_name(arg = nil)
17
+ if arg
18
+ raise "You cannot set the launch_template_name directly" unless from_aws
19
+ @launch_template_name = arg
20
+ else
21
+ @launch_template_name || default_identifier
22
+ end
23
+ end
24
+
25
+ def security_group(group)
26
+ security_groups << group
27
+ end
28
+
29
+ def create!
30
+ ensure_present! :image_id, :instance_type
31
+
32
+ info "creating launch template"
33
+ conn.create_launch_template(
34
+ launch_template_name: launch_template_name,
35
+ launch_template_data: {
36
+ image_id: image_id,
37
+ instance_type: instance_type,
38
+ monitoring: {
39
+ enabled: true
40
+ },
41
+ }.merge(create_options)
42
+ )
43
+
44
+ info self.inspect
45
+ end
46
+
47
+ def destroy!
48
+ info self.to_s
49
+ raise StandardError.new('No launch_template_name found') unless launch_template_name
50
+ conn.delete_launch_template(launch_template_name: launch_template_name)
51
+ end
52
+
53
+ def all_instances
54
+ Aerosol::Instance.all.select { |instance|
55
+ !instance.launch_template.nil? &&
56
+ (instance.launch_template.launch_template_name == launch_template_name)
57
+ }.each(&:description)
58
+ end
59
+
60
+ def self.request_all_for_token(next_token)
61
+ options = next_token.nil? ? {} : { next_token: next_token }
62
+ Aerosol::AWS.compute.describe_launch_templates(options)
63
+ end
64
+
65
+ def self.request_all
66
+ next_token = nil
67
+ lts = []
68
+
69
+ begin
70
+ new_lts = request_all_for_token(next_token)
71
+ lts.concat(new_lts.launch_templates)
72
+ next_token = new_lts.next_token
73
+ end until next_token.nil?
74
+ lts
75
+ end
76
+
77
+ def to_s
78
+ %{Aerosol::LaunchTemplate { \
79
+ "launch_template_name" => "#{launch_template_name}", \
80
+ "launch_template_id" => "#{launch_template_id}", \
81
+ "latest_version_number" => "#{latest_version_number}", \
82
+ "image_id" => "#{image_id}", \
83
+ "instance_type" => "#{instance_type}", \
84
+ "security_group_ids" => #{security_groups.to_s}, \
85
+ "user_data" => "#{user_data}", \
86
+ "iam_instance_profile" => "#{iam_instance_profile}", \
87
+ "kernel_id" => "#{kernel_id}", \
88
+ "key_name" => "#{key_name}", \
89
+ "spot_price" => "#{spot_price}", \
90
+ "created_time" => "#{created_time}", \
91
+ "block_device_mappings" => #{block_device_mappings}", \
92
+ "ebs_optimized" => #{ebs_optimized} \
93
+ }}
94
+ end
95
+
96
+ def corrected_user_data
97
+ realized_user_data = user_data.is_a?(Proc) ? user_data.call : user_data
98
+
99
+ Base64.encode64(Aerosol::Util.strip_heredoc(realized_user_data || ''))
100
+ end
101
+
102
+ private
103
+ def create_options
104
+ instance_market_options = {
105
+ market_type: 'spot',
106
+ spot_options: {
107
+ max_price: spot_price
108
+ }
109
+ } if spot_price
110
+
111
+ {
112
+ iam_instance_profile: { name: iam_instance_profile },
113
+ kernel_id: kernel_id,
114
+ key_name: key_name,
115
+ security_group_ids: security_groups,
116
+ instance_market_options: instance_market_options,
117
+ user_data: corrected_user_data,
118
+ network_interfaces: network_interfaces,
119
+ block_device_mappings: block_device_mappings,
120
+ ebs_optimized: ebs_optimized,
121
+ }.reject { |k, v| v.nil? }
122
+ end
123
+
124
+ def conn
125
+ Aerosol::AWS.compute
126
+ end
127
+ end
@@ -229,7 +229,7 @@ class Aerosol::Runner
229
229
 
230
230
  def old_instances
231
231
  require_deploy!
232
- old_auto_scaling_groups.map(&:launch_configuration).compact.map(&:all_instances).flatten.compact
232
+ old_auto_scaling_groups.map(&:launch_details).compact.map(&:all_instances).flatten.compact
233
233
  end
234
234
 
235
235
  def old_auto_scaling_groups
@@ -243,6 +243,7 @@ class Aerosol::Runner
243
243
  def select_auto_scaling_groups(&block)
244
244
  require_deploy!
245
245
  Aerosol::LaunchConfiguration.all # load all of the launch configurations first
246
+ Aerosol::LaunchTemplate.all
246
247
  Aerosol::AutoScaling.all.select { |asg|
247
248
  (asg.tags['Deploy'].to_s == auto_scaling.tags['Deploy']) &&
248
249
  (block.nil? ? true : block.call(asg))
@@ -251,11 +252,13 @@ class Aerosol::Runner
251
252
 
252
253
  def new_instances
253
254
  require_deploy!
254
- while launch_configuration.all_instances.length < auto_scaling.min_size
255
+
256
+ while launch_details.all_instances.length < auto_scaling.min_size
255
257
  info "Waiting for instances to come up"
256
258
  sleep 10
257
259
  end
258
- launch_configuration.all_instances
260
+
261
+ launch_details.all_instances
259
262
  end
260
263
 
261
264
  def with_deploy(name)
@@ -280,7 +283,7 @@ class Aerosol::Runner
280
283
  :live_check, :db_config_path, :instance_live_grace_period,
281
284
  :app_port, :continue_if_stop_app_fails, :stop_app_retries,
282
285
  :is_alive?, :log_files, :tail_logs, :to => :deploy
283
- delegate :launch_configuration, :to => :auto_scaling
286
+ delegate :launch_details, :to => :auto_scaling
284
287
 
285
288
  private
286
289
 
@@ -1,5 +1,5 @@
1
1
  # Copyright Swipely, Inc. All rights reserved.
2
2
 
3
3
  module Aerosol
4
- VERSION = '1.7.0'
4
+ VERSION = '1.9.2'
5
5
  end
@@ -0,0 +1,402 @@
1
+ require 'spec_helper'
2
+
3
+ describe Aerosol::LaunchTemplate do
4
+ subject do
5
+ described_class.new do
6
+ name :my_launch_template
7
+ image_id 'ami-123'
8
+ instance_type 'super-cool-instance-type'
9
+ user_data <<-END_OF_STRING
10
+ #!/bin/bash
11
+ rm -rf /
12
+ END_OF_STRING
13
+ end
14
+ end
15
+ before { subject.stub(:sleep) }
16
+
17
+ describe "#launch_template_name" do
18
+ context "with no namespace set" do
19
+ let(:identifier) { "my_launch_template-#{Aerosol::Util.git_sha}" }
20
+ it "returns a normal identifier" do
21
+ expect(subject.launch_template_name).to eq(identifier)
22
+ end
23
+ end
24
+
25
+ context "with a namespace set" do
26
+ let(:namespace) { "test" }
27
+ let(:identifier) { "#{namespace}-my_launch_template-#{Aerosol::Util.git_sha}" }
28
+
29
+ before { Aerosol.namespace namespace }
30
+ after { Aerosol.instance_variable_set(:"@namespace", nil) }
31
+
32
+ it "returns a namespaced identifier" do
33
+ expect(subject.launch_template_name).to eq(identifier)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#security_group' do
39
+ subject { described_class.new!(:name => 'conf-conf-conf') }
40
+
41
+ it 'adds the argument to the list of security groups' do
42
+ expect { subject.security_group 'my group' }
43
+ .to change { subject.security_groups.length }
44
+ .by 1
45
+ end
46
+
47
+ it 'does not the default security group' do
48
+ expect { subject.security_group 'other test' }
49
+ .to_not change { described_class.default_values[:security_groups] }
50
+ end
51
+ end
52
+
53
+ describe '#create!' do
54
+ context 'when some required fields are nil' do
55
+ before { subject.instance_variable_set(:@image_id, nil) }
56
+
57
+ it 'raises an error' do
58
+ expect { subject.create! }.to raise_error
59
+ end
60
+ end
61
+
62
+ context 'when everything is present' do
63
+ context 'and the launch template already exists' do
64
+ it 'raises an error' do
65
+ Aerosol::AWS.compute.stub_responses(
66
+ :create_launch_template,
67
+ Aws::EC2::Errors::AlreadyExists
68
+ )
69
+ expect { subject.create! }.to raise_error
70
+ end
71
+ end
72
+
73
+ context 'and the launch template does not exist yet' do
74
+ after { subject.destroy! rescue nil }
75
+
76
+ it 'creates the launch template group' do
77
+ Aerosol::AWS.compute.stub_responses(:create_launch_template, [])
78
+ expect { subject.create! }.to_not raise_error
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#destroy!' do
85
+ context 'when the launch_template_name is nil' do
86
+
87
+ it 'raises an error' do
88
+ allow(subject).to receive(:launch_template_name).and_return(nil)
89
+ Aerosol::AWS.compute.stub_responses(:delete_launch_template, [])
90
+ expect { subject.destroy! }.to raise_error
91
+ end
92
+ end
93
+
94
+ context 'when the launch_template_name is present' do
95
+ context 'but the launch template does not exist' do
96
+ it 'raises an error' do
97
+ Aerosol::AWS.compute.stub_responses(
98
+ :delete_launch_template,
99
+ Aws::EC2::Errors::ValidationError
100
+ )
101
+ expect { subject.destroy! }.to raise_error
102
+ end
103
+ end
104
+
105
+ context 'and the launch template exists' do
106
+ it 'deletes the launch template' do
107
+ Aerosol::AWS.compute.stub_responses(:delete_launch_template, [])
108
+ expect { subject.destroy! }.to_not raise_error
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ describe '#create' do
115
+ context 'when the launch_template_name is nil' do
116
+ subject do
117
+ described_class.new! do
118
+ name :random_test_name
119
+ image_id 'test-ami-who-even-cares-really'
120
+ instance_type 'm1.large'
121
+ end
122
+ end
123
+
124
+ it 'raises an error' do
125
+ allow(subject).to receive(:launch_template_name).and_return(nil)
126
+ Aerosol::AWS.compute.stub_responses(:create_launch_template, [])
127
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
128
+ launch_templates: [], next_token: nil
129
+ })
130
+ expect { subject.create }.to raise_error
131
+ end
132
+ end
133
+
134
+ context 'when the launch_template_name is present' do
135
+ subject do
136
+ described_class.new! do
137
+ name :random_test_name_2
138
+ image_id 'test-ami-who-even-cares-really'
139
+ instance_type 'm1.large'
140
+ end
141
+ end
142
+
143
+ context 'but the launch template already exists' do
144
+ it 'does not call #create!' do
145
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
146
+ launch_templates: [{
147
+ launch_template_name: subject.launch_template_name,
148
+ }],
149
+ next_token: nil
150
+ })
151
+ expect(subject).to_not receive(:create!)
152
+ subject.create
153
+ end
154
+ end
155
+
156
+ context 'and the launch template does not yet exist' do
157
+ it 'creates it' do
158
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
159
+ launch_templates: [],
160
+ next_token: nil
161
+ })
162
+ subject.should_receive(:create!)
163
+ subject.create
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ describe '#destroy' do
170
+ subject do
171
+ described_class.new! do
172
+ name :random_test_name_3
173
+ image_id 'awesome-ami'
174
+ instance_type 'm1.large'
175
+ end
176
+ end
177
+
178
+ context 'when the launch_template_name is nil' do
179
+ it 'raises an error' do
180
+ allow(subject).to receive(:launch_template_name).and_return(nil)
181
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
182
+ launch_templates: [],
183
+ next_token: nil
184
+ })
185
+ expect { subject.create }.to raise_error(ArgumentError)
186
+ end
187
+ end
188
+
189
+ context 'when the launch_template_name is present' do
190
+ context 'and the launch template already exists' do
191
+
192
+ it 'calls #destroy!' do
193
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
194
+ launch_templates: [{
195
+ launch_template_name: subject.launch_template_name
196
+ }],
197
+ next_token: nil
198
+ })
199
+ subject.should_receive(:destroy!)
200
+ subject.destroy
201
+ end
202
+ end
203
+
204
+ context 'but the launch template does not yet exist' do
205
+ it 'does not call #destroy!' do
206
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
207
+ launch_templates: [],
208
+ next_token: nil
209
+ })
210
+ subject.should_not_receive(:destroy!)
211
+ subject.destroy
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ describe '.exists?' do
218
+ subject { described_class }
219
+ let(:instance) do
220
+ subject.new! do
221
+ name :exists_test_name
222
+ image_id 'ami123'
223
+ instance_type 'm1.large'
224
+ stub(:sleep)
225
+ end
226
+ end
227
+
228
+ context 'when the argument exists' do
229
+ it 'returns true' do
230
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
231
+ launch_templates: [{
232
+ launch_template_name: instance.launch_template_name,
233
+ }],
234
+ next_token: nil
235
+ })
236
+ subject.exists?(instance.launch_template_name).should be_true
237
+ end
238
+ end
239
+
240
+ context 'when the argument does not exist' do
241
+ let(:instance) { described_class.new! }
242
+
243
+ it 'returns false' do
244
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
245
+ launch_templates: [],
246
+ next_token: nil
247
+ })
248
+ subject.exists?(instance.launch_template_name).should be_false
249
+ end
250
+ end
251
+ end
252
+
253
+ describe '.request_all' do
254
+ describe 'repeats until no NextToken' do
255
+ it 'should include both autoscaling groups lists' do
256
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, [
257
+ {
258
+ launch_templates: [
259
+ { launch_template_name: '1' },
260
+ { launch_template_name: '4' }
261
+ ],
262
+ next_token: 'yes'
263
+ },
264
+ {
265
+ launch_templates: [
266
+ { launch_template_name: '2' },
267
+ { launch_template_name: '3' }
268
+ ],
269
+ next_token: nil
270
+ }
271
+ ])
272
+ expect(Aerosol::LaunchTemplate.request_all.map(&:launch_template_name)).to eq(['1','4','2','3'])
273
+ end
274
+ end
275
+ end
276
+
277
+ describe '.all' do
278
+ subject { described_class }
279
+
280
+ context 'when there are no launch templates' do
281
+ before do
282
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, [
283
+ { launch_templates: [], next_token: nil }
284
+ ])
285
+ end
286
+ its(:all) { should be_empty }
287
+ end
288
+
289
+ context 'when there are launch templates' do
290
+ let(:insts) {
291
+ [
292
+ { launch_template_name: 'test' },
293
+ { launch_template_name: 'test2' }
294
+ ]
295
+ }
296
+
297
+ it 'returns each of them' do
298
+ Aerosol::AWS.compute.stub_responses(:describe_launch_templates, {
299
+ launch_templates: insts,
300
+ next_token: nil
301
+ })
302
+ subject.all.map(&:launch_template_name).should == %w[test test2]
303
+ end
304
+ end
305
+ end
306
+
307
+ describe '.from_hash' do
308
+ context 'when the launch template has not been initialized' do
309
+ subject { described_class.from_hash(hash) }
310
+ let(:hash) do
311
+ {
312
+ launch_template_name: '~test-launch-config~',
313
+ image_id: 'ami-123',
314
+ instance_type: 'm1.large',
315
+ security_groups: [],
316
+ user_data: 'echo hi',
317
+ iam_instance_profile: nil,
318
+ kernel_id: 'kernel-id',
319
+ key_name: 'key-name',
320
+ spot_price: '0.04',
321
+ }
322
+ end
323
+
324
+ it 'creates a new launch template with the specified values' do
325
+ subject.launch_template_name.should == '~test-launch-config~'
326
+ subject.image_id.should == 'ami-123'
327
+ subject.instance_type.should == 'm1.large'
328
+ subject.security_groups.should be_empty
329
+ subject.user_data.should == 'echo hi'
330
+ subject.iam_instance_profile.should be_nil
331
+ subject.kernel_id.should == 'kernel-id'
332
+ subject.spot_price.should == '0.04'
333
+ subject.from_aws = true
334
+ end
335
+
336
+ it 'generates a name' do
337
+ subject.name.to_s.should start_with 'LaunchTemplate_'
338
+ end
339
+ end
340
+
341
+ context 'when the launch template has already been initialized' do
342
+ let(:old_hash) do
343
+ {
344
+ launch_template_name: 'this-aws-id-abc-123',
345
+ image_id: 'ami-456',
346
+ }
347
+ end
348
+ let(:new_hash) { old_hash.merge(instance_type: 'm1.large') }
349
+ let!(:existing) { described_class.from_hash(old_hash) }
350
+ let(:new) { described_class.from_hash(new_hash) }
351
+
352
+ it 'makes a new instance' do
353
+ expect { new }.to change { described_class.instances.length }.by(1)
354
+ new.launch_template_name.should == 'this-aws-id-abc-123'
355
+ new.image_id.should == 'ami-456'
356
+ end
357
+ end
358
+ end
359
+
360
+ describe '#corrected_user_data' do
361
+ let(:encoded_user_data_string) { Base64.encode64('test') }
362
+
363
+ context 'when the user_data is a String' do
364
+ subject do
365
+ described_class.new do
366
+ name :corrected_user_data
367
+ user_data 'test'
368
+ end
369
+ end
370
+
371
+ it 'correctly encodes to base64' do
372
+ expect(subject.corrected_user_data).to eq(encoded_user_data_string)
373
+ end
374
+ end
375
+
376
+ context 'when the user_data is a Proc' do
377
+ subject do
378
+ described_class.new do
379
+ name :corrected_user_data_2
380
+ user_data { 'test' }
381
+ end
382
+ end
383
+
384
+ it 'correctly encodes to base64' do
385
+ expect(subject.corrected_user_data).to eq(encoded_user_data_string)
386
+ end
387
+ end
388
+ end
389
+
390
+ describe '#meta_data' do
391
+ subject do
392
+ described_class.new do
393
+ name :my_launch_template
394
+ meta_data('Test' => '1')
395
+ end
396
+ end
397
+
398
+ it 'returns the hash' do
399
+ expect(subject.meta_data['Test']).to eq('1')
400
+ end
401
+ end
402
+ end
@@ -253,6 +253,57 @@ describe Aerosol::Runner do
253
253
  end
254
254
 
255
255
  describe '#new_instances' do
256
+ context 'With a launch template' do
257
+ let!(:lt) do
258
+ Aerosol::LaunchTemplate.new! do
259
+ name :lt
260
+ image_id 'fake-ami-how-scandalous'
261
+ instance_type 'm1.large'
262
+ end
263
+ end
264
+ let!(:asg_lt) do
265
+ Aerosol::AutoScaling.new! do
266
+ name :asg_lt
267
+ availability_zones 'us-east-1'
268
+ min_size 0
269
+ max_size 3
270
+ launch_template :lt
271
+ stub(:sleep)
272
+ end
273
+ end
274
+ let!(:instance1) do
275
+ Aerosol::Instance.from_hash(
276
+ {
277
+ instance_id: 'z0',
278
+ launch_template: { launch_template_name: lt.launch_template_name }
279
+ }
280
+ )
281
+ end
282
+ let!(:instance2) do
283
+ double(
284
+ :launch_template => double(:launch_template_name => 'lc7-8891022'),
285
+ :launch_configuration => nil
286
+ )
287
+ end
288
+ let!(:instance3) do
289
+ double(
290
+ :launch_template => nil,
291
+ :launch_configuration => double(:launch_configuration_name => 'lc0-8891022')
292
+ )
293
+ end
294
+
295
+ before do
296
+ Aerosol::Instance.stub(:all).and_return([instance1, instance2, instance3])
297
+ end
298
+
299
+ it 'returns each instance that is a member of the current launch template' do
300
+ deploy = Aerosol::Deploy.new!(name: :lt_deploy, auto_scaling: :asg_lt)
301
+ subject.with_deploy(:lt_deploy) do
302
+ subject.new_instances.should == [instance1]
303
+ end
304
+ end
305
+ end
306
+
256
307
  let!(:lc7) do
257
308
  Aerosol::LaunchConfiguration.new! do
258
309
  name :lc7
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aerosol
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Swipely, Inc.
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-06 00:00:00.000000000 Z
11
+ date: 2021-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -53,19 +53,61 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: aws-sdk
56
+ name: aws-sdk-core
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2.0'
61
+ version: '3'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2.0'
68
+ version: '3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: aws-sdk-s3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: aws-sdk-ec2
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: aws-sdk-autoscaling
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: minigit
71
113
  requirement: !ruby/object:Gem::Requirement
@@ -238,6 +280,7 @@ files:
238
280
  - lib/aerosol/env.rb
239
281
  - lib/aerosol/instance.rb
240
282
  - lib/aerosol/launch_configuration.rb
283
+ - lib/aerosol/launch_template.rb
241
284
  - lib/aerosol/rake_task.rb
242
285
  - lib/aerosol/runner.rb
243
286
  - lib/aerosol/util.rb
@@ -250,6 +293,7 @@ files:
250
293
  - spec/aerosol/env_spec.rb
251
294
  - spec/aerosol/instance_spec.rb
252
295
  - spec/aerosol/launch_configuration_spec.rb
296
+ - spec/aerosol/launch_template_spec.rb
253
297
  - spec/aerosol/rake_task_spec.rb
254
298
  - spec/aerosol/runner_spec.rb
255
299
  - spec/aerosol_spec.rb
@@ -259,7 +303,7 @@ homepage: https://github.com/swipely/aerosol
259
303
  licenses:
260
304
  - MIT
261
305
  metadata: {}
262
- post_install_message:
306
+ post_install_message:
263
307
  rdoc_options: []
264
308
  require_paths:
265
309
  - lib
@@ -274,9 +318,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
274
318
  - !ruby/object:Gem::Version
275
319
  version: '0'
276
320
  requirements: []
277
- rubyforge_project:
278
- rubygems_version: 2.4.6
279
- signing_key:
321
+ rubygems_version: 3.0.3
322
+ signing_key:
280
323
  specification_version: 4
281
324
  summary: Instance-based deploys made easy
282
325
  test_files:
@@ -288,9 +331,9 @@ test_files:
288
331
  - spec/aerosol/env_spec.rb
289
332
  - spec/aerosol/instance_spec.rb
290
333
  - spec/aerosol/launch_configuration_spec.rb
334
+ - spec/aerosol/launch_template_spec.rb
291
335
  - spec/aerosol/rake_task_spec.rb
292
336
  - spec/aerosol/runner_spec.rb
293
337
  - spec/aerosol_spec.rb
294
338
  - spec/spec_helper.rb
295
339
  - spec/support/vcr.rb
296
- has_rdoc: