aerosol 1.7.0.pre.1 → 1.9.1

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