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
@@ -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'