aerosol 1.7.0.pre.1 → 1.9.1

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: b8819f96c48ea7dd20ca4e8b8dd60f57438e8490
4
- data.tar.gz: 1101234f4a09e3e3ab6c037a1ff2d508c1ebf667
2
+ SHA256:
3
+ metadata.gz: 9494ad62c369aa73f67af3dca0dcc28d8dd29ef820e5bd75c05e30b4203bd950
4
+ data.tar.gz: a7be58f47b1d874751346371cc3e448bcea64f5fce4846fc2b2c4875788db5ee
5
5
  SHA512:
6
- metadata.gz: 6ca3ac081e15a0240242a2c9b9a72ffba1cf4ac7efd5e36287462f2ecf3ce8c22f1b131446525b177c3a0157256e9df7ff160e204e081276c14885907eb0fe08
7
- data.tar.gz: cad77cab038f356a8a061c0a2fd5a912dc1dd435d90a473f2209f92c314f0b4769700469b1dda450be2d84030ead7957ec32625554b5cb601778e8c2236ce036
6
+ metadata.gz: 805b520c8b7e883e58113b864732bbd841f01d56d3d1703da8e79982d940d590a47d0f2ed5458efa523bbb03c95d81a82eee5b031d81f946f92d7ff58b1f6024
7
+ data.tar.gz: 8b0390907ccbefef4e58d333f2e92da0a5489304380776fa00836fa9d578200c1623a489985aa1bff66e48e22cea84cdbcec2915ad984b1d364e0fcf31955b9d
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
5
  - 2.0
7
6
  - 2.1
8
7
  - 2.2
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,14 +18,17 @@ 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'
25
28
  gem.add_dependency 'dockly-util', '~> 0.1.0'
26
29
  gem.add_development_dependency 'cane'
27
30
  gem.add_development_dependency 'pry'
28
- gem.add_development_dependency 'rake'
31
+ gem.add_development_dependency 'rake', '~> 10.0'
29
32
  gem.add_development_dependency 'rspec', '< 3.0'
30
33
  gem.add_development_dependency 'webmock'
31
34
  gem.add_development_dependency 'vcr'
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
 
@@ -5,8 +5,9 @@ class Aerosol::AutoScaling
5
5
  logger_prefix '[aerosol auto_scaling]'
6
6
  aws_attribute :auto_scaling_group_name, :availability_zones, :min_size, :max_size, :default_cooldown,
7
7
  :desired_capacity, :health_check_grace_period, :health_check_type, :load_balancer_names,
8
- :placement_group, :tags, :created_time, :vpc_zone_identifier
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 :launch_template, Aerosol::LaunchTemplate
10
11
  primary_key :auto_scaling_group_name
11
12
 
12
13
  def initialize(options={}, &block)
@@ -38,21 +39,30 @@ class Aerosol::AutoScaling
38
39
  end
39
40
 
40
41
  def create!
41
- ensure_present! :launch_configuration, :max_size, :min_size
42
+ ensure_present! :max_size, :min_size
42
43
  raise 'availability_zones or vpc_zone_identifier must be set' if [availability_zones, vpc_zone_identifier].none?
44
+ raise 'launch_configuration or launch_template must be set' unless launch_details
43
45
 
44
46
  info "creating auto scaling group"
45
- launch_configuration.create
47
+ launch_details = create_launch_details
48
+
46
49
  info self.inspect
47
50
 
48
51
  conn.create_auto_scaling_group({
49
52
  auto_scaling_group_name: auto_scaling_group_name,
50
53
  availability_zones: [*availability_zones],
51
- launch_configuration_name: launch_configuration.launch_configuration_name,
52
54
  max_size: max_size,
53
55
  min_size: min_size
54
- }.merge(create_options))
55
- sleep 10
56
+ }
57
+ .merge(create_options)
58
+ .merge(launch_details)
59
+ )
60
+
61
+ conn.wait_until(:group_in_service, auto_scaling_group_names: [auto_scaling_group_name]) do |waiter|
62
+ waiter.before_wait do |attempt_count, _response|
63
+ info "Wait count #{attempt_count} of #{waiter.max_attempts} for #{auto_scaling_group_name} to be in service"
64
+ end
65
+ end
56
66
  end
57
67
 
58
68
  def destroy!
@@ -60,9 +70,9 @@ class Aerosol::AutoScaling
60
70
  conn.delete_auto_scaling_group(auto_scaling_group_name: auto_scaling_group_name, force_delete: true)
61
71
  begin
62
72
  (0..2).each { break if deleting?; sleep 1 }
