dply 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +54 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +34 -0
- data/Rakefile +2 -0
- data/bin/dplyr +76 -0
- data/bin/drake +84 -0
- data/code_dump/alt_remote_task.rb +119 -0
- data/code_dump/old_remote_task.rb +146 -0
- data/dply.gemspec +22 -0
- data/lib/dply.rb +5 -0
- data/lib/dply/cli/deploy.rb +69 -0
- data/lib/dply/config.rb +102 -0
- data/lib/dply/custom_logger.rb +29 -0
- data/lib/dply/deploy.rb +33 -0
- data/lib/dply/dplyr.rb +45 -0
- data/lib/dply/error.rb +4 -0
- data/lib/dply/ext/string.rb +33 -0
- data/lib/dply/git.rb +43 -0
- data/lib/dply/helper.rb +35 -0
- data/lib/dply/linker.rb +71 -0
- data/lib/dply/lock.rb +24 -0
- data/lib/dply/logger.rb +27 -0
- data/lib/dply/release.rb +34 -0
- data/lib/dply/remote_task.rb +139 -0
- data/lib/dply/repo.rb +31 -0
- data/lib/dply/report.rb +65 -0
- data/lib/dply/setup.rb +12 -0
- data/lib/dply/setup/default.rb +31 -0
- data/lib/dply/setup/git.rb +52 -0
- data/lib/dply/setup/release.rb +58 -0
- data/lib/dply/shared_dirs.rb +28 -0
- data/lib/dply/shell.rb +47 -0
- data/lib/dply/stages_config.rb +94 -0
- data/lib/dply/strategy.rb +12 -0
- data/lib/dply/strategy/default.rb +50 -0
- data/lib/dply/strategy/git.rb +85 -0
- data/lib/dply/tasks.rb +37 -0
- data/lib/dply/version.rb +3 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b85a4c3a9c6317f7eb0c58b3f6e2ba5126015fb6
|
4
|
+
data.tar.gz: 0b8fb6959886ba99125fe7559510dd54180c92a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dae899e3f733399352bd7ac25ba0c473e56b8fb166fba1b9478e4461393752c0dcbc8266c43ce2c6bacc6573399f53240fecd3ca8670e824f1c3fcd6068a18c8
|
7
|
+
data.tar.gz: e21cf728a5340e980e98db23705c9d36a4382dd0e431841715931cfc60c4e6176787bf70660f68a4cfb8b1297e50a273815a503de9c7de64163eb50892f24469
|
data/.gitignore
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
/.config
|
24
|
+
/coverage/
|
25
|
+
/InstalledFiles
|
26
|
+
/pkg/
|
27
|
+
/spec/reports/
|
28
|
+
/test/tmp/
|
29
|
+
/test/version_tmp/
|
30
|
+
/tmp/
|
31
|
+
|
32
|
+
## Specific to RubyMotion:
|
33
|
+
.dat*
|
34
|
+
.repl_history
|
35
|
+
build/
|
36
|
+
|
37
|
+
## Documentation cache and generated files:
|
38
|
+
/.yardoc/
|
39
|
+
/_yardoc/
|
40
|
+
/doc/
|
41
|
+
/rdoc/
|
42
|
+
|
43
|
+
## Environment normalisation:
|
44
|
+
/.bundle/
|
45
|
+
/lib/bundler/man/
|
46
|
+
|
47
|
+
# for a library or gem, you might want to ignore these files since the code is
|
48
|
+
# intended to run in multiple environments; otherwise, check them in:
|
49
|
+
# Gemfile.lock
|
50
|
+
# .ruby-version
|
51
|
+
# .ruby-gemset
|
52
|
+
|
53
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
54
|
+
.rvmrc
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Neeraj
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Dply
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'dply'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install dply
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/dply/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
30
|
+
=======
|
31
|
+
dply
|
32
|
+
====
|
33
|
+
|
34
|
+
A rake based deploy tool
|
data/Rakefile
ADDED
data/bin/dplyr
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Signal.trap("INT") { exit 1 }
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
require 'dply/error'
|
9
|
+
require 'dply/version'
|
10
|
+
require 'dply/logger'
|
11
|
+
require 'dply/dplyr'
|
12
|
+
require 'pathname'
|
13
|
+
|
14
|
+
logger = ::Dply::Logger.logger
|
15
|
+
options = {}
|
16
|
+
|
17
|
+
opts_parser = OptionParser.new do |opts|
|
18
|
+
|
19
|
+
banner = []
|
20
|
+
banner << "Usage: dplyr [global options] stage command [options] args"
|
21
|
+
banner << "Commands:"
|
22
|
+
banner << " deploy"
|
23
|
+
banner << " switch"
|
24
|
+
banner << " rollback"
|
25
|
+
|
26
|
+
banner << "Options: "
|
27
|
+
opts.banner = banner.join("\n")
|
28
|
+
|
29
|
+
opts.on("-d", "--deploy-dir [DEPLOY DIR]" , "Set deploy dir") do |d|
|
30
|
+
path = Pathname.new(d)
|
31
|
+
raise ::Dply::Error, "deploy_dir path must be absolute" if path.relative?
|
32
|
+
options[:deploy_dir] = path
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-v", "--version", "Show version") do |v|
|
36
|
+
puts ::Dply::VERSION
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("--debug", "Show debug messages") do
|
41
|
+
options[:debug] = true
|
42
|
+
logger.level = ::Logger::DEBUG
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("--trace", "Show debug messages and exception stack trace") do
|
46
|
+
options[:debug] = true
|
47
|
+
options[:trace] = true
|
48
|
+
logger.level = ::Logger::DEBUG
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
52
|
+
puts opts
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
begin
|
57
|
+
opts_parser.order!(ARGV)
|
58
|
+
deploy_dir = options[:deploy_dir] || Dir.pwd
|
59
|
+
Dir.chdir deploy_dir
|
60
|
+
|
61
|
+
stage = ARGV.shift
|
62
|
+
|
63
|
+
dplyr = ::Dply::Dplyr.new(stage, ARGV)
|
64
|
+
dplyr.run
|
65
|
+
|
66
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument, ::Dply::Error => e
|
67
|
+
cause = e.cause
|
68
|
+
if options[:trace]
|
69
|
+
puts cause
|
70
|
+
cause ? (raise cause) : (raise e)
|
71
|
+
else
|
72
|
+
logger.debug "#{cause.message}" if cause
|
73
|
+
logger.error "#{e.message}"
|
74
|
+
abort
|
75
|
+
end
|
76
|
+
end
|
data/bin/drake
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Signal.trap("INT") { exit 1 }
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
require 'dply/error'
|
9
|
+
require 'dply/version'
|
10
|
+
require 'dply/logger'
|
11
|
+
require 'pathname'
|
12
|
+
|
13
|
+
logger = ::Dply::Logger.logger
|
14
|
+
options = {}
|
15
|
+
|
16
|
+
opts_parser = OptionParser.new do |opts|
|
17
|
+
|
18
|
+
banner = []
|
19
|
+
banner << "Usage: dply [global options] command [options] args"
|
20
|
+
banner << "Commands:"
|
21
|
+
banner << " deploy"
|
22
|
+
banner << " switch"
|
23
|
+
banner << " rollback"
|
24
|
+
|
25
|
+
banner << "Options: "
|
26
|
+
opts.banner = banner.join("\n")
|
27
|
+
|
28
|
+
opts.on("-d", "--deploy-dir [DEPLOY DIR]" , "Set deploy dir") do |d|
|
29
|
+
path = Pathname.new(d)
|
30
|
+
raise ::Dply::Error, "deploy_dir path must be absolute" if path.relative?
|
31
|
+
options[:deploy_dir] = path
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-v", "--version", "Show version") do |v|
|
35
|
+
puts ::Dply::VERSION
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("--debug", "Show debug messages") do
|
40
|
+
options[:debug] = true
|
41
|
+
logger.level = ::Logger::DEBUG
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("--trace", "Show debug messages and exception stack trace") do
|
45
|
+
options[:debug] = true
|
46
|
+
options[:trace] = true
|
47
|
+
logger.level = ::Logger::DEBUG
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
51
|
+
puts opts
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
begin
|
57
|
+
opts_parser.order!(ARGV)
|
58
|
+
deploy_dir = options[:deploy_dir] || Dir.pwd
|
59
|
+
Dir.chdir deploy_dir
|
60
|
+
|
61
|
+
command = (ARGV.shift || "").to_sym
|
62
|
+
case command
|
63
|
+
when :deploy
|
64
|
+
require 'dply/cli/deploy'
|
65
|
+
cli = Dply::Cli::Deploy.new(deploy_dir, ARGV)
|
66
|
+
cli.run
|
67
|
+
when :''
|
68
|
+
puts opts_parser
|
69
|
+
else
|
70
|
+
raise Dply::Error, "no such command #{command}"
|
71
|
+
end
|
72
|
+
|
73
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument, ::Dply::Error => e
|
74
|
+
cause = e.cause
|
75
|
+
if options[:trace]
|
76
|
+
puts cause
|
77
|
+
cause ? (raise cause) : (raise e)
|
78
|
+
else
|
79
|
+
logger.debug "#{cause.message}" if cause
|
80
|
+
logger.error "#{e.message}"
|
81
|
+
abort
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'pty'
|
3
|
+
module Dply
|
4
|
+
class RemoteTask
|
5
|
+
|
6
|
+
attr_reader :hosts, :parallel_jobs, :task
|
7
|
+
|
8
|
+
def initialize(hosts, task, parallel_jobs: 1)
|
9
|
+
@hosts = hosts
|
10
|
+
@parallel_jobs = parallel_jobs
|
11
|
+
@task = task
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
init_run
|
16
|
+
@hosts_iterator = hosts.to_enum
|
17
|
+
spawn_jobs(@hosts_iterator, parallel_jobs)
|
18
|
+
# loop do
|
19
|
+
# break if @io_objects.empty?
|
20
|
+
# r,w = IO.select(@io_objects)
|
21
|
+
# r.each do |stream|
|
22
|
+
# eof_reached = handle_ready_stream stream
|
23
|
+
# spawn_jobs(hosts_iterator, 1) if eof_reached
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
print_summary
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def init_run
|
32
|
+
@io_objects = []
|
33
|
+
@io_names = {}
|
34
|
+
@wait_threads = {}
|
35
|
+
@hosts_exhausted = false
|
36
|
+
@failed_jobs = []
|
37
|
+
@successful_jobs = []
|
38
|
+
end
|
39
|
+
|
40
|
+
def spawn_job(host_info)
|
41
|
+
user = host_info[:user]
|
42
|
+
host = host_info[:host]
|
43
|
+
deploy_dir = host_info[:deploy_dir]
|
44
|
+
run_cmd = %(ssh -tt -oBatchMode=yes -l #{user} #{host} "#{cmd} -d #{deploy_dir} #{task} 2>&1")
|
45
|
+
popen(run_cmd, host_info)
|
46
|
+
# stdin, stdout, wait_thr = Open3.popen2("ssh -tt -oBatchMode=yes -l #{user} #{host} #{cmd} -d #{deploy_dir} #{task} 2>&1")
|
47
|
+
# @io_names[stdout] = host_info[:id]
|
48
|
+
# @wait_threads[host_info] = wait_thr
|
49
|
+
# @io_objects << stdout
|
50
|
+
end
|
51
|
+
|
52
|
+
def spawn_jobs(hosts_iterator, n)
|
53
|
+
return if @hosts_exhausted
|
54
|
+
n.times do
|
55
|
+
host_info = hosts_iterator.next
|
56
|
+
spawn_job(host_info)
|
57
|
+
end
|
58
|
+
rescue StopIteration
|
59
|
+
@hosts_exhausted = true
|
60
|
+
end
|
61
|
+
|
62
|
+
def cmd
|
63
|
+
"dply"
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_ready_stream(stream)
|
67
|
+
begin
|
68
|
+
host_string = "#{@io_names[stream]}".bold.grey
|
69
|
+
puts "#{host_string} #{stream.readline}"
|
70
|
+
return false
|
71
|
+
rescue EOFError,Errno::ECONNRESET, Errno::EPIPE => e
|
72
|
+
@io_objects.delete(stream)
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_summary
|
78
|
+
puts "end"
|
79
|
+
total_jobs = @wait_threads.count
|
80
|
+
successful = 0
|
81
|
+
@wait_threads.each do |host_info, thr|
|
82
|
+
status = thr.value
|
83
|
+
exit_status = status.exitstatus
|
84
|
+
if exit_status == 0
|
85
|
+
puts "success #{host_info}"
|
86
|
+
else
|
87
|
+
puts "failed #{host_info}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def popen(cmd, host_info)
|
93
|
+
Thread.abort_on_exception = true
|
94
|
+
t = Thread.new do
|
95
|
+
begin
|
96
|
+
puts "here"
|
97
|
+
r, w, pid = PTY.spawn(cmd)
|
98
|
+
host_string = "#{host_info[:id]}".bold.grey
|
99
|
+
print_pty_output r, host_string
|
100
|
+
pid, status = Process.waitpid2(pid)
|
101
|
+
spawn_jobs(@hosts_iterator, 1)
|
102
|
+
status
|
103
|
+
rescue PTY::ChildExited => e
|
104
|
+
puts "in errr"
|
105
|
+
return e.status
|
106
|
+
end
|
107
|
+
end
|
108
|
+
t.abort_on_exception = true
|
109
|
+
@wait_threads[host_info] = t
|
110
|
+
t.run
|
111
|
+
end
|
112
|
+
|
113
|
+
def print_pty_output(r, host_string)
|
114
|
+
r.each { |line| puts "#{host_string} #{line}" }
|
115
|
+
rescue Errno::EIO
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'pty'
|
2
|
+
module Dply
|
3
|
+
class RemoteTask
|
4
|
+
|
5
|
+
attr_reader :hosts, :parallel_jobs, :task
|
6
|
+
|
7
|
+
def initialize(hosts, task, parallel_jobs: 1)
|
8
|
+
@hosts = hosts
|
9
|
+
@parallel_jobs = parallel_jobs
|
10
|
+
@task = task
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
if parallel_jobs > 1
|
15
|
+
run_in_parallel
|
16
|
+
else
|
17
|
+
run_serially
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_in_parallel
|
22
|
+
init_run
|
23
|
+
hosts_iterator = hosts.to_enum
|
24
|
+
spawn_jobs(hosts_iterator, parallel_jobs)
|
25
|
+
loop do
|
26
|
+
break if @io_objects.empty?
|
27
|
+
r,w = IO.select(@io_objects)
|
28
|
+
r.each do |stream|
|
29
|
+
eof_reached = handle_ready_stream stream
|
30
|
+
spawn_jobs(hosts_iterator, 1) if eof_reached
|
31
|
+
end
|
32
|
+
end
|
33
|
+
print_summary
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_serially
|
37
|
+
hosts.each do |host_info|
|
38
|
+
puts "=== Running on #{host_info[:id]} ==="
|
39
|
+
run_cmd = remote_cmd host_info
|
40
|
+
system run_cmd
|
41
|
+
puts "=== end ==="
|
42
|
+
puts
|
43
|
+
raise ::Dply::Error, "remote deploy failed on #{host_info[:id]}" if $? != 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def init_run
|
50
|
+
@io_objects = []
|
51
|
+
@io_names = {}
|
52
|
+
@exit_statuses = {}
|
53
|
+
@hosts_exhausted = false
|
54
|
+
job_output_template
|
55
|
+
end
|
56
|
+
|
57
|
+
def spawn_job(host_info)
|
58
|
+
run_cmd = remote_cmd host_info
|
59
|
+
popen(run_cmd, host_info)
|
60
|
+
end
|
61
|
+
|
62
|
+
def spawn_jobs(hosts_iterator, n)
|
63
|
+
return if @hosts_exhausted
|
64
|
+
n.times do
|
65
|
+
host_info = hosts_iterator.next
|
66
|
+
spawn_job(host_info)
|
67
|
+
end
|
68
|
+
rescue StopIteration
|
69
|
+
@hosts_exhausted = true
|
70
|
+
end
|
71
|
+
|
72
|
+
def remote_cmd(host_info)
|
73
|
+
user = host_info[:user]
|
74
|
+
host = host_info[:host]
|
75
|
+
deploy_dir = host_info[:deploy_dir]
|
76
|
+
%(ssh -tt -oBatchMode=yes -l #{user} #{host} "dply -d #{deploy_dir} #{task} 2>&1")
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_ready_stream(stream)
|
80
|
+
host_string = "#{@io_names[stream]}"
|
81
|
+
printf @job_output_template, host_string, stream.readline
|
82
|
+
return false
|
83
|
+
rescue EOFError,Errno::ECONNRESET, Errno::EPIPE, Errno::EIO => e
|
84
|
+
@io_objects.delete(stream)
|
85
|
+
return true
|
86
|
+
end
|
87
|
+
|
88
|
+
def print_summary
|
89
|
+
total_jobs = hosts.count
|
90
|
+
run_count = @exit_statuses.count
|
91
|
+
not_run = total_jobs - run_count
|
92
|
+
failed = @exit_statuses.values.select { |v| v !=0 }.count
|
93
|
+
successful = @exit_statuses.values.select { |v| v == 0 }.count
|
94
|
+
puts "--------"
|
95
|
+
puts "Summary"
|
96
|
+
puts "--------"
|
97
|
+
puts "Succeeded: "
|
98
|
+
|
99
|
+
total_jobs = @exit_statuses.count
|
100
|
+
failed = 0
|
101
|
+
@exit_statuses.each do |host_info, status|
|
102
|
+
exit_status = status.exitstatus
|
103
|
+
if exit_status == 0
|
104
|
+
puts "#{"success".green} #{host_info[:id]} "
|
105
|
+
else
|
106
|
+
puts "#{"failed ".red} #{host_info[:id]}"
|
107
|
+
failed +=1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
if failed > 0
|
111
|
+
puts "-------"
|
112
|
+
raise ::Dply::Error, "#{failed}/#{total_jobs} jobs failed" if failed > 0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def popen(cmd, host_info)
|
117
|
+
r, w, pid = PTY.spawn(cmd)
|
118
|
+
@io_objects << r
|
119
|
+
@io_names[r] = host_info[:id]
|
120
|
+
wait_thread host_info, pid
|
121
|
+
rescue PTY::ChildExited => e
|
122
|
+
@exit_statuses[host_info] = e.status
|
123
|
+
end
|
124
|
+
|
125
|
+
def wait_thread(host_info, pid)
|
126
|
+
t = Thread.new do
|
127
|
+
pid, status = Process.waitpid2(pid)
|
128
|
+
@exit_statuses[host_info] = status
|
129
|
+
end
|
130
|
+
t.abort_on_exception = true
|
131
|
+
t.run
|
132
|
+
end
|
133
|
+
|
134
|
+
def host_id_max_width
|
135
|
+
hosts.map {|h| h[:id].size }.max
|
136
|
+
end
|
137
|
+
|
138
|
+
def job_output_template
|
139
|
+
@job_output_template ||= begin
|
140
|
+
id_template = "%-#{host_id_max_width}s".bold.grey
|
141
|
+
template = "#{id_template} %s"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|