deis-rails 1.0.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/deis-rails.gemspec +2 -2
- data/exe/deis-rails +3 -347
- data/lib/deis/commands/copy_config.rb +13 -0
- data/lib/deis/commands/deploy.rb +85 -0
- data/lib/deis/commands/disable.rb +14 -0
- data/lib/deis/commands/enable.rb +19 -0
- data/lib/deis/commands/exists.rb +11 -0
- data/lib/deis/commands/info.rb +27 -0
- data/lib/deis/commands/version.rb +9 -0
- data/lib/deis/commands.rb +8 -0
- data/lib/deis/helpers.rb +134 -0
- data/lib/deis/runner.rb +12 -0
- data/lib/deis/version.rb +3 -0
- data/lib/deis.rb +22 -0
- metadata +13 -3
- data/lib/deis/rails/version.rb +0 -5
- data/lib/deis/rails.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84debe6de07a82d2e3c22e9d6c59017cafe09b36
|
4
|
+
data.tar.gz: 0ad15867cd9d0a67d60ec5cbf7b3f50f770cf495
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0966ba8a2470af60e17fddc9f520385f915eb8aca0515c4e6cfda2a4ce05cceb653d758f9931bd31f486e654b8a3bd2dc188f0e11720bbdc0788651a11467cd6
|
7
|
+
data.tar.gz: 996f886f110f8f52b050c97045f61315081669c8cd006f7c2871616ddd51e5eb055eecce2aacb1ca7a1243cf82beaad31f183ccbc89e52bac912322d6e2b6bcb
|
data/Gemfile.lock
CHANGED
data/deis-rails.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'deis/
|
4
|
+
require 'deis/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "deis-rails"
|
8
|
-
spec.version = Deis::
|
8
|
+
spec.version = Deis::VERSION
|
9
9
|
spec.authors = ["Jason Waldrip"]
|
10
10
|
spec.email = ["jason@waldrip.net"]
|
11
11
|
|
data/exe/deis-rails
CHANGED
@@ -1,348 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require '
|
3
|
-
|
4
|
-
|
5
|
-
require 'pry'
|
6
|
-
require 'pty'
|
7
|
-
require 'benchmark'
|
8
|
-
require 'colorize'
|
9
|
-
|
10
|
-
class DeisUtils < Struct.new(:args)
|
11
|
-
|
12
|
-
TRUTHY_STRINGS = %w(t true y yes 1).flat_map do |str|
|
13
|
-
[str.downcase, str.upcase, str.capitalize]
|
14
|
-
end.uniq
|
15
|
-
|
16
|
-
FALSEY_STRINGS = %w(f false n no 0).flat_map do |str|
|
17
|
-
[str.downcase, str.upcase, str.capitalize]
|
18
|
-
end.uniq
|
19
|
-
|
20
|
-
NonZeroExitError = Class.new(StandardError)
|
21
|
-
|
22
|
-
def run
|
23
|
-
command = args.shift
|
24
|
-
status = Helpers.run_util(command, *args)
|
25
|
-
exit status === false ? 1 : 0
|
26
|
-
end
|
27
|
-
|
28
|
-
module Helpers
|
29
|
-
|
30
|
-
module_function def run_util(name, *args)
|
31
|
-
status = false
|
32
|
-
time = Benchmark.realtime do
|
33
|
-
status = get_runner(name).new(*args).run
|
34
|
-
end
|
35
|
-
STDOUT.puts "#{$0} #{name} #{args.join ' '}` took #{seconds_to_human time}".light_blue if debug?
|
36
|
-
status
|
37
|
-
end
|
38
|
-
|
39
|
-
module_function def debug?
|
40
|
-
ENV['DEBUG'].in? TRUTHY_STRINGS
|
41
|
-
end
|
42
|
-
|
43
|
-
module_function def get_runner(name)
|
44
|
-
DeisUtils.const_get(name.gsub('-', '_').camelize)
|
45
|
-
rescue NameError
|
46
|
-
STDERR.puts "command `#{name}` does not exist!".red
|
47
|
-
exit 1
|
48
|
-
end
|
49
|
-
|
50
|
-
def app_exists?(app)
|
51
|
-
!!deis_command('info', app: app)
|
52
|
-
rescue NonZeroExitError
|
53
|
-
false
|
54
|
-
end
|
55
|
-
|
56
|
-
def status(msg)
|
57
|
-
msg = "| #{msg} |"
|
58
|
-
sep = ''.ljust(msg.size, '-')
|
59
|
-
puts "\n\e[0;92;49m#{[sep, msg, sep].join "\n"}\e[0m"
|
60
|
-
end
|
61
|
-
|
62
|
-
def databases(app)
|
63
|
-
databases = deis_command('pg:info', app: app).split('===')
|
64
|
-
databases[1..-1].map do |db|
|
65
|
-
lines = db.lines
|
66
|
-
name = lines[0].split(' ')[0]
|
67
|
-
data = lines[1..-1].join
|
68
|
-
{ 'Name' => name }.merge YAML.load(data)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def info(app)
|
73
|
-
cmd_result = deis_command(:info, app: app)
|
74
|
-
data = JSON.load cmd_result.match(/Application\r\n(?<json>\{.*\})/m)[:json]
|
75
|
-
controller_host = data['url'].split('.').tap { |p| p[p.index app] = 'deis' }.join('.')
|
76
|
-
data['git_url'] = "ssh://git@#{controller_host}:2222/#{app}.git"
|
77
|
-
data['domains'] = cmd_result.match(/Domains\r\n(?<domains>.*)/m)[:domains].lines.map(&:strip).reject(&:empty?)
|
78
|
-
data.sort.to_h
|
79
|
-
end
|
80
|
-
|
81
|
-
def shell(*commands)
|
82
|
-
flags = commands.last.is_a?(Hash) ? commands.pop : {}
|
83
|
-
command = commands.join ' '
|
84
|
-
flags.each_with_object(command) do |(k, v), c|
|
85
|
-
c << case v
|
86
|
-
when TrueClass, FalseClass
|
87
|
-
v ? " --#{k}" : ''
|
88
|
-
when String, Fixnum
|
89
|
-
" --#{k} #{v}"
|
90
|
-
else
|
91
|
-
raise ArgumentError
|
92
|
-
end
|
93
|
-
end
|
94
|
-
result = nil
|
95
|
-
time = Benchmark.realtime do
|
96
|
-
result = capture_syscall command
|
97
|
-
end
|
98
|
-
STDOUT.puts "`#{command}` took #{seconds_to_human time}".light_black if debug?
|
99
|
-
result
|
100
|
-
end
|
101
|
-
|
102
|
-
def deis_command(*cmds)
|
103
|
-
shell :deis, *cmds
|
104
|
-
end
|
105
|
-
|
106
|
-
def git_clone(url, flags = {})
|
107
|
-
shell "git clone #{url}", flags
|
108
|
-
end
|
109
|
-
|
110
|
-
def deploy(url, options = {})
|
111
|
-
ref = options.delete(:ref) || 'master'
|
112
|
-
shell "git push #{url} #{ref}:master", options
|
113
|
-
end
|
114
|
-
|
115
|
-
def capture_syscall(command)
|
116
|
-
puts "\n#{command}".light_yellow if debug?
|
117
|
-
String.new.tap do |output|
|
118
|
-
threads = []
|
119
|
-
PTY.spawn(command) do |p_out, p_in, pid|
|
120
|
-
threads << Thread.new { p_in.close }
|
121
|
-
threads << Thread.new { output << capture_output(p_out) }
|
122
|
-
Process.wait pid
|
123
|
-
threads.each(&:join)
|
124
|
-
end
|
125
|
-
raise NonZeroExitError, output unless $?.exitstatus == 0
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def capture_output(io)
|
130
|
-
output = ''
|
131
|
-
io.each do |line|
|
132
|
-
output << line
|
133
|
-
STDOUT.puts "#{line.strip}".light_black unless line.nil? || line.strip.empty? if debug?
|
134
|
-
end
|
135
|
-
ensure
|
136
|
-
return output
|
137
|
-
end
|
138
|
-
|
139
|
-
# Warn Slack for upcoming maintenance mode
|
140
|
-
def warn_for_maintenance(app)
|
141
|
-
Slacker.message(text: "WARNING: #{app} will go into maintenance mode shortly.",
|
142
|
-
username: "Deploy Bot",
|
143
|
-
channel: "#pulse-dev",
|
144
|
-
color: "warning",
|
145
|
-
icon_emoji: ":warning:")
|
146
|
-
end
|
147
|
-
|
148
|
-
# Enable Maintenance Mode on Heroku
|
149
|
-
def enable_maintenance_mode(app)
|
150
|
-
get_units!
|
151
|
-
status "#{app} is going into maintenance mode!"
|
152
|
-
Slacker.message(text: "ALERT: #{app} is going into maintenance mode!",
|
153
|
-
username: "Deploy Bot",
|
154
|
-
channel: "#pulse-dev",
|
155
|
-
color: "danger",
|
156
|
-
icon_emoji: ":bangbang:")
|
157
|
-
scale app, units.keys.each_with_object({}) { |k, h| h[k] = 0 }
|
158
|
-
end
|
159
|
-
|
160
|
-
# Disable Maintenance Mode on Heroku
|
161
|
-
def disable_maintenance_mode(app)
|
162
|
-
scale app, units
|
163
|
-
status "#{app} is no longer in maintenance mode!"
|
164
|
-
Slacker.message(text: "NOTICE: #{app} is no longer in maintenance mode.",
|
165
|
-
username: "Deploy Bot",
|
166
|
-
channel: "#pulse-dev",
|
167
|
-
color: "good",
|
168
|
-
icon_emoji: ":white_check_mark:")
|
169
|
-
end
|
170
|
-
|
171
|
-
def scale(app, opts={})
|
172
|
-
process_string = opts.map { |k, v| "#{k}=#{v}" }.join ' '
|
173
|
-
deis_command "scale #{process_string}", app: app
|
174
|
-
end
|
175
|
-
|
176
|
-
def get_units!
|
177
|
-
unit_string = deis_command('ps', app: app)
|
178
|
-
shell('foreman check').strip.sub(/.*\((.*)\)/, "\\1").split(', ').each do |unit|
|
179
|
-
units[unit] ||= unit_string.lines.select { |l| l.include? "#{unit}." }.count
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def units
|
184
|
-
@units ||= {}
|
185
|
-
end
|
186
|
-
|
187
|
-
module_function def seconds_to_human(secs)
|
188
|
-
secs = secs.round
|
189
|
-
output = ''
|
190
|
-
seconds = secs % 60
|
191
|
-
minutes = (secs - seconds) / 60
|
192
|
-
output << "#{minutes}m" if minutes > 0
|
193
|
-
output << "#{seconds}s"
|
194
|
-
end
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
class Info < Struct.new :app
|
199
|
-
include Helpers
|
200
|
-
|
201
|
-
def run
|
202
|
-
h = info(app)
|
203
|
-
status "`#{app}` Information"
|
204
|
-
output_hash h
|
205
|
-
end
|
206
|
-
|
207
|
-
def output_hash(hash, indent = 0)
|
208
|
-
hash.each do |k, v|
|
209
|
-
case v
|
210
|
-
when Hash
|
211
|
-
puts (' ' * indent) + k + ':'
|
212
|
-
output_hash(v, indent + 1)
|
213
|
-
when Array
|
214
|
-
puts (' ' * indent) + k + ':', *v.map { |i| (' ' * (indent + 1)) + i.to_s }
|
215
|
-
else
|
216
|
-
puts (' ' * indent) + "#{k}: #{v}"
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
class Exists < Struct.new :app
|
223
|
-
include Helpers
|
224
|
-
|
225
|
-
def run
|
226
|
-
app_exists?(app)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
class CopyConfig < Struct.new :source_app, :dest_app
|
231
|
-
include Helpers
|
232
|
-
|
233
|
-
def run
|
234
|
-
system <<-sh
|
235
|
-
deis config --app #{source_app} --oneline | xargs deis config:set --app #{dest_app}
|
236
|
-
sh
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
class Disable < Struct.new :app
|
241
|
-
include Helpers
|
242
|
-
|
243
|
-
def run
|
244
|
-
status "Disabling App: #{app}"
|
245
|
-
units = shell('foreman check').strip.sub(/.*\((.*)\)/, "\\1").split(', ')
|
246
|
-
scale app, units.each_with_object({}) { |k, h| h[k] = 0 }
|
247
|
-
end
|
248
|
-
|
249
|
-
end
|
250
|
-
|
251
|
-
class Enable < Struct.new :app
|
252
|
-
include Helpers
|
253
|
-
|
254
|
-
def run
|
255
|
-
status "Enabling App: #{app}"
|
256
|
-
get_units!
|
257
|
-
if units.any? { |_, v| v > 0 }
|
258
|
-
status "App Already enabled!"
|
259
|
-
return
|
260
|
-
end
|
261
|
-
scale app, units.keys.each_with_object({}) { |k, h| h[k] = 1 }
|
262
|
-
status "App enabled!"
|
263
|
-
end
|
264
|
-
|
265
|
-
end
|
266
|
-
|
267
|
-
# Deploy a deis repo
|
268
|
-
class Deploy < Struct.new(:app, :ref)
|
269
|
-
include Helpers
|
270
|
-
|
271
|
-
LAST_MIGRATION_CMD = %{bundle exec rake db:migrate:status}
|
272
|
-
MIGRATION_REGEX = /up\s+(?<migration>[0-9]{8}[0-9])+/
|
273
|
-
|
274
|
-
attr_reader :worker_count
|
275
|
-
attr_reader :web_count
|
276
|
-
|
277
|
-
def run
|
278
|
-
status "Deploying `#{ref}` to `#{app}` on Deis"
|
279
|
-
precheck_migrations!
|
280
|
-
output = deploy info['git_url'], ref: ref
|
281
|
-
if output.include? 'Another git push is ongoing'
|
282
|
-
sleep 60 # one minute
|
283
|
-
return run_util 'deploy', app, ref
|
284
|
-
end
|
285
|
-
run_migrations! if needs_migrations?
|
286
|
-
rescue NonZeroExitError => e
|
287
|
-
raise e unless e.message.include? 'Another git push is ongoing'
|
288
|
-
remove_instance_variable :@needs_migrations
|
289
|
-
sleep 60
|
290
|
-
retry
|
291
|
-
end
|
292
|
-
|
293
|
-
private
|
294
|
-
|
295
|
-
def info
|
296
|
-
@info ||= super(app)
|
297
|
-
end
|
298
|
-
|
299
|
-
def run_migrations!
|
300
|
-
# Todo: put the site in a readonly state but NEVER in maintenance mode
|
301
|
-
status 'Running Migrations'
|
302
|
-
deis_command('run rake "db:migrate"', app: app)
|
303
|
-
end
|
304
|
-
|
305
|
-
def precheck_migrations!
|
306
|
-
needs_migrations?
|
307
|
-
end
|
308
|
-
|
309
|
-
def needs_migrations?
|
310
|
-
return false if TRUTHY_STRINGS.include? ENV['SKIP_MIGRATIONS']
|
311
|
-
return true if TRUTHY_STRINGS.include? ENV['FORCE_MIGRATIONS']
|
312
|
-
@needs_migrations ||= begin
|
313
|
-
status 'Checking Migration Status'
|
314
|
-
(local_migration != remote_migration).tap do |val|
|
315
|
-
if val
|
316
|
-
puts 'Database out of date, Migrations are Required'
|
317
|
-
else
|
318
|
-
puts 'Database is up to date'
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
def remote_migration
|
325
|
-
@remote_migration ||= sha_from_migration_status deis_command("run #{LAST_MIGRATION_CMD}", app: app)
|
326
|
-
rescue NonZeroExitError
|
327
|
-
'error'
|
328
|
-
end
|
329
|
-
|
330
|
-
def local_migration
|
331
|
-
@local_migration ||= sha_from_migration_status shell(LAST_MIGRATION_CMD)
|
332
|
-
rescue NonZeroExitError
|
333
|
-
'error'
|
334
|
-
end
|
335
|
-
|
336
|
-
def sha_from_migration_status(result)
|
337
|
-
lines = result.lines.reject { |line| line.include? '**** NO FILE ****' }
|
338
|
-
migration_lines = lines.map(&:strip).select { |line| line =~ MIGRATION_REGEX }
|
339
|
-
Digest::SHA2.hexdigest migration_lines.join
|
340
|
-
end
|
341
|
-
|
342
|
-
end
|
343
|
-
|
344
|
-
end
|
345
|
-
|
346
|
-
Bundler.with_clean_env do
|
347
|
-
DeisUtils.new(ARGV).run
|
348
|
-
end
|
2
|
+
require 'bundler/setup'
|
3
|
+
require_relative '../lib/deis'
|
4
|
+
Deis::Runner.new(ARGV).run
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Deis
|
2
|
+
module Commands
|
3
|
+
# Deploy a deis repo
|
4
|
+
class Deploy < Struct.new(:app, :ref)
|
5
|
+
include Helpers
|
6
|
+
|
7
|
+
LAST_MIGRATION_CMD = %{bundle exec rake db:migrate:status}
|
8
|
+
MIGRATION_REGEX = /up\s+(?<migration>[0-9]{8}[0-9])+/
|
9
|
+
|
10
|
+
attr_reader :worker_count
|
11
|
+
attr_reader :web_count
|
12
|
+
|
13
|
+
def run
|
14
|
+
status "Deploying `#{ref}` to `#{app}` on Deis"
|
15
|
+
precheck_migrations!
|
16
|
+
output = deploy info['git_url'], ref: ref
|
17
|
+
if output.include? 'Another git push is ongoing'
|
18
|
+
sleep 60 # one minute
|
19
|
+
return run_util 'deploy', app, ref
|
20
|
+
end
|
21
|
+
run_migrations! if needs_migrations?
|
22
|
+
rescue NonZeroExitError => e
|
23
|
+
raise e unless e.message.include? 'Another git push is ongoing'
|
24
|
+
remove_instance_variable :@needs_migrations
|
25
|
+
sleep 60
|
26
|
+
retry
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def debug?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def info
|
36
|
+
@info ||= super(app)
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_migrations!
|
40
|
+
# Todo: put the site in a readonly state but NEVER in maintenance mode
|
41
|
+
status 'Running Migrations'
|
42
|
+
deis_command(:run, 'rake "db:create"', app: app)
|
43
|
+
deis_command(:run, 'rake "db:migrate"', app: app)
|
44
|
+
end
|
45
|
+
|
46
|
+
def precheck_migrations!
|
47
|
+
needs_migrations?
|
48
|
+
end
|
49
|
+
|
50
|
+
def needs_migrations?
|
51
|
+
return false if TRUTHY_STRINGS.include? ENV['SKIP_MIGRATIONS']
|
52
|
+
return true if TRUTHY_STRINGS.include? ENV['FORCE_MIGRATIONS']
|
53
|
+
@needs_migrations ||= begin
|
54
|
+
status 'Checking Migration Status'
|
55
|
+
(local_migration != remote_migration).tap do |val|
|
56
|
+
if val
|
57
|
+
puts 'Database out of date, Migrations are Required'
|
58
|
+
else
|
59
|
+
puts 'Database is up to date'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def remote_migration
|
66
|
+
@remote_migration ||= sha_from_migration_status deis_command("run #{LAST_MIGRATION_CMD}", app: app)
|
67
|
+
rescue NonZeroExitError
|
68
|
+
'error'
|
69
|
+
end
|
70
|
+
|
71
|
+
def local_migration
|
72
|
+
@local_migration ||= sha_from_migration_status shell(LAST_MIGRATION_CMD)
|
73
|
+
rescue NonZeroExitError
|
74
|
+
'error'
|
75
|
+
end
|
76
|
+
|
77
|
+
def sha_from_migration_status(result)
|
78
|
+
lines = result.lines.reject { |line| line.include? '**** NO FILE ****' }
|
79
|
+
migration_lines = lines.map(&:strip).select { |line| line =~ MIGRATION_REGEX }
|
80
|
+
Digest::SHA2.hexdigest migration_lines.join
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Deis
|
2
|
+
module Commands
|
3
|
+
class Disable < Struct.new :app
|
4
|
+
include Helpers
|
5
|
+
|
6
|
+
def run
|
7
|
+
status "Disabling App: #{app}"
|
8
|
+
units = shell('foreman check').strip.sub(/.*\((.*)\)/, "\\1").split(', ')
|
9
|
+
scale app, units.each_with_object({}) { |k, h| h[k] = 0 }
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Deis
|
2
|
+
module Commands
|
3
|
+
class Enable < Struct.new :app
|
4
|
+
include Helpers
|
5
|
+
|
6
|
+
def run
|
7
|
+
status "Enabling App: #{app}"
|
8
|
+
get_units!
|
9
|
+
if units.any? { |_, v| v > 0 }
|
10
|
+
status "App Already enabled!"
|
11
|
+
return
|
12
|
+
end
|
13
|
+
scale app, units.keys.each_with_object({}) { |k, h| h[k] = 1 }
|
14
|
+
status "App enabled!"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Deis
|
2
|
+
module Commands
|
3
|
+
class Info < Struct.new :app
|
4
|
+
include Helpers
|
5
|
+
|
6
|
+
def run
|
7
|
+
h = info(app)
|
8
|
+
status "`#{app}` Information"
|
9
|
+
output_hash h
|
10
|
+
end
|
11
|
+
|
12
|
+
def output_hash(hash, indent = 0)
|
13
|
+
hash.each do |k, v|
|
14
|
+
case v
|
15
|
+
when Hash
|
16
|
+
puts (' ' * indent) + k + ':'
|
17
|
+
output_hash(v, indent + 1)
|
18
|
+
when Array
|
19
|
+
puts (' ' * indent) + k + ':', *v.map { |i| (' ' * (indent + 1)) + i.to_s }
|
20
|
+
else
|
21
|
+
puts (' ' * indent) + "#{k}: #{v}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/deis/helpers.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'pty'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
module Deis
|
5
|
+
module Helpers
|
6
|
+
|
7
|
+
def run_util(name, *args)
|
8
|
+
status = false
|
9
|
+
time = Benchmark.realtime do
|
10
|
+
status = get_runner(name).new(*args).run
|
11
|
+
end
|
12
|
+
STDOUT.puts "#{$0} #{name} #{args.join ' '}` took #{seconds_to_human time}".light_blue if debug?
|
13
|
+
status
|
14
|
+
end
|
15
|
+
|
16
|
+
def debug?
|
17
|
+
ENV['DEBUG'].in? TRUTHY_STRINGS
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_runner(name)
|
21
|
+
Deis::Commands.const_get(name.gsub('-', '_').camelize)
|
22
|
+
rescue NameError
|
23
|
+
STDERR.puts "command `#{name}` does not exist!".red
|
24
|
+
exit 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def app_exists?(app)
|
28
|
+
!!deis_command('info', app: app)
|
29
|
+
rescue NonZeroExitError
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def status(msg)
|
34
|
+
msg = "| #{msg} |"
|
35
|
+
sep = ''.ljust(msg.size, '-')
|
36
|
+
puts "\n\e[0;92;49m#{[sep, msg, sep].join "\n"}\e[0m"
|
37
|
+
end
|
38
|
+
|
39
|
+
def info(app)
|
40
|
+
cmd_result = deis_command(:info, app: app)
|
41
|
+
data = JSON.load cmd_result.match(/Application\r\n(?<json>\{.*\})/m)[:json]
|
42
|
+
controller_host = data['url'].split('.').tap { |p| p[p.index app] = 'deis' }.join('.')
|
43
|
+
data['git_url'] = "ssh://git@#{controller_host}:2222/#{app}.git"
|
44
|
+
data['domains'] = cmd_result.match(/Domains\r\n(?<domains>.*)/m)[:domains].lines.map(&:strip).reject(&:empty?)
|
45
|
+
data.sort.to_h
|
46
|
+
end
|
47
|
+
|
48
|
+
def shell(*commands)
|
49
|
+
Bundler.with_clean_env do
|
50
|
+
flags = commands.last.is_a?(Hash) ? commands.pop : {}
|
51
|
+
command = commands.join ' '
|
52
|
+
flags.each_with_object(command) do |(k, v), c|
|
53
|
+
c << case v
|
54
|
+
when TrueClass, FalseClass
|
55
|
+
v ? " --#{k}" : ''
|
56
|
+
when String, Fixnum
|
57
|
+
" --#{k} #{v}"
|
58
|
+
else
|
59
|
+
raise ArgumentError
|
60
|
+
end
|
61
|
+
end
|
62
|
+
result = nil
|
63
|
+
time = Benchmark.realtime do
|
64
|
+
result = capture_syscall command
|
65
|
+
end
|
66
|
+
STDOUT.puts "`#{command}` took #{seconds_to_human time}".light_black if debug?
|
67
|
+
end
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
def deis_command(*cmds)
|
72
|
+
shell :deis, *cmds
|
73
|
+
end
|
74
|
+
|
75
|
+
def git_clone(url, flags = {})
|
76
|
+
shell "git clone #{url}", flags
|
77
|
+
end
|
78
|
+
|
79
|
+
def deploy(url, options = {})
|
80
|
+
ref = options.delete(:ref) || 'master'
|
81
|
+
shell "git push #{url} #{ref}:master", options
|
82
|
+
end
|
83
|
+
|
84
|
+
def capture_syscall(command)
|
85
|
+
puts "\n#{command}".light_yellow if debug?
|
86
|
+
String.new.tap do |output|
|
87
|
+
threads = []
|
88
|
+
PTY.spawn(command) do |p_out, p_in, pid|
|
89
|
+
threads << Thread.new { p_in.close }
|
90
|
+
threads << Thread.new { output << capture_output(p_out) }
|
91
|
+
Process.wait pid
|
92
|
+
threads.each(&:join)
|
93
|
+
end
|
94
|
+
raise NonZeroExitError, output unless $?.exitstatus == 0
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def capture_output(io)
|
99
|
+
output = ''
|
100
|
+
io.each do |line|
|
101
|
+
output << line
|
102
|
+
STDOUT.puts "#{line.strip}".light_black unless line.nil? || line.strip.empty? if debug?
|
103
|
+
end
|
104
|
+
ensure
|
105
|
+
return output
|
106
|
+
end
|
107
|
+
|
108
|
+
def scale(app, opts={})
|
109
|
+
process_string = opts.map { |k, v| "#{k}=#{v}" }.join ' '
|
110
|
+
deis_command "scale #{process_string}", app: app
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_units!
|
114
|
+
unit_string = deis_command('ps', app: app)
|
115
|
+
shell('foreman check').strip.sub(/.*\((.*)\)/, "\\1").split(', ').each do |unit|
|
116
|
+
units[unit] ||= unit_string.lines.select { |l| l.include? "#{unit}." }.count
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def units
|
121
|
+
@units ||= {}
|
122
|
+
end
|
123
|
+
|
124
|
+
module_function def seconds_to_human(secs)
|
125
|
+
secs = secs.round
|
126
|
+
output = ''
|
127
|
+
seconds = secs % 60
|
128
|
+
minutes = (secs - seconds) / 60
|
129
|
+
output << "#{minutes}m" if minutes > 0
|
130
|
+
output << "#{seconds}s"
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
data/lib/deis/runner.rb
ADDED
data/lib/deis/version.rb
ADDED
data/lib/deis.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'pry'
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
module Deis
|
6
|
+
|
7
|
+
autoload :Helpers, 'deis/helpers'
|
8
|
+
autoload :Commands, 'deis/commands'
|
9
|
+
autoload :Runner, 'deis/runner'
|
10
|
+
autoload :VERSION, 'deis/version'
|
11
|
+
|
12
|
+
TRUTHY_STRINGS = %w(t true y yes 1).flat_map do |str|
|
13
|
+
[str.downcase, str.upcase, str.capitalize]
|
14
|
+
end.uniq
|
15
|
+
|
16
|
+
FALSEY_STRINGS = %w(f false n no 0).flat_map do |str|
|
17
|
+
[str.downcase, str.upcase, str.capitalize]
|
18
|
+
end.uniq
|
19
|
+
|
20
|
+
NonZeroExitError = Class.new(StandardError)
|
21
|
+
|
22
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deis-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Waldrip
|
@@ -102,8 +102,18 @@ files:
|
|
102
102
|
- bin/setup
|
103
103
|
- deis-rails.gemspec
|
104
104
|
- exe/deis-rails
|
105
|
-
- lib/deis
|
106
|
-
- lib/deis/
|
105
|
+
- lib/deis.rb
|
106
|
+
- lib/deis/commands.rb
|
107
|
+
- lib/deis/commands/copy_config.rb
|
108
|
+
- lib/deis/commands/deploy.rb
|
109
|
+
- lib/deis/commands/disable.rb
|
110
|
+
- lib/deis/commands/enable.rb
|
111
|
+
- lib/deis/commands/exists.rb
|
112
|
+
- lib/deis/commands/info.rb
|
113
|
+
- lib/deis/commands/version.rb
|
114
|
+
- lib/deis/helpers.rb
|
115
|
+
- lib/deis/runner.rb
|
116
|
+
- lib/deis/version.rb
|
107
117
|
homepage: https://github.com/brandfolder/deis-rails
|
108
118
|
licenses:
|
109
119
|
- MIT
|
data/lib/deis/rails/version.rb
DELETED