63
- launch_configuration.destroy
73
+ launch_details.destroy
64
74
  rescue => ex
65
- info "Launch Config: #{launch_configuration} for #{auto_scaling_group_name} was not deleted."
75
+ info "Launch Details: #{launch_details} for #{auto_scaling_group_name} were not deleted."
66
76
  info ex.message
67
77
  end
68
78
  end
@@ -81,6 +91,10 @@ class Aerosol::AutoScaling
81
91
  .instances.map { |instance| Aerosol::Instance.from_hash(instance) }
82
92
  end
83
93
 
94
+ def launch_details
95
+ launch_configuration || launch_template
96
+ end
97
+
84
98
  def tag(val)
85
99
  tags.merge!(val)
86
100
  end
@@ -138,14 +152,37 @@ class Aerosol::AutoScaling
138
152
  "placement_group" => "#{placement_group}", \
139
153
  "tags" => #{tags.to_s}, \
140
154
  "created_time" => "#{created_time}" \
155
+ "target_group_arns" => "#{target_group_arns}" \
141
156
  }}
142
157
  end
143
158
 
159
+ def self.from_hash(hash)
160
+ instance = super(hash)
161
+ instance['launch_template'] = (hash[:launch_template][:launch_template_name]) if hash[:launch_template]
162
+ instance
163
+ end
164
+
144
165
  private
145
166
  def conn
146
167
  Aerosol::AWS.auto_scaling
147
168
  end
148
169
 
170
+ def create_launch_details
171
+ if launch_configuration
172
+ launch_configuration.create
173
+ { launch_configuration_name: launch_configuration.launch_configuration_name }
174
+ elsif launch_template
175
+ launch_template.create
176
+ {
177
+ launch_template:
178
+ {
179
+ launch_template_name: launch_template.launch_template_name,
180
+ version: '$Latest'
181
+ }
182
+ }
183
+ end
184
+ end
185
+
149
186
  def create_options
150
187
  {
151
188
  default_cooldown: default_cooldown,
@@ -155,7 +192,8 @@ private
155
192
  load_balancer_names: load_balancer_names,
156
193
  placement_group: placement_group,
157
194
  tags: tags_to_array,
158
- vpc_zone_identifier: vpc_zone_identifier
195
+ vpc_zone_identifier: vpc_zone_identifier,
196
+ target_group_arns: target_group_arns
159
197
  }.reject { |k, v| v.nil? }
160
198
  end
161
199
 
@@ -5,6 +5,7 @@ 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 :launch_template, Aerosol::LaunchTemplate
8
9
  primary_key :instance_id
9
10
 
10
11
  def live?
@@ -58,6 +59,12 @@ class Aerosol::Instance
58
59
  instances
59
60
  end
60
61
 
62
+ def self.from_hash(hash)
63
+ instance = super(hash)
64
+ instance['launch_template'] = (hash[:launch_template][:launch_template_name]) if hash[:launch_template]
65
+ instance
66
+ end
67
+
61
68
  private
62
69
  def describe!
63
70
  ensure_present! :instance_id
