aerosol 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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