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 +5 -5
- data/.travis.yml +2 -3
- data/aerosol.gemspec +4 -1
- data/lib/aerosol.rb +10 -4
- data/lib/aerosol/auto_scaling.rb +43 -8
- data/lib/aerosol/aws_model.rb +6 -4
- data/lib/aerosol/instance.rb +5 -0
- data/lib/aerosol/launch_configuration.rb +5 -3
- data/lib/aerosol/launch_template.rb +127 -0
- data/lib/aerosol/runner.rb +7 -4
- data/lib/aerosol/version.rb +1 -1
- data/spec/aerosol/launch_template_spec.rb +402 -0
- data/spec/aerosol/runner_spec.rb +51 -0
- metadata +54 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6d4984e8f3a4ad49c8fcf43ee3f8a46afc0a8ff5a03bc17e0e2a7c5ae51d5ff9
|
4
|
+
data.tar.gz: 6a02a6faf80b6a0bc3ccb317df748dac95d994f4a0fb146724ed844240727451
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ddb94dbd6f44e8cb419d6051fc0e5855a8bc5bb9ce45de11e35da6600831d0a8f8928990d0429aa6f415b80a91381c4c198a5c1d8a2a341f7ea47992b2417178
|
7
|
+
data.tar.gz: 5f2488d1ec6336cb2a4a3aea9dadd7ab030b788bfb72fffb8723369857e875ccba832d88ecba070098c750d208de76c74ac5ee9c80cb4879f497e9f829435e0a
|
data/.travis.yml
CHANGED
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', '~>
|
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
|
|
data/lib/aerosol/auto_scaling.rb
CHANGED
@@ -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! :
|
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
|
-
|
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
|
-
}
|
55
|
-
|
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
|
-
|
78
|
+
launch_details.destroy
|
64
79
|
rescue => ex
|
65
|
-
info "Launch
|
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,
|
data/lib/aerosol/aws_model.rb
CHANGED
@@ -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
|
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? }
|
data/lib/aerosol/instance.rb
CHANGED
@@ -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
|
data/lib/aerosol/runner.rb
CHANGED
@@ -229,7 +229,7 @@ class Aerosol::Runner
|
|
229
229
|
|
230
230
|
def old_instances
|
231
231
|
require_deploy!
|
232
|
-
old_auto_scaling_groups.map(&:
|
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
|
-
|
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
|
-
|
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 :
|
286
|
+
delegate :launch_details, :to => :auto_scaling
|
284
287
|
|
285
288
|
private
|
286
289
|
|
data/lib/aerosol/version.rb
CHANGED
@@ -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
|
data/spec/aerosol/runner_spec.rb
CHANGED
@@ -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.
|
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:
|
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: '
|
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: '
|
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
|
-
|
278
|
-
|
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:
|