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 +5 -5
- data/.travis.yml +1 -2
- data/aerosol.gemspec +5 -2
- data/lib/aerosol.rb +10 -4
- data/lib/aerosol/auto_scaling.rb +47 -9
- data/lib/aerosol/instance.rb +7 -0
- data/lib/aerosol/launch_configuration.rb +5 -3
- data/lib/aerosol/launch_template.rb +133 -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 +60 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9494ad62c369aa73f67af3dca0dcc28d8dd29ef820e5bd75c05e30b4203bd950
|
4
|
+
data.tar.gz: a7be58f47b1d874751346371cc3e448bcea64f5fce4846fc2b2c4875788db5ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 805b520c8b7e883e58113b864732bbd841f01d56d3d1703da8e79982d940d590a47d0f2ed5458efa523bbb03c95d81a82eee5b031d81f946f92d7ff58b1f6024
|
7
|
+
data.tar.gz: 8b0390907ccbefef4e58d333f2e92da0a5489304380776fa00836fa9d578200c1623a489985aa1bff66e48e22cea84cdbcec2915ad984b1d364e0fcf31955b9d
|
data/.travis.yml
CHANGED
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', '~>
|
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
|
|
data/lib/aerosol/auto_scaling.rb
CHANGED
@@ -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! :
|
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
|
-
|
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
|
-
}
|
55
|
-
|
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
|
-
|
73
|
+
launch_details.destroy
|
64
74
|
rescue => ex
|
65
|
-
info "Launch
|
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
|
|
data/lib/aerosol/instance.rb
CHANGED
@@ -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
|
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.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:
|
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: '
|
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
|
@@ -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:
|
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:
|