aerosol 1.7.0 → 1.9.2

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 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: