deis-rails 1.0.1 → 1.0.3
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.
- 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