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
@@ -0,0 +1,106 @@
1
+ class Aerosol::LaunchConfiguration
2
+ include Aerosol::AWSModel
3
+ include Dockly::Util::Logger::Mixin
4
+
5
+ logger_prefix '[aerosol launch_configuration]'
6
+ aws_attribute :aws_identifier => 'LaunchConfigurationName',
7
+ :ami => 'ImageId',
8
+ :instance_type => 'InstanceType',
9
+ :security_groups => 'SecurityGroups',
10
+ :user_data => 'UserData',
11
+ :iam_role => 'IamInstanceProfile',
12
+ :kernel_id => 'KernelId',
13
+ :key_name => 'KeyName',
14
+ :spot_price => 'SpotPrice',
15
+ :created_time => 'CreatedTime',
16
+ :associate_public_ip_address => 'AssociatePublicIpAddress'
17
+
18
+ primary_key :aws_identifier
19
+ default_value(:security_groups) { [] }
20
+
21
+ def aws_identifier(arg = nil)
22
+ if arg
23
+ raise "You cannot set the aws_identifer directly" unless from_aws
24
+ @aws_identifier = arg
25
+ else
26
+ @aws_identifier || default_identifier
27
+ end
28
+ end
29
+
30
+ def security_group(group)
31
+ security_groups << group
32
+ end
33
+
34
+ def create!
35
+ ensure_present! :ami, :instance_type
36
+
37
+ info self.to_s
38
+ conn.create_launch_configuration(ami, instance_type, aws_identifier, create_options)
39
+ sleep 10 # TODO: switch to fog models and .wait_for { ready? }
40
+ end
41
+
42
+ def destroy!
43
+ info self.to_s
44
+ conn.delete_launch_configuration(aws_identifier)
45
+ end
46
+
47
+ def all_instances
48
+ Aerosol::Instance.all.select { |instance|
49
+ !instance.launch_configuration.nil? &&
50
+ (instance.launch_configuration.aws_identifier == self.aws_identifier)
51
+ }.each(&:description)
52
+ end
53
+
54
+ def self.request_all_for_token(next_token)
55
+ options = next_token.nil? ? {} : { 'NextToken' => next_token }
56
+ Aerosol::AWS.auto_scaling
57
+ .describe_launch_configurations(options)
58
+ .body
59
+ .[]('DescribeLaunchConfigurationsResult')
60
+ end
61
+
62
+ def self.request_all
63
+ next_token = nil
64
+ lcs = []
65
+
66
+ begin
67
+ new_lcs = request_all_for_token(next_token)
68
+ lcs.concat(new_lcs['LaunchConfigurations'])
69
+ next_token = new_lcs['NextToken']
70
+ end while !next_token.nil?
71
+
72
+ lcs
73
+ end
74
+
75
+ def to_s
76
+ %{Aerosol::LaunchConfiguration { \
77
+ "aws_identifier" => "#{aws_identifier}", \
78
+ "ami" => "#{ami}", \
79
+ "instance_type" => "#{instance_type}", \
80
+ "security_groups" => #{security_groups.to_s}, \
81
+ "user_data" => "#{user_data}", \
82
+ "iam_role" => "#{iam_role}", \
83
+ "kernel_id" => "#{kernel_id}", \
84
+ "key_name" => "#{key_name}", \
85
+ "spot_price" => "#{spot_price}", \
86
+ "created_time" => "#{created_time}" \
87
+ }}
88
+ end
89
+
90
+ private
91
+ def create_options
92
+ { # TODO Add dsl so that 'BlockDeviceMappings' may be specified
93
+ 'IamInstanceProfile' => iam_role,
94
+ 'KernelId' => kernel_id,
95
+ 'KeyName' => key_name,
96
+ 'SecurityGroups' => security_groups,
97
+ 'SpotPrice' => spot_price,
98
+ 'UserData' => Aerosol::Util.strip_heredoc(user_data || ''),
99
+ 'AssociatePublicIpAddress' => associate_public_ip_address
100
+ }.reject { |k, v| v.nil? }
101
+ end
102
+
103
+ def conn
104
+ Aerosol::AWS.auto_scaling
105
+ end
106
+ end
@@ -0,0 +1,141 @@
1
+ require 'rake'
2
+ require 'aerosol'
3
+
4
+ $rake_task_logger = Dockly::Util::Logger.new('[aerosol rake_task]', STDOUT, false)
5
+
6
+ class Rake::AutoScalingTask < Rake::Task
7
+ def needed?
8
+ !auto_scaling.exists?
9
+ end
10
+
11
+ def auto_scaling
12
+ Aerosol.auto_scaling(name.split(':').last.to_sym)
13
+ end
14
+ end
15
+
16
+ module Rake::DSL
17
+ def auto_scaling(*args, &block)
18
+ Rake::AutoScalingTask.define_task(*args, &block)
19
+ end
20
+ end
21
+
22
+ namespace :aerosol do
23
+ desc "Verify an aerosol.rb file exists"
24
+ task :load do
25
+ raise "No aerosol.rb found!" unless File.exist?('aerosol.rb')
26
+ end
27
+
28
+ namespace :auto_scaling do
29
+ Aerosol.auto_scalings.values.reject(&:from_aws).each do |inst|
30
+ auto_scaling inst.name => 'aerosol:load' do |name|
31
+ Thread.current[:rake_task] = name
32
+ inst.create
33
+ end
34
+ end
35
+ end
36
+
37
+ namespace :ssh do
38
+ Aerosol.deploys.values.each do |inst|
39
+ desc "Prints out ssh command to all instances of the latest deploy of #{inst.name}"
40
+ task inst.name do |name|
41
+ Thread.current[:rake_task] = name
42
+ inst.generate_ssh_commands.each do |ssh_command|
43
+ puts ssh_command
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ all_deploy_tasks = []
50
+ all_asynch_deploy_tasks = []
51
+
52
+ namespace :env do
53
+ Aerosol.envs.values.each do |env|
54
+ desc "Run all of the deploys for #{env.name} in parallel"
55
+ multitask env.name => env.deploy.map { |dep| "aerosol:#{dep.name}:all" }
56
+ end
57
+ end
58
+
59
+
60
+ Aerosol.deploys.values.each do |inst|
61
+ namespace :"#{inst.name}" do
62
+ desc "Runs the ActiveRecord migration through the SSH connection given"
63
+ task :run_migration => 'aerosol:load' do |name|
64
+ Thread.current[:rake_task] = name
65
+ Aerosol::Runner.new.with_deploy(inst.name) do |runner|
66
+ runner.run_migration
67
+ end
68
+ end
69
+
70
+ desc "Creates a new auto scaling group for the current git hash"
71
+ task :create_auto_scaling_group => "aerosol:auto_scaling:#{inst.auto_scaling.name}"
72
+
73
+ desc "Waits for instances of the new autoscaling groups to start up"
74
+ task :wait_for_new_instances => 'aerosol:load' do |name|
75
+ Thread.current[:rake_task] = name
76
+ Aerosol::Runner.new.with_deploy(inst.name) do |runner|
77
+ runner.wait_for_new_instances
78
+ end
79
+ end
80
+
81
+ desc "Runs command to shut down the application on the old instances instead of just terminating"
82
+ task :stop_old_app => 'aerosol:load' do |name|
83
+ Thread.current[:rake_task] = name
84
+ Aerosol::Runner.new.with_deploy(inst.name) do |runner|
85
+ runner.stop_app
86
+ end
87
+ end
88
+
89
+ desc "Terminates instances with the current tag and different git hash"
90
+ task :destroy_old_auto_scaling_groups => 'aerosol:load' do |name|
91
+ Thread.current[:rake_task] = name
92
+ Aerosol::Runner.new.with_deploy(inst.name) do |runner|
93
+ runner.destroy_old_auto_scaling_groups
94
+ end
95
+ end
96
+
97
+ desc "Terminates instances with the current tag and current git hash"
98
+ task :destroy_new_auto_scaling_groups => 'aerosol:load' do |name|
99
+ Thread.current[:rake_task] = name
100
+ Aerosol::Runner.new.with_deploy(inst.name) do |runner|
101
+ runner.destroy_new_auto_scaling_groups
102
+ end
103
+ end
104
+
105
+ desc "Runs a post deploy command"
106
+ task :run_post_deploy => 'aerosol:load' do |name|
107
+ Thread.current[:rake_task] = name
108
+ inst.run_post_deploy
109
+ end
110
+
111
+ ##
112
+
113
+ desc "Runs migration and creates auto scaling groups"
114
+ task :all_prep => [:run_migration, :create_auto_scaling_group]
115
+
116
+ desc "Waits for new instances, stops old application, destroys old auto scaling groups "\
117
+ "and runs the post deploy command"
118
+ task :all_release => [:wait_for_new_instances, :stop_old_app, :destroy_old_auto_scaling_groups, :run_post_deploy]
119
+
120
+ desc "Run migration, create auto scaling group, wait for instances, stop old application, "\
121
+ "destroy old auto scaling groups and run the post deploy command"
122
+ task :all => [:all_prep, :all_release]
123
+ all_deploy_tasks << "aerosol:#{inst.name}:all"
124
+
125
+ ##
126
+
127
+ desc "Runs migration and creates auto scaling groups in parallel"
128
+ multitask :all_asynch_prep => [:run_migration, :create_auto_scaling_group]
129
+
130
+ desc "Same as `all` but runs the migration and creates auto scaling groups in parallel"
131
+ task :all_asynch => [:all_asynch_prep, :all_release]
132
+ all_asynch_deploy_tasks << "aerosol:#{inst.name}:all_asynch"
133
+ end
134
+ end
135
+
136
+ desc "Runs all the all deploy tasks in the aerosol.rb"
137
+ task :deploy_all => all_deploy_tasks
138
+
139
+ desc "Runs all the all deploy tasks in the aerosol.rb in parallel"
140
+ multitask :deploy_all_asynch => all_asynch_deploy_tasks
141
+ end
@@ -0,0 +1,329 @@
1
+ require 'socket'
2
+ require 'active_record'
3
+ require 'grit'
4
+ require 'timeout'
5
+
6
+ class Aerosol::Runner
7
+ extend Dockly::Util::Delegate
8
+ include Dockly::Util::Logger::Mixin
9
+
10
+ logger_prefix '[aerosol runner]'
11
+ attr_reader :deploy, :log_pids
12
+
13
+ def initialize
14
+ @log_pids = {}
15
+ end
16
+
17
+ def run_migration
18
+ require_deploy!
19
+ return unless deploy.migrate?
20
+ raise 'To run a migration, $RAILS_ENV must be set.' if ENV['RAILS_ENV'].nil?
21
+
22
+ info "running migration"
23
+ begin
24
+ info "loading config for env #{ENV['RAILS_ENV']}"
25
+ original_config = YAML.load(ERB.new(File.read(db_config_path)).result)[ENV['RAILS_ENV']]
26
+ debug "creating ssh tunnel"
27
+ migration_ssh.with_connection do |session|
28
+ # session.logger.sev_threshold=Logger::Severity::DEBUG
29
+ debug "finding free port"
30
+ port = random_open_port
31
+ db_port = original_config['port'] || 3306 # TODO: get default port from DB driver
32
+ host = original_config['host']
33
+ info "forwarding 127.0.0.1:#{port} --> #{host}:#{db_port}"
34
+ session.forward.local(port, host, db_port)
35
+ child = fork do
36
+ GC.disable
37
+ with_prefix('child:') do |logger|
38
+ logger.debug "establishing connection"
39
+ ActiveRecord::Base.establish_connection(original_config.merge(
40
+ 'host' => '127.0.0.1',
41
+ 'port' => port
42
+ ))
43
+ logger.info "running migration"
44
+ ActiveRecord::Migrator.migrate(%w[db/migrate])
45
+ end
46
+ end
47
+ debug "waiting for child"
48
+ exitstatus = nil
49
+ session.loop(0.1) do
50
+ pid = Process.waitpid(child, Process::WNOHANG)
51
+ exitstatus = $?.exitstatus if !pid.nil?
52
+ pid.nil?
53
+ end
54
+ raise "migration failed: #{exitstatus}" unless exitstatus == 0
55
+ end
56
+ info "complete"
57
+ ensure
58
+ ActiveRecord::Base.clear_all_connections!
59
+ end
60
+ info "migration ran"
61
+ end
62
+
63
+ def wait_for_new_instances
64
+ require_deploy!
65
+ info "waiting for new instances"
66
+
67
+ live_instances = []
68
+ Timeout.timeout(instance_live_grace_period) do
69
+ loop do
70
+ current_instances = new_instances
71
+ remaining_instances = current_instances - live_instances
72
+ info "waiting for instances to be live (#{remaining_instances.count} remaining)"
73
+ debug "current instances: #{current_instances.map(&:id)}"
74
+ debug "live instances: #{live_instances.map(&:id)}"
75
+ live_instances.concat(remaining_instances.select { |instance| healthy?(instance) })
76
+ break if (current_instances - live_instances).empty?
77
+ debug 'sleeping for 10 seconds'
78
+ sleep(10)
79
+ end
80
+ end
81
+
82
+ info 'new instances are up'
83
+ rescue Timeout::Error
84
+ raise "[aerosol runner] site live check timed out after #{instance_live_grace_period} seconds"
85
+ ensure
86
+ log_pids.each do |instance_id, fork|
87
+ debug "Killing tailing for #{instance_id}: #{Time.now}"
88
+ Process.kill('HUP', fork)
89
+ debug "Killed process for #{instance_id}: #{Time.now}"
90
+ debug "Waiting for process to die"
91
+ Process.wait(fork)
92
+ debug "Process ended for #{instance_id}: #{Time.now}"
93
+ end
94
+ end
95
+
96
+ def healthy?(instance)
97
+ debug "Checking if #{instance.id} is healthy"
98
+
99
+ unless instance.live?
100
+ debug "#{instance.id} is not live"
101
+ return false
102
+ end
103
+
104
+ debug "trying to SSH to #{instance.id}"
105
+ success = false
106
+ ssh.with_connection(instance) do |session|
107
+ start_tailing_logs(ssh, instance) if log_pids[instance.id].nil?
108
+ debug "checking if #{instance.id} is healthy"
109
+ success = if is_alive?.nil?
110
+ debug 'Using default site live check'
111
+ check_site_live(session)
112
+ else
113
+ debug 'Using custom site live check'
114
+ is_alive?.call(session, self)
115
+ end
116
+ end
117
+
118
+ if success
119
+ debug "#{instance.id} is healthy"
120
+ else
121
+ debug "#{instance.id} is not healthy"
122
+ end
123
+ success
124
+ rescue => ex
125
+ debug "#{instance.id} is not healthy: #{ex.message}"
126
+ false
127
+ end
128
+
129
+ def check_site_live(session)
130
+ command = [
131
+ 'wget',
132
+ '-q',
133
+ # Since we're hitting localhost, the cert will always be invalid, so don't try to check it.
134
+ deploy.ssl ? '--no-check-certificate' : nil,
135
+ "'#{deploy.live_check_url}'",
136
+ '-O',
137
+ '/dev/null'
138
+ ].compact.join(' ')
139
+
140
+ debug "running #{command}"
141
+ ret = ssh_exec!(session, command)
142
+ debug "finished running #{command}"
143
+ ret[:exit_status].zero?
144
+ end
145
+
146
+ def start_tailing_logs(ssh, instance)
147
+ if tail_logs && log_files.length > 0
148
+ command = [
149
+ 'sudo', 'tail', '-f', *log_files
150
+ ].join(' ')
151
+
152
+ log_pids[instance.id] ||= ssh_fork(command, ssh, instance)
153
+ end
154
+ end
155
+
156
+ def ssh_fork(command, ssh, instance)
157
+ debug 'starting ssh fork'
158
+ fork do
159
+ Signal.trap('HUP') do
160
+ debug 'Killing tailing session'
161
+ Process.exit!
162
+ end
163
+ debug 'starting tail'
164
+ begin
165
+ ssh.with_connection(instance) do |session|
166
+ debug 'tailing session connected'
167
+ buffer = ''
168
+ ssh_exec!(session, command) do |stream, data|
169
+ data.lines.each do |line|
170
+ if line.end_with?($/)
171
+ debug "[#{instance.id}] #{stream}: #{buffer + line}"
172
+ buffer = ''
173
+ else
174
+ buffer = line
175
+ end
176
+ end
177
+ end
178
+ end
179
+ rescue => ex
180
+ error "#{ex.class}: #{ex.message}"
181
+ error "#{ex.backtrace.join("\n")}"
182
+ ensure
183
+ debug 'finished'
184
+ end
185
+ end
186
+ end
187
+
188
+ def stop_app
189
+ info "stopping old app"
190
+ to_stop = old_instances
191
+
192
+ info "starting with #{to_stop.length} instances to stop"
193
+
194
+ stop_app_retries.succ.times do |n|
195
+ break if to_stop.empty?
196
+ debug "stop app: #{to_stop.length} instances remaining"
197
+ to_stop.reject! { |instance| stop_one_app(instance) }
198
+ end
199
+
200
+ if to_stop.length.zero?
201
+ info "successfully stopped the app on each old instance"
202
+ elsif !continue_if_stop_app_fails
203
+ raise "Failed to stop app on #{to_stop.length} instances"
204
+ end
205
+ info "stopped old app"
206
+ end
207
+
208
+ def destroy_old_auto_scaling_groups
209
+ require_deploy!
210
+ info "destroying old autoscaling groups"
211
+ sleep deploy.sleep_before_termination
212
+ old_auto_scaling_groups.map(&:destroy)
213
+ info "destroyed old autoscaling groups"
214
+ end
215
+
216
+ def destroy_new_auto_scaling_groups
217
+ require_deploy!
218
+ info "destroying autoscaling groups created for this sha"
219
+ new_auto_scaling_groups.map(&:destroy)
220
+ info "destroyed new autoscaling groups"
221
+ end
222
+
223
+ def old_instances
224
+ require_deploy!
225
+ old_auto_scaling_groups.map(&:launch_configuration).compact.map(&:all_instances).flatten.compact
226
+ end
227
+
228
+ def old_auto_scaling_groups
229
+ select_auto_scaling_groups { |asg| asg.tags['GitSha'] != auto_scaling.tags['GitSha'] }
230
+ end
231
+
232
+ def new_auto_scaling_groups
233
+ select_auto_scaling_groups { |asg| asg.tags['GitSha'] == auto_scaling.tags['GitSha'] }
234
+ end
235
+
236
+ def select_auto_scaling_groups(&block)
237
+ require_deploy!
238
+ Aerosol::LaunchConfiguration.all # load all of the launch configurations first
239
+ Aerosol::AutoScaling.all.select { |asg|
240
+ (asg.tags['Deploy'].to_s == auto_scaling.tags['Deploy']) &&
241
+ (block.nil? ? true : block.call(asg))
242
+ }
243
+ end
244
+
245
+ def new_instances
246
+ require_deploy!
247
+ while launch_configuration.all_instances.length < auto_scaling.min_size
248
+ info "Waiting for instances to come up"
249
+ sleep 10
250
+ end
251
+ launch_configuration.all_instances
252
+ end
253
+
254
+ def with_deploy(name)
255
+ unless dep = Aerosol::Deploy[name]
256
+ raise "No deploy named '#{name}'"
257
+ end
258
+ original = @deploy
259
+ @deploy = dep
260
+ yield self
261
+ @deploy = original
262
+ end
263
+
264
+ def require_deploy!
265
+ raise "@deploy must be present" if deploy.nil?
266
+ end
267
+
268
+ def git_sha
269
+ @git_sha ||= Aerosol::Util.git_sha
270
+ end
271
+
272
+ delegate :ssh, :migration_ssh, :package, :auto_scaling, :stop_command,
273
+ :live_check, :db_config_path, :instance_live_grace_period,
274
+ :app_port, :continue_if_stop_app_fails, :stop_app_retries,
275
+ :is_alive?, :log_files, :tail_logs, :to => :deploy
276
+ delegate :launch_configuration, :to => :auto_scaling
277
+
278
+ private
279
+
280
+ def stop_one_app(instance)
281
+ debug "attempting to stop app on: #{instance.public_hostname}"
282
+ ssh.with_connection(instance) do |session|
283
+ session.exec!(stop_command)
284
+ session.loop
285
+ end
286
+ info "successfully stopped app on: #{instance.public_hostname}"
287
+ true
288
+ rescue => ex
289
+ warn "stop app failed on #{instance.public_hostname} due to: #{ex}"
290
+ false
291
+ end
292
+
293
+ # inspired by: http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library
294
+ def ssh_exec!(ssh, command, options = {}, &block)
295
+ res = { :out => "", :err => "", :exit_status => nil }
296
+ ssh.open_channel do |channel|
297
+ if options[:tty]
298
+ channel.request_pty do |ch, success|
299
+ raise "could not start a pseudo-tty" unless success
300
+ channel = ch
301
+ end
302
+ end
303
+
304
+ channel.exec(command) do |ch, success|
305
+ raise "unable to run remote cmd: #{command}" unless success
306
+
307
+ channel.on_data do |_, data|
308
+ block.call(:out, data) unless block.nil?
309
+ res[:out] << data
310
+ end
311
+ channel.on_extended_data do |_, type, data|
312
+ block.call(:err, data) unless block.nil?
313
+ res[:err] << data
314
+ end
315
+ channel.on_request("exit-status") { |_, data| res[:exit_status] = data.read_long }
316
+ end
317
+ end
318
+ ssh.loop
319
+ res
320
+ end
321
+
322
+ def random_open_port
323
+ socket = Socket.new(:INET, :STREAM, 0)
324
+ socket.bind(Addrinfo.tcp("127.0.0.1", 0))
325
+ port = socket.local_address.ip_port
326
+ socket.close
327
+ port
328
+ end
329
+ end
@@ -0,0 +1,41 @@
1
+ require 'grit'
2
+
3
+ module Aerosol::Util
4
+ extend self
5
+
6
+ def is_tar?(path)
7
+ if File.size(path) < 262
8
+ return false
9
+ end
10
+ magic = nil
11
+ File.open(path, "r") do |f|
12
+ f.read(257)
13
+ magic = f.read(5)
14
+ end
15
+ magic == "ustar"
16
+ end
17
+
18
+ def is_gzip?(path)
19
+ if File.size(path) < 2
20
+ return false
21
+ end
22
+ magic = nil
23
+ File.open(path, "r") do |f|
24
+ magic = f.read(2)
25
+ end
26
+ magic = magic.unpack('H*')[0]
27
+ magic == "1f8b"
28
+ end
29
+
30
+ def strip_heredoc(str)
31
+ str.gsub(/^#{str[/\A\s*/]}/, '')
32
+ end
33
+
34
+ def git_repo
35
+ @git_repo ||= Grit::Repo.new('.')
36
+ end
37
+
38
+ def git_sha
39
+ @git_sha ||= git_repo.git.show.lines.first.chomp.match(/^commit ([a-f0-9]+)$/)[1][0..6] rescue 'unknown'
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ # Copyright Swipely, Inc. All rights reserved.
2
+
3
+ module Aerosol
4
+ VERSION = '0.5.1'
5
+ end
data/lib/aerosol.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'fog'
2
+ require 'dockly/util'
3
+
4
+ module Aerosol
5
+ require 'aerosol/aws'
6
+ require 'aerosol/util'
7
+ require 'aerosol/aws_model'
8
+ require 'aerosol/launch_configuration'
9
+ require 'aerosol/auto_scaling'
10
+ require 'aerosol/instance'
11
+ require 'aerosol/connection'
12
+ require 'aerosol/deploy'
13
+ require 'aerosol/env'
14
+
15
+ attr_reader :deploy, :instance, :git_sha, :namespace
16
+ attr_writer :load_file
17
+
18
+ LOAD_FILE = 'aerosol.rb'
19
+
20
+ def load_file
21
+ @load_file || LOAD_FILE
22
+ end
23
+
24
+ def inst
25
+ @instance ||= load_inst
26
+ end
27
+
28
+ def load_inst
29
+ setup.tap do |state|
30
+ if File.exists?(load_file)
31
+ instance_eval(IO.read(load_file), load_file)
32
+ end
33
+ end
34
+ end
35
+
36
+ def namespace(value = nil)
37
+ if value.nil?
38
+ @namespace
39
+ else
40
+ @namespace = value
41
+ end
42
+ end
43
+
44
+ def setup
45
+ {
46
+ :auto_scalings => Aerosol::AutoScaling.instances,
47
+ :deploys => Aerosol::Deploy.instances,
48
+ :launch_configurations => Aerosol::LaunchConfiguration.instances,
49
+ :sshs => Aerosol::Connection.instances,
50
+ :envs => Aerosol::Env.instances
51
+ }
52
+ end
53
+
54
+ {
55
+ :auto_scaling => Aerosol::AutoScaling,
56
+ :deploy => Aerosol::Deploy,
57
+ :launch_configuration => Aerosol::LaunchConfiguration,
58
+ :ssh => Aerosol::Connection,
59
+ :env => Aerosol::Env
60
+ }.each do |method, klass|
61
+ define_method(method) do |sym, &block|
62
+ if block.nil?
63
+ inst[:"#{method}s"][sym]
64
+ else
65
+ klass.new!(:name => sym, &block)
66
+ end
67
+ end
68
+ end
69
+
70
+ [:auto_scalings, :deploys, :launch_configurations, :sshs, :envs].each do |method|
71
+ define_method(method) do
72
+ inst[method]
73
+ end
74
+ end
75
+
76
+ module_function :inst, :load_inst, :setup, :load_file, :load_file=,
77
+ :auto_scaling, :launch_configuration, :deploy, :ssh, :git_sha,
78
+ :auto_scalings, :launch_configurations, :deploys, :sshs,
79
+ :namespace, :env, :envs
80
+ end
81
+
82
+ require 'aerosol/runner'
83
+ require 'aerosol/rake_task'