@@ -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,133 @@
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, :image_id, :instance_type, :security_groups, :user_data,
7
+ :iam_instance_profile, :kernel_id, :key_name, :spot_price, :created_time,
8
+ :network_interfaces, :block_device_mappings, :ebs_optimized
9
+ dsl_attribute :meta_data
10
+
11
+ primary_key :launch_template_name
12
+ default_value(:security_groups) { [] }
13
+ default_value(:meta_data) { {} }
14
+
15
+ def launch_template_name(arg = nil)
16
+ if arg
17
+ raise "You cannot set the launch_template_name directly" unless from_aws
18
+ @launch_template_name = arg
19
+ else
20
+ @launch_template_name || default_identifier
21
+ end
22
+ end
23
+
24
+ def exists?
25
+ info "launch_template: needed?: #{launch_template_name}: " +
26
+ "checking for launch template: #{launch_template_name}"
27
+ exists = super
28
+ info "launch template: needed?: #{launch_template_name}: " +
29
+ "#{exists ? 'found' : 'did not find'} launch template: #{launch_template_name}"
30
+ exists
31
+ end
32
+
33
+ def security_group(group)
34
+ security_groups << group
35
+ end
36
+
37
+ def create!
38
+ ensure_present! :image_id, :instance_type
39
+
40
+ info "creating launch template"
41
+ conn.create_launch_template(
42
+ launch_template_name: launch_template_name,
43
+ launch_template_data: {
44
+ image_id: image_id,
45
+ instance_type: instance_type,
46
+ monitoring: {
47
+ enabled: true
48
+ },
49
+ }.merge(create_options)
50
+ )
51
+ info self.inspect
52
+ end
53
+
54
+ def destroy!
55
+ info self.to_s
56
+ raise StandardError.new('No launch_template_name found') unless launch_template_name
57
+ conn.delete_launch_template(launch_template_name: launch_template_name)
58
+ end
59
+
60
+ def all_instances
61
+ Aerosol::Instance.all.select { |instance|
62
+ !instance.launch_template.nil? &&
63
+ (instance.launch_template == launch_template_name)
64
+ }.each(&:description)
65
+ end
66
+
67
+ def self.request_all_for_token(next_token)
68
+ options = next_token.nil? ? {} : { next_token: next_token }
69
+ Aerosol::AWS.compute.describe_launch_templates(options)
70
+ end
71
+
72
+ def self.request_all
73
+ next_token = nil
74
+ lts = []
75
+
76
+ begin
77
+ new_lts = request_all_for_token(next_token)
78
+ lts.concat(new_lts.launch_templates)
79
+ next_token = new_lts.next_token
80
+ end until next_token.nil?
81
+
82
+ lts
83
+ end
84
+
85
+ def to_s
86
+ %{Aerosol::LaunchTemplate { \
87
+ "launch_template_name" => "#{launch_template_name}", \
88
+ "image_id" => "#{image_id}", \
89
+ "instance_type" => "#{instance_type}", \
90
+ "security_group_ids" => #{security_groups.to_s}, \
91
+ "user_data" => "#{user_data}", \
92
+ "iam_instance_profile" => "#{iam_instance_profile}", \
93
+ "kernel_id" => "#{kernel_id}", \
94
+ "key_name" => "#{key_name}", \
95
+ "spot_price" => "#{spot_price}", \
96
+ "created_time" => "#{created_time}", \
97
+ "block_device_mappings" => #{block_device_mappings}", \
98
+ "ebs_optimized" => #{ebs_optimized} \
99
+ }}
100
+ end
101
+
102
+ def corrected_user_data
103
+ realized_user_data = user_data.is_a?(Proc) ? user_data.call : user_data
104
+
105
+ Base64.encode64(Aerosol::Util.strip_heredoc(realized_user_data || ''))
106
+ end
107
+
108
+ private
109
+ def create_options
110
+ instance_market_options = {
111
+ market_type: 'spot',
112
+ spot_options: {
113
+ max_price: spot_price
114
+ }
115
+ } if spot_price
116
+
117
+ {
118
+ iam_instance_profile: { name: iam_instance_profile },
119
+ kernel_id: kernel_id,
120
+ key_name: key_name,
121
+ security_group_ids: security_groups,
122
+ instance_market_options: instance_market_options,
123
+ user_data: corrected_user_data,
124
+ network_interfaces: network_interfaces,
125
+ block_device_mappings: block_device_mappings,
126
+ ebs_optimized: ebs_optimized,
127
+ }.reject { |k, v| v.nil? }
128
+ end
129
+
130
+ def conn
131
+ Aerosol::AWS.compute
132
+ end
133
+ 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.pre.1'
4
+ VERSION = '1.9.1'
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.pre.1
4
+ version: 1.9.1
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: 2016-04-29 00:00:00.000000000 Z
11
+ date: 2021-03-22 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
@@ -154,16 +196,16 @@ dependencies:
154
196
  name: rake
155
197
  requirement: !ruby/object:Gem::Requirement
156
198
  requirements:
157
- - - ">="
199
+ - - "~>"
158
200
  - !ruby/object:Gem::Version
159
- version: '0'
201
+ version: '10.0'
160
202
  type: :development
161
203
  prerelease: false
162
204
  version_requirements: !ruby/object:Gem::Requirement
163
205
  requirements:
164
- - - ">="
206
+ - - "~>"
165
207
  - !ruby/object:Gem::Version
166
- version: '0'
208
+ version: '10.0'
167
209
  - !ruby/object:Gem::Dependency
168
210
  name: rspec
169
211
  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
@@ -270,13 +314,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
270
314
  version: '0'
271
315
  required_rubygems_version: !ruby/object:Gem::Requirement
272
316
  requirements:
273
- - - ">"
317
+ - - ">="
274
318
  - !ruby/object:Gem::Version
275
- version: 1.3.1
319
+ version: '0'
276
320
  requirements: []
277
- rubyforge_project:
278
- rubygems_version: 2.4.5
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: