aerosol 0.5.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.cane +3 -0
  3. data/.gitignore +15 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +399 -0
  9. data/Rakefile +18 -0
  10. data/aerosol.gemspec +32 -0
  11. data/bin/aerosol +8 -0
  12. data/img/aerosol.pdf +3898 -11
  13. data/img/aerosol.png +0 -0
  14. data/lib/aerosol/auto_scaling.rb +240 -0
  15. data/lib/aerosol/aws.rb +62 -0
  16. data/lib/aerosol/aws_model.rb +93 -0
  17. data/lib/aerosol/cli.rb +41 -0
  18. data/lib/aerosol/connection.rb +39 -0
  19. data/lib/aerosol/deploy.rb +105 -0
  20. data/lib/aerosol/env.rb +6 -0
  21. data/lib/aerosol/instance.rb +55 -0
  22. data/lib/aerosol/launch_configuration.rb +106 -0
  23. data/lib/aerosol/rake_task.rb +141 -0
  24. data/lib/aerosol/runner.rb +329 -0
  25. data/lib/aerosol/util.rb +41 -0
  26. data/lib/aerosol/version.rb +5 -0
  27. data/lib/aerosol.rb +83 -0
  28. data/spec/aerosol/auto_scaling_spec.rb +420 -0
  29. data/spec/aerosol/aws_spec.rb +24 -0
  30. data/spec/aerosol/cli_spec.rb +10 -0
  31. data/spec/aerosol/connection_spec.rb +94 -0
  32. data/spec/aerosol/deploy_spec.rb +192 -0
  33. data/spec/aerosol/env_spec.rb +16 -0
  34. data/spec/aerosol/instance_spec.rb +57 -0
  35. data/spec/aerosol/launch_configuration_spec.rb +328 -0
  36. data/spec/aerosol/rake_task_spec.rb +19 -0
  37. data/spec/aerosol/runner_spec.rb +482 -0
  38. data/spec/aerosol_spec.rb +41 -0
  39. data/spec/fixtures/Procfile +1 -0
  40. data/spec/fixtures/Rakefile +17 -0
  41. data/spec/fixtures/not_a_tar-2.txt +1 -0
  42. data/spec/fixtures/not_a_tar.txt +1 -0
  43. data/spec/fixtures/tar-2.tar +0 -0
  44. data/spec/fixtures/test-1.tar +0 -0
  45. data/spec/fixtures/test-2.tar.gz +0 -0
  46. data/spec/spec_helper.rb +22 -0
  47. data/spec/support/vcr.rb +11 -0
  48. data/spec/vcr/Deployz_Docker/_fetch_import/when_both_import_and_name_are_present_present/and_it_points_to_a_non-S3_url/pulls_the_file.yml +214 -0
  49. metadata +312 -0
data/img/aerosol.png ADDED
Binary file
@@ -0,0 +1,240 @@
1
+ class Aerosol::AutoScaling
2
+ include Aerosol::AWSModel
3
+ include Dockly::Util::Logger::Mixin
4
+
5
+ logger_prefix '[aerosol auto_scaling]'
6
+ aws_attribute :aws_identifier => 'AutoScalingGroupName',
7
+ :availability_zones => 'AvailabilityZones',
8
+ :min_size => 'MinSize',
9
+ :max_size => 'MaxSize',
10
+ :default_cooldown => 'DefaultCooldown',
11
+ :desired_capacity => 'DesiredCapacity',
12
+ :health_check_grace_period => 'HealthCheckGracePeriod',
13
+ :health_check_type => 'HealthCheckType',
14
+ :load_balancer_names => 'LoadBalancerNames',
15
+ :placement_group => 'PlacementGroup',
16
+ :tag_from_array => 'Tags',
17
+ :created_time => 'CreatedTime',
18
+ :vpc_zone_identifier => 'VPCZoneIdentifier'
19
+ aws_class_attribute :launch_configuration, Aerosol::LaunchConfiguration
20
+ primary_key :aws_identifier
21
+
22
+ def initialize(options={}, &block)
23
+ tag = options.delete(:tag)
24
+ super(options, &block)
25
+
26
+ tags.merge!(tag) unless tag.nil?
27
+
28
+ tags["GitSha"] ||= Aerosol::Util.git_sha
29
+ tags["Deploy"] ||= namespaced_name
30
+ end
31
+
32
+ def aws_identifier(arg = nil)
33
+ if arg
34
+ raise "You cannot set the aws_identifer directly" unless from_aws
35
+ @aws_identifier = arg
36
+ else
37
+ @aws_identifier || default_identifier
38
+ end
39
+ end
40
+
41
+ def exists?
42
+ info "auto_scaling: needed?: #{namespaced_name}: " +
43
+ "checking for auto scaling group: #{aws_identifier}"
44
+ exists = super
45
+ info "auto scaling: needed?: #{namespaced_name}: " +
46
+ "#{exists ? 'found' : 'did not find'} auto scaling group: #{aws_identifier}"
47
+ exists
48
+ end
49
+
50
+ def create!
51
+ ensure_present! :launch_configuration, :max_size, :min_size
52
+ raise 'availability_zones or vpc_zone_identifier must be set' if [availability_zones, vpc_zone_identifier].none?
53
+
54
+ info "creating auto scaling group"
55
+ launch_configuration.create
56
+ info self.inspect
57
+
58
+ conn.create_auto_scaling_group(aws_identifier, availability_zones,
59
+ launch_configuration.aws_identifier,
60
+ max_size, min_size,
61
+ create_options)
62
+ make_fake_instances
63
+ sleep 10 # TODO: switch to fog models and .wait_for { ready? }
64
+ end
65
+
66
+ def destroy!
67
+ info self.inspect
68
+ conn.delete_auto_scaling_group(aws_identifier, 'ForceDelete' => true)
69
+ begin
70
+ (0..2).each { break if deleting?; sleep 1 }
71
+ launch_configuration.destroy
72
+ rescue => ex
73
+ info "Launch Config: #{launch_configuration} for #{aws_identifier} was not deleted."
74
+ info ex.message
75
+ end
76
+ end
77
+
78
+ def deleting?
79
+ asgs = conn.describe_auto_scaling_groups("AutoScalingGroupNames" => self.aws_identifier)
80
+ .body
81
+ .[]('DescribeAutoScalingGroupsResult')
82
+ .[]('AutoScalingGroups')
83
+
84
+ return true if asgs.empty?
85
+
86
+ asgs.first['Status'].to_s.include?('Delete')
87
+ end
88
+
89
+ def all_instances
90
+ Aerosol::AWS.auto_scaling
91
+ .describe_auto_scaling_groups('AutoScalingGroupNames' => self.aws_identifier)
92
+ .body
93
+ .[]('DescribeAutoScalingGroupsResult')
94
+ .[]('AutoScalingGroups')
95
+ .first
96
+ .[]('Instances')
97
+ .map { |instance| Aerosol::Instance.from_hash(instance) }
98
+ end
99
+
100
+ def tag(val)
101
+ tags.merge!(val)
102
+ end
103
+
104
+ def tags
105
+ @tags ||= {}
106
+ end
107
+
108
+ def self.request_all_for_token(next_token)
109
+ options = next_token.nil? ? {} : { 'NexToken' => '' }
110
+ Aerosol::AWS.auto_scaling
111
+ .describe_auto_scaling_groups(options)
112
+ .body
113
+ .[]('DescribeAutoScalingGroupsResult')
114
+ end
115
+
116
+ def self.request_all
117
+ next_token = nil
118
+ asgs = []
119
+
120
+ begin
121
+ new_asgs = request_all_for_token(next_token)
122
+ asgs.concat(new_asgs['AutoScalingGroups'])
123
+ next_token = new_asgs['NextToken']
124
+ end while !next_token.nil?
125
+
126
+ asgs
127
+ end
128
+
129
+ def self.latest_for_tag(key, value)
130
+ all.select { |group| group.tags[key] == value }
131
+ .sort_by { |group| group.created_time }.last
132
+ end
133
+
134
+ def to_s
135
+ %{Aerosol::AutoScaling { \
136
+ "aws_identifier" => "#{aws_identifier}", \
137
+ "availability_zones" => "#{availability_zones}", \
138
+ "min_size" => "#{min_size}", \
139
+ "max_size" => "#{max_size}", \
140
+ "default_cooldown" => "#{default_cooldown}", \
141
+ "desired_capacity" => "#{desired_capacity}", \
142
+ "health_check_grace_period" => "#{health_check_grace_period}", \
143
+ "health_check_type" => "#{health_check_type}", \
144
+ "load_balancer_names" => "#{load_balancer_names}", \
145
+ "placement_group" => "#{placement_group}", \
146
+ "tags" => #{tags.to_s}, \
147
+ "created_time" => "#{created_time}" \
148
+ }}
149
+ end
150
+
151
+ private
152
+ def conn
153
+ Aerosol::AWS.auto_scaling
154
+ end
155
+
156
+ def create_options
157
+ {
158
+ 'DefaultCooldown' => default_cooldown,
159
+ 'DesiredCapacity' => desired_capacity,
160
+ 'HealthCheckGracePeriod' => health_check_grace_period,
161
+ 'HealthCheckType' => health_check_type,
162
+ 'LoadBalancerNames' => load_balancer_names,
163
+ 'PlacementGroup' => placement_group,
164
+ 'Tags' => tags,
165
+ 'VPCZoneIdentifier' => vpc_zone_identifier
166
+ }.reject { |k, v| v.nil? }
167
+ end
168
+
169
+ def tag_from_array(ary)
170
+ if ary.is_a? Hash
171
+ ary.each do |key, value|
172
+ tag key => value
173
+ end
174
+ else
175
+ ary.each do |hash|
176
+ tag hash['Key'] => hash['Value']
177
+ end
178
+ end
179
+ end
180
+
181
+ # Unfortunately, Fog does not create fake instances after an auto scaling
182
+ # group is created.
183
+ def make_fake_instances
184
+ return unless Fog.mock?
185
+
186
+ asg_instances = []
187
+ all_instances = []
188
+ min_size.times do |n|
189
+ instance_id = Fog::AWS::Mock.instance_id
190
+ asg_instances << {
191
+ 'AvailabilityZone' => availability_zones,
192
+ 'HealthStatus' => 'Good',
193
+ 'InstanceId' => instance_id,
194
+ 'LifecycleState' => 'Pending',
195
+ 'LaunchConfigurationName' => launch_configuration.aws_identifier
196
+ }
197
+
198
+ all_instances << {
199
+ 'amiLaunchIndex' => n,
200
+ 'architecture' => 'i386',
201
+ 'blockDeviceMapping' => [],
202
+ 'clientToken' => 'FAKE_CLIENT_TOKEN',
203
+ 'dnsName' => 'not-a-real-hostname',
204
+ 'ebsOptimized' => false,
205
+ 'hypervisor' => 'xen',
206
+ 'imageId' => launch_configuration.ami,
207
+ 'instanceId' => instance_id,
208
+ 'instanceState' => { 'code' => 0, 'name' => 'not pending?' },
209
+ 'instanceType' => launch_configuration.instance_type,
210
+ 'kernelId' => launch_configuration.kernel_id || Fog::AWS::Mock.kernel_id,
211
+ 'keyName' => launch_configuration.key_name,
212
+ 'launchTime' => Time.now,
213
+ 'monitoring' => { 'state' => false },
214
+ 'placement' => { 'availabilityZone' => availability_zones,
215
+ 'groupName' => self.aws_identifier,
216
+ 'tenancy' => 'default' },
217
+ 'privateDnsName' => nil,
218
+ 'productCodes' => [],
219
+ 'reason' => nil,
220
+ 'rootDeviceType' => 'instance-store',
221
+ 'virtualizationType' => 'paravirtual',
222
+ 'groupIds' => [],
223
+ 'groupSet' => launch_configuration.security_groups,
224
+ 'iamInstanceProfile' => launch_configuration.iam_role,
225
+ 'networkInterfaces' => [],
226
+ 'ownerId' => nil,
227
+ 'privateIpAddress' => nil,
228
+ 'reservationId' => Fog::AWS::Mock.reservation_id,
229
+ 'stateReason' => {},
230
+ 'ipAddress' => Fog::AWS::Mock.ip_address,
231
+ 'privateIpAddress' => Fog::AWS::Mock.private_ip_address
232
+ }
233
+ end
234
+ Aerosol::AWS.auto_scaling.data[:auto_scaling_groups][aws_identifier]
235
+ .merge!('Instances' => asg_instances)
236
+ all_instances.each do |instance|
237
+ Aerosol::AWS.compute.data[:instances][instance['instanceId']] = instance
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,62 @@
1
+ require 'fog/aws'
2
+
3
+ # This module holds the connections for all AWS services used by the gem.
4
+ module Aerosol::AWS
5
+ extend self
6
+
7
+ def service(name, klass)
8
+ define_method name do
9
+ if val = instance_variable_get(:"@#{name}")
10
+ val
11
+ else
12
+ instance = klass.new(creds)
13
+ instance_variable_set(:"@#{name}", instance)
14
+ end
15
+ end
16
+ services << name
17
+ end
18
+
19
+ def services
20
+ @services ||= []
21
+ end
22
+
23
+ def env_attr(*names)
24
+ names.each do |name|
25
+ define_method name do
26
+ instance_variable_get(:"@#{name}") || ENV[name.to_s.upcase]
27
+ end
28
+
29
+ define_method :"#{name}=" do |val|
30
+ reset_cache!
31
+ instance_variable_set(:"@#{name}", val)
32
+ end
33
+
34
+ env_attrs << name
35
+ end
36
+ end
37
+
38
+ def env_attrs
39
+ @env_attrs ||= []
40
+ end
41
+
42
+ def creds
43
+ attrs = Hash[env_attrs.map { |attr| [attr, public_send(attr)] }].reject { |k, v| v.nil? }
44
+ if attrs.empty?
45
+ if ENV['FOG_CREDENTIAL']
46
+ attrs = {} # let Fog use the env var
47
+ else
48
+ attrs = { :use_iam_profile => true }
49
+ end
50
+ end
51
+ attrs
52
+ end
53
+
54
+ def reset_cache!
55
+ services.each { |service| instance_variable_set(:"@#{service}", nil) }
56
+ end
57
+
58
+ service :s3, Fog::Storage::AWS
59
+ service :compute, Fog::Compute::AWS
60
+ service :auto_scaling, Fog::AWS::AutoScaling
61
+ env_attr :aws_access_key_id, :aws_secret_access_key
62
+ end
@@ -0,0 +1,93 @@
1
+ module Aerosol::AWSModel
2
+ def self.included(base)
3
+ base.instance_eval do
4
+ include Dockly::Util::DSL
5
+ extend ClassMethods
6
+
7
+ attr_accessor :from_aws
8
+ end
9
+ end
10
+
11
+ def initialize(hash={}, &block)
12
+ self.from_aws = false
13
+ super
14
+ end
15
+
16
+ def namespaced_name
17
+ Aerosol.namespace ? "#{Aerosol.namespace}-#{name}" : name.to_s
18
+ end
19
+
20
+ def default_identifier
21
+ "#{namespaced_name}-#{Aerosol::Util.git_sha}"
22
+ end
23
+
24
+ def create
25
+ raise '#create! must be defined to use #create' unless respond_to?(:create!)
26
+ create! unless exists?
27
+ end
28
+
29
+ def destroy
30
+ raise '#destroy! must be defined to use #destroy' unless respond_to?(:destroy!)
31
+ destroy! if exists?
32
+ end
33
+
34
+ def exists?
35
+ primary_value = send(self.class.primary_key)
36
+ self.class.exists?(primary_value)
37
+ end
38
+
39
+ module ClassMethods
40
+ def primary_key(attr = nil)
41
+ @primary_key = attr unless attr.nil?
42
+ @primary_key
43
+ end
44
+
45
+ def aws_attribute(hash)
46
+ dsl_attribute(*hash.keys)
47
+ aws_attributes.merge!(hash)
48
+ end
49
+
50
+ def aws_class_attribute(name, klass)
51
+ unless klass.ancestors.include?(Aerosol::AWSModel) || (klass == self)
52
+ raise '.aws_class_attribute requires a Aerosol::AWSModel that is not the current class.'
53
+ end
54
+
55
+ dsl_class_attribute(name, klass)
56
+ aws_class_attributes.merge!({ name => klass })
57
+ end
58
+
59
+ def exists?(key)
60
+ all.map { |inst| inst.send(primary_key) }.include?(key)
61
+ end
62
+
63
+ def all
64
+ raise 'Please define .request_all to use .all' unless respond_to?(:request_all)
65
+ request_all.map { |hash| from_hash(hash) }
66
+ end
67
+
68
+ def from_hash(hash)
69
+ 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|
71
+ [name, klass.instances.values.find do |inst|
72
+ inst.send(klass.primary_key) &&
73
+ (inst.send(klass.primary_key) == hash[klass.aws_attributes[klass.primary_key]])
74
+ end]
75
+ end].reject { |k, v| v.nil? }
76
+
77
+ instance = new!
78
+ instance.from_aws = true
79
+
80
+ aws_attributes.each { |k, v| instance.send(k, hash[v]) unless hash[v].nil? }
81
+ refs.each { |name, inst| instance.send(name, inst.name) }
82
+ instance
83
+ end
84
+
85
+ def aws_attributes
86
+ @aws_attributes ||= {}
87
+ end
88
+
89
+ def aws_class_attributes
90
+ @aws_class_attributes ||= {}
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'aerosol'
3
+ require 'clamp'
4
+
5
+ class Aerosol::AbstractCommand < Clamp::Command
6
+ option ['-f', '--file'], 'FILE', 'aerosol file to read', :default => 'aerosol.rb', :attribute_name => :file
7
+
8
+ def execute
9
+ if File.exist?(file)
10
+ Aerosol.load_file = file
11
+ else
12
+ raise 'Could not find an aerosol file!'
13
+ end
14
+ end
15
+ end
16
+
17
+ class Aerosol::SshCommand < Aerosol::AbstractCommand
18
+ option ['-r', '--run'], :flag, 'run first ssh command', :attribute_name => :run_first
19
+ parameter 'DEPLOY', 'the deploy to list commands for', :attribute_name => :deploy_name
20
+
21
+ def execute
22
+ super
23
+ if deploy = Aerosol.deploy(deploy_name.to_sym)
24
+ ssh_commands = deploy.generate_ssh_commands
25
+ raise 'No instances to ssh too!' if ssh_commands.empty?
26
+
27
+ ssh_commands.each do |ssh_command|
28
+ puts ssh_command
29
+ end
30
+
31
+ if run_first?
32
+ system(ssh_commands.first)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ class Aerosol::Cli < Aerosol::AbstractCommand
39
+ subcommand ['ssh', 's'], 'Print ssh commands for latest running instances', Aerosol::SshCommand
40
+ end
41
+
@@ -0,0 +1,39 @@
1
+ require 'net/ssh'
2
+ require 'net/ssh/gateway'
3
+
4
+ class Aerosol::Connection
5
+ include Dockly::Util::DSL
6
+ include Dockly::Util::Logger::Mixin
7
+
8
+ logger_prefix '[aerosol connection]'
9
+ dsl_attribute :user, :host, :jump
10
+
11
+ def with_connection(overridden_host=nil, &block)
12
+ actual_host = overridden_host || host
13
+ unless actual_host.is_a?(String)
14
+ actual_host = (actual_host.public_hostname || actual_host.private_ip_address)
15
+ end
16
+
17
+ if jump
18
+ info "connecting to gateway #{jump[:user] || user}@#{jump[:host]}"
19
+ gateway = nil
20
+ Timeout.timeout(20) do
21
+ gateway = Net::SSH::Gateway.new(jump[:host], jump[:user] || user, :forward_agent => true)
22
+ end
23
+
24
+ begin
25
+ info "connecting to #{user}@#{actual_host} through gateway"
26
+ gateway.ssh(actual_host, user, &block)
27
+ ensure
28
+ info "shutting down gateway connection"
29
+ gateway.shutdown!
30
+ end
31
+ else
32
+ info "connecting to #{user}@#{actual_host}"
33
+ Net::SSH.start(actual_host, user, &block)
34
+ end
35
+ rescue Timeout::Error => ex
36
+ error "Timeout error #{ex.message}"
37
+ error ex.backtrace.join("\n")
38
+ end
39
+ end
@@ -0,0 +1,105 @@
1
+ class Aerosol::Deploy
2
+ include Dockly::Util::DSL
3
+ include Dockly::Util::Logger::Mixin
4
+
5
+ logger_prefix '[aerosol deploy]'
6
+ dsl_attribute :stop_command, :db_config_path,
7
+ :instance_live_grace_period, :app_port,
8
+ :continue_if_stop_app_fails, :stop_app_retries,
9
+ :sleep_before_termination, :post_deploy_command,
10
+ :ssl, :log_files, :tail_logs
11
+
12
+ dsl_class_attribute :ssh, Aerosol::Connection
13
+ dsl_class_attribute :migration_ssh, Aerosol::Connection
14
+ dsl_class_attribute :local_ssh, Aerosol::Connection
15
+ dsl_class_attribute :auto_scaling, Aerosol::AutoScaling
16
+
17
+ default_value :db_config_path, 'config/database.yml'
18
+ default_value :instance_live_grace_period, 30 * 60 # 30 Minutes
19
+ default_value :continue_if_stop_app_fails, false
20
+ default_value :stop_app_retries, 2
21
+ default_value :sleep_before_termination, 20
22
+ default_value :ssl, false
23
+ default_value :tail_logs, false
24
+ default_value :log_files, ['/var/log/syslog']
25
+
26
+ def live_check(arg = nil)
27
+ case
28
+ when arg.nil?
29
+ @live_check
30
+ when arg.start_with?('/')
31
+ @live_check = arg
32
+ else
33
+ @live_check = "/#{arg}"
34
+ end
35
+ @live_check
36
+ end
37
+
38
+ def is_alive?(&block)
39
+ @is_alive = block unless block.nil?
40
+ @is_alive
41
+ end
42
+
43
+ def live_check_url
44
+ [ssl ? 'https' : 'http', '://localhost:', app_port, live_check].join
45
+ end
46
+
47
+ def do_not_migrate!
48
+ self.instance_variable_set(:@db_config_path, nil)
49
+ end
50
+
51
+ def migration(opts = {})
52
+ self.db_config_path(opts[:db_config_path])
53
+ end
54
+
55
+ def migrate?
56
+ !!db_config_path
57
+ end
58
+
59
+ def local_ssh_ref
60
+ local_ssh || ssh
61
+ end
62
+
63
+ def run_post_deploy
64
+ return if post_deploy_command.nil?
65
+ info "running post deploy: #{post_deploy_command}"
66
+ if system(post_deploy_command)
67
+ info "post deploy ran successfully"
68
+ true
69
+ else
70
+ raise "post deploy failed"
71
+ end
72
+ end
73
+
74
+ def generate_ssh_command(instance)
75
+ ssh_command = "ssh -o 'UserKnownHostsFile=/dev/null' -o 'StrictHostKeyChecking=no' "
76
+ unless local_ssh_ref.nil?
77
+ unless local_ssh_ref.jump.nil? || local_ssh_ref.jump.empty?
78
+ ssh_command << "-o 'ProxyCommand=ssh -W %h:%p "
79
+ ssh_command << "#{local_ssh_ref.jump[:user]}@" if local_ssh_ref.jump[:user]
80
+ ssh_command << "#{local_ssh_ref.jump[:host]}' "
81
+ end
82
+ ssh_command << "#{local_ssh_ref.user}@" unless local_ssh_ref.user.nil?
83
+ end
84
+ ssh_command << "#{instance.public_hostname || instance.private_ip_address}"
85
+ end
86
+
87
+ def generate_ssh_commands
88
+ group = Aerosol::AutoScaling.latest_for_tag('Deploy', auto_scaling.namespaced_name)
89
+ raise "Could not find any auto scaling groups for this deploy (#{name})." if group.nil?
90
+
91
+ ssh_commands = []
92
+
93
+ with_prefix("[#{name}]") do |logger|
94
+ logger.info "found group: #{group.aws_identifier}"
95
+ instances = group.all_instances
96
+ raise "Could not find any instances for auto scaling group #{group.namespaced_name}" if instances.empty?
97
+ instances.each do |instance|
98
+ logger.info "printing ssh command for #{instance.public_hostname || instance.private_ip_address}"
99
+ ssh_commands << generate_ssh_command(instance)
100
+ end
101
+ end
102
+
103
+ return ssh_commands
104
+ end
105
+ end
@@ -0,0 +1,6 @@
1
+ # An environment is a set of deploys.
2
+ class Aerosol::Env
3
+ include Dockly::Util::DSL
4
+
5
+ dsl_class_attribute :deploy, Aerosol::Deploy, type: Array
6
+ end
@@ -0,0 +1,55 @@
1
+ class Aerosol::Instance
2
+ include Aerosol::AWSModel
3
+
4
+ aws_attribute :availability_zone => 'AvailabilityZone',
5
+ :health_status => 'HealthStatus',
6
+ :id => 'InstanceId',
7
+ :lifecycle_state => 'LifecycleState'
8
+ aws_class_attribute :launch_configuration, Aerosol::LaunchConfiguration
9
+ primary_key :id
10
+
11
+ def live?
12
+ describe_again
13
+ instance_state_name == 'running'
14
+ end
15
+
16
+ def instance_state_name
17
+ description['instanceState']['name']
18
+ end
19
+
20
+ def public_hostname
21
+ description['dnsName']
22
+ end
23
+
24
+ def private_ip_address
25
+ description['privateIpAddress']
26
+ end
27
+
28
+ def ami
29
+ description['imageId']
30
+ end
31
+
32
+ def description
33
+ @description ||= describe!
34
+ end
35
+
36
+ def self.request_all
37
+ Aerosol::AWS.auto_scaling
38
+ .describe_auto_scaling_instances
39
+ .body
40
+ .[]('DescribeAutoScalingInstancesResult')
41
+ .[]('AutoScalingInstances')
42
+ end
43
+
44
+ private
45
+ def describe!
46
+ ensure_present! :id
47
+ result = Aerosol::AWS.compute.describe_instances('instance-id' => id).body
48
+ result['reservationSet'].first['instancesSet'].first rescue nil
49
+ end
50
+
51
+ def describe_again
52
+ @description = nil
53
+ description
54
+ end
55
+ end