remote_run 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/remote_run/configuration.rb +98 -0
- data/lib/remote_run/host.rb +91 -94
- data/lib/remote_run/runner.rb +108 -186
- data/lib/remote_run/task.rb +10 -0
- data/lib/remote_run/version.rb +1 -1
- data/lib/remote_run.rb +5 -0
- data/spec/remote_run/configuration_spec.rb +92 -0
- data/spec/remote_run/host_spec.rb +9 -9
- data/spec/remote_run/runner_spec.rb +0 -0
- data/spec/spec_helper.rb +3 -2
- metadata +15 -9
@@ -0,0 +1,98 @@
|
|
1
|
+
module RemoteRun
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :remote_path, :local_path, :login_as, :exclude, :temp_path, :quiet
|
4
|
+
attr_reader :local_hostname, :identifier, :start_time
|
5
|
+
attr_reader :host_manager, :task_manager
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@task_manager = TaskManager.new
|
9
|
+
@host_manager = HostManager.new
|
10
|
+
|
11
|
+
@local_path = Dir.getwd
|
12
|
+
@login_as = `whoami`.strip
|
13
|
+
@remote_path = "/tmp/remote"
|
14
|
+
@exclude = []
|
15
|
+
@temp_path = "/tmp/remote"
|
16
|
+
@quiet = false
|
17
|
+
@start_time = Time.now
|
18
|
+
|
19
|
+
# used in the runner
|
20
|
+
@identifier = `echo $RANDOM`.strip
|
21
|
+
@local_hostname = `hostname`.strip
|
22
|
+
|
23
|
+
$runner = self
|
24
|
+
yield self
|
25
|
+
end
|
26
|
+
|
27
|
+
def hosts
|
28
|
+
@host_manager.hosts
|
29
|
+
end
|
30
|
+
|
31
|
+
def tasks
|
32
|
+
@task_manager.tasks
|
33
|
+
end
|
34
|
+
|
35
|
+
def hosts=(hostnames)
|
36
|
+
hostnames.each do |hostname|
|
37
|
+
@host_manager.add(Host.new(hostname))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def tasks=(shell_commands)
|
42
|
+
shell_commands.each do |shell_command|
|
43
|
+
@task_manager.add(Task.new(shell_command))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def run
|
48
|
+
Runner.new(self).run
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
class HostManager
|
54
|
+
def initialize(&block)
|
55
|
+
@hosts = []
|
56
|
+
end
|
57
|
+
|
58
|
+
def add(host)
|
59
|
+
Thread.new do
|
60
|
+
if host.is_up?
|
61
|
+
@hosts << host
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def hosts
|
67
|
+
while @hosts.empty?
|
68
|
+
sleep(0.5)
|
69
|
+
end
|
70
|
+
@hosts
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class TaskManager
|
75
|
+
attr_reader :tasks
|
76
|
+
|
77
|
+
def initialize
|
78
|
+
@tasks = []
|
79
|
+
end
|
80
|
+
|
81
|
+
def add(task)
|
82
|
+
@tasks.push(task)
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_task
|
86
|
+
@tasks.shift
|
87
|
+
end
|
88
|
+
|
89
|
+
def count
|
90
|
+
@tasks.length
|
91
|
+
end
|
92
|
+
|
93
|
+
def has_more_tasks?
|
94
|
+
@tasks.size > 0
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/remote_run/host.rb
CHANGED
@@ -1,128 +1,125 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def lock
|
14
|
-
unless locked?
|
15
|
-
@lock_file.get && locked_by_me?
|
1
|
+
module RemoteRun
|
2
|
+
class Host
|
3
|
+
FAIL = 1
|
4
|
+
PASS = 0
|
5
|
+
SSH_CONFIG = " -o ControlMaster=auto -o ControlPath=~/.ssh/master-%l-%r@%h:%p -o NumberOfPasswordPrompts=0 -o StrictHostKeyChecking=no -4 "
|
6
|
+
attr_reader :hostname
|
7
|
+
|
8
|
+
def initialize(hostname)
|
9
|
+
@hostname = hostname
|
10
|
+
@lock_file = LockFile.new(@hostname, $runner.local_hostname, $runner.identifier)
|
16
11
|
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def unlock
|
20
|
-
@lock_file.release
|
21
|
-
end
|
22
|
-
|
23
|
-
def run(task)
|
24
|
-
Runner.log("Running '#{task}' on #{@hostname}", :white)
|
25
|
-
command = %Q{ssh #{SSH_CONFIG} #{ssh_host_and_user} 'cd #{$runner.remote_path}; #{task}' 2>&1}
|
26
|
-
system(command)
|
27
|
-
$?.exitstatus
|
28
|
-
end
|
29
12
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
if system(%{rsync --delete --delete-excluded #{excludes.join(" ")} --rsh='ssh #{SSH_CONFIG}' --timeout=60 -a #{$runner.temp_path}/ #{ssh_host_and_user}:#{$runner.remote_path}/})
|
35
|
-
Runner.log("Finished copying to #{@hostname}", :green)
|
36
|
-
return true
|
37
|
-
else
|
38
|
-
Runner.log("rsync failed on #{@hostname}.", :red)
|
39
|
-
return false
|
13
|
+
def lock
|
14
|
+
unless locked?
|
15
|
+
@lock_file.get && locked_by_me?
|
16
|
+
end
|
40
17
|
end
|
41
|
-
end
|
42
18
|
|
43
|
-
|
44
|
-
|
45
|
-
if result == "success"
|
46
|
-
Runner.log("#{@hostname} is up", :green)
|
47
|
-
return true
|
48
|
-
else
|
49
|
-
Runner.log("#{@hostname} is down: #{result}", :red)
|
50
|
-
return false
|
19
|
+
def unlock
|
20
|
+
@lock_file.release
|
51
21
|
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def start_ssh_master_connection
|
55
|
-
system("ssh #{SSH_CONFIG} #{ssh_host_and_user} -M &> /dev/null")
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def ssh_host_and_user
|
61
|
-
"#{$runner.login_as}@#{@hostname}"
|
62
|
-
end
|
63
|
-
|
64
|
-
def locked?
|
65
|
-
@lock_file.locked?
|
66
|
-
end
|
67
22
|
|
68
|
-
|
69
|
-
|
70
|
-
|
23
|
+
def run(task)
|
24
|
+
command = %Q{ssh #{SSH_CONFIG} #{ssh_host_and_user} 'cd #{$runner.remote_path}; #{task}' 2>&1}
|
25
|
+
system(command)
|
26
|
+
$?.exitstatus
|
27
|
+
end
|
71
28
|
|
72
|
-
|
73
|
-
|
29
|
+
def copy_codebase
|
30
|
+
system("ssh #{SSH_CONFIG} #{ssh_host_and_user} 'mkdir -p #{$runner.remote_path}'")
|
31
|
+
excludes = $runner.exclude.map { |dir| "--exclude '#{dir}'"}
|
32
|
+
if system(%{rsync --delete --delete-excluded #{excludes.join(" ")} --rsh='ssh #{SSH_CONFIG}' --timeout=60 -a #{$runner.temp_path}/ #{ssh_host_and_user}:#{$runner.remote_path}/})
|
33
|
+
return true
|
34
|
+
else
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
end
|
74
38
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
|
39
|
+
def is_up?
|
40
|
+
result = `ssh #{SSH_CONFIG} -o ConnectTimeout=2 #{ssh_host_and_user} "echo 'success'" 2>/dev/null`.strip
|
41
|
+
if result == "success"
|
42
|
+
return true
|
43
|
+
else
|
44
|
+
return false
|
45
|
+
end
|
79
46
|
end
|
80
47
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
48
|
+
def start_ssh_master_connection
|
49
|
+
fork do
|
50
|
+
system("ssh #{SSH_CONFIG} #{ssh_host_and_user} -M &> /dev/null")
|
84
51
|
end
|
85
52
|
end
|
86
53
|
|
54
|
+
private
|
55
|
+
|
56
|
+
def ssh_host_and_user
|
57
|
+
"#{$runner.login_as}@#{@hostname}"
|
58
|
+
end
|
59
|
+
|
87
60
|
def locked?
|
88
|
-
@
|
61
|
+
@lock_file.locked?
|
89
62
|
end
|
90
63
|
|
91
64
|
def locked_by_me?
|
92
|
-
@
|
65
|
+
@lock_file.locked_by_me?
|
93
66
|
end
|
94
67
|
|
95
|
-
|
96
|
-
|
97
|
-
end
|
68
|
+
class LockFile
|
69
|
+
FILE = "/tmp/remote-run-lock"
|
98
70
|
|
99
|
-
|
100
|
-
|
101
|
-
@
|
71
|
+
def initialize(remote_hostname, local_hostname, unique_run_marker)
|
72
|
+
@filename = FILE
|
73
|
+
@locker = "#{local_hostname}-#{unique_run_marker}"
|
74
|
+
@remote_file = RemoteFile.new(remote_hostname)
|
102
75
|
end
|
103
76
|
|
104
|
-
def
|
105
|
-
|
77
|
+
def release
|
78
|
+
if locked_by_me?
|
79
|
+
@remote_file.delete(@filename)
|
80
|
+
end
|
106
81
|
end
|
107
82
|
|
108
|
-
def
|
109
|
-
|
83
|
+
def locked?
|
84
|
+
@remote_file.exist?(@filename)
|
110
85
|
end
|
111
86
|
|
112
|
-
def
|
113
|
-
|
87
|
+
def locked_by_me?
|
88
|
+
@remote_file.exist?(@filename) && @remote_file.read(@filename).strip == @locker
|
114
89
|
end
|
115
90
|
|
116
|
-
def
|
117
|
-
|
91
|
+
def get
|
92
|
+
@remote_file.write(@filename, @locker)
|
118
93
|
end
|
119
94
|
|
120
|
-
|
121
|
-
|
122
|
-
|
95
|
+
class RemoteFile
|
96
|
+
def initialize(hostname)
|
97
|
+
@hostname = hostname
|
98
|
+
end
|
99
|
+
|
100
|
+
def exist?(file_path)
|
101
|
+
run_and_test("test -f #{file_path}")
|
102
|
+
end
|
103
|
+
|
104
|
+
def read(file_path)
|
105
|
+
run("test -e #{file_path} && cat #{file_path}")
|
106
|
+
end
|
107
|
+
|
108
|
+
def write(file_path, text)
|
109
|
+
run_and_test("test -e #{file_path} || echo #{text} > #{file_path}")
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete(file_path)
|
113
|
+
run_and_test("rm -f #{file_path}")
|
114
|
+
end
|
115
|
+
|
116
|
+
def run(command)
|
117
|
+
`ssh #{Host::SSH_CONFIG} #{$runner.login_as}@#{@hostname} '#{command};'`.strip
|
118
|
+
end
|
123
119
|
|
124
|
-
|
125
|
-
|
120
|
+
def run_and_test(command)
|
121
|
+
system("ssh #{Host::SSH_CONFIG} #{$runner.login_as}@#{@hostname} '#{command}' 2>/dev/null")
|
122
|
+
end
|
126
123
|
end
|
127
124
|
end
|
128
125
|
end
|
data/lib/remote_run/runner.rb
CHANGED
@@ -1,233 +1,155 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# config options
|
12
|
-
@local_path = Dir.getwd
|
13
|
-
@login_as = `whoami`.strip
|
14
|
-
@remote_path = "/tmp/remote"
|
15
|
-
@exclude = []
|
16
|
-
@temp_path = "/tmp/remote"
|
17
|
-
|
18
|
-
# used in the runner
|
19
|
-
@identifier = `echo $RANDOM`.strip
|
20
|
-
@local_hostname = `hostname`.strip
|
21
|
-
@results = []
|
22
|
-
@children = []
|
23
|
-
@failed = []
|
24
|
-
@last_timestamp = Time.now.strftime("%S")[0]
|
25
|
-
|
26
|
-
$runner = self
|
27
|
-
yield self
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.run(&block)
|
31
|
-
@@start_time = Time.now
|
32
|
-
runner = new(&block)
|
33
|
-
runner.run
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.run_time
|
37
|
-
minutes = ((Time.now - @@start_time) / 60).to_i
|
38
|
-
seconds = ((Time.now - @@start_time) % 60).to_i
|
39
|
-
"#{minutes}:#{"%02d" % seconds}"
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.log(message, color = :yellow)
|
43
|
-
highline = HighLine.new
|
44
|
-
system("stty #{@@stty_config} 2>/dev/null")
|
45
|
-
highline.say(highline.color("[Remote :: #{$runner.identifier} :: #{run_time}] #{message}", color))
|
46
|
-
end
|
47
|
-
|
48
|
-
def hosts
|
49
|
-
@host_manager.all
|
50
|
-
end
|
1
|
+
module RemoteRun
|
2
|
+
class Runner
|
3
|
+
def initialize(configuration)
|
4
|
+
@configuration = configuration
|
5
|
+
@results = []
|
6
|
+
@children = []
|
7
|
+
@failed = []
|
8
|
+
@stty_config = `stty -g`
|
9
|
+
@last_timestamp = Time.now.strftime("%S")[0]
|
10
|
+
@hosts = []
|
51
11
|
|
52
|
-
|
53
|
-
|
54
|
-
@
|
12
|
+
@task_manager = @configuration.task_manager
|
13
|
+
@host_manager = @configuration.host_manager
|
14
|
+
@starting_number_of_tasks = @task_manager.count
|
55
15
|
end
|
56
|
-
end
|
57
16
|
|
58
|
-
|
59
|
-
|
60
|
-
|
17
|
+
def run
|
18
|
+
setup_unlock_on_exit
|
19
|
+
start_ssh_master_connections
|
20
|
+
sync_working_copy_to_temp_location
|
21
|
+
start_tasks
|
22
|
+
wait_for_tasks_to_finish
|
23
|
+
handle_results
|
61
24
|
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def run
|
65
|
-
@host_manager.unlock_on_exit
|
66
|
-
@host_manager.start_ssh_master_connections
|
67
|
-
sync_working_copy_to_temp_location
|
68
|
-
hosts = []
|
69
|
-
|
70
|
-
Runner.log("Starting tasks... #{Time.now}")
|
71
25
|
|
72
|
-
|
73
|
-
while @task_manager.has_more_tasks?
|
74
|
-
hosts = @host_manager.hosts.dup if hosts.empty?
|
26
|
+
private
|
75
27
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
task = @task_manager.find_task
|
83
|
-
@children << fork do
|
84
|
-
begin
|
85
|
-
this_host = host.dup
|
86
|
-
unless this_host.copy_codebase
|
87
|
-
@task_manager.add(task)
|
88
|
-
status = 0
|
89
|
-
end
|
90
|
-
status = this_host.run(task)
|
91
|
-
host.unlock
|
92
|
-
Runner.log("#{host.hostname} failed.", :red) if status != 0
|
93
|
-
rescue Errno::EPIPE
|
94
|
-
Runner.log("broken pipe on #{host.hostname}...")
|
95
|
-
ensure
|
96
|
-
Process.exit!(status)
|
97
|
-
end
|
28
|
+
def setup_unlock_on_exit
|
29
|
+
at_exit do
|
30
|
+
@configuration.hosts.each do |host|
|
31
|
+
begin
|
32
|
+
host.unlock
|
33
|
+
rescue Errno::EPIPE
|
98
34
|
end
|
99
35
|
end
|
100
36
|
end
|
101
37
|
end
|
102
38
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
display_log
|
107
|
-
check_for_finished
|
108
|
-
end
|
109
|
-
|
110
|
-
failed_tasks = @results.select { |result| result != 0 }
|
111
|
-
status_code = if failed_tasks.length == 0
|
112
|
-
Runner.log("Task passed.", :green)
|
113
|
-
Host::PASS
|
114
|
-
else
|
115
|
-
Runner.log("#{failed_tasks.length} task(s) failed.", :red)
|
116
|
-
Host::FAIL
|
117
|
-
end
|
118
|
-
|
119
|
-
Runner.log("Total Time: #{self.class.run_time} minutes.")
|
120
|
-
status_code
|
121
|
-
end
|
122
|
-
|
123
|
-
def check_for_finished
|
124
|
-
@children.each do |child_pid|
|
125
|
-
if Process.waitpid(child_pid, Process::WNOHANG)
|
126
|
-
if $?.exitstatus != 0
|
127
|
-
@failed << child_pid
|
128
|
-
end
|
129
|
-
|
130
|
-
@results << $?.exitstatus
|
131
|
-
@children.delete(child_pid)
|
39
|
+
def start_ssh_master_connections
|
40
|
+
@configuration.hosts.each do |host|
|
41
|
+
host.start_ssh_master_connection
|
132
42
|
end
|
133
43
|
end
|
134
|
-
sleep(0.5)
|
135
|
-
end
|
136
|
-
|
137
|
-
private
|
138
44
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
def display_log
|
147
|
-
now = Time.now.strftime("%S")[0]
|
148
|
-
unless now == @last_timestamp
|
149
|
-
display_status("Waiting on #{@task_manager.count} of #{@starting_number_of_tasks} tasks to start.") if @task_manager.count > 0
|
150
|
-
display_status("Waiting on #{@children.length} of #{@starting_number_of_tasks - @task_manager.count} started tasks to finish. #{@failed.size} failed.") if @children.length > 0
|
151
|
-
$stdout.print("\n\n")
|
152
|
-
$stdout.flush
|
153
|
-
@last_timestamp = now
|
45
|
+
def sync_working_copy_to_temp_location
|
46
|
+
log("Creating temporary copy of #{@configuration.local_path} in #{@configuration.temp_path}...")
|
47
|
+
excludes = @configuration.exclude.map { |dir| "--exclude '#{dir}'"}
|
48
|
+
system("rsync --delete --delete-excluded #{excludes.join(" ")} -aq #{@configuration.local_path}/ #{@configuration.temp_path}/")
|
49
|
+
log("Done.")
|
154
50
|
end
|
155
|
-
end
|
156
51
|
|
157
|
-
|
158
|
-
|
159
|
-
end
|
52
|
+
def start_tasks
|
53
|
+
log("Starting tasks... #{Time.now}")
|
160
54
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
55
|
+
while @task_manager.has_more_tasks?
|
56
|
+
display_log
|
57
|
+
check_for_finished
|
58
|
+
find_lock_and_start
|
59
|
+
end
|
165
60
|
|
166
|
-
|
167
|
-
@hosts
|
61
|
+
log("All tasks started... #{Time.now}")
|
168
62
|
end
|
169
63
|
|
170
|
-
def
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
@hosts << host
|
175
|
-
end
|
64
|
+
def wait_for_tasks_to_finish
|
65
|
+
while @children.length > 0
|
66
|
+
display_log
|
67
|
+
check_for_finished
|
176
68
|
end
|
177
69
|
end
|
178
70
|
|
179
|
-
def
|
180
|
-
|
181
|
-
|
182
|
-
|
71
|
+
def handle_results
|
72
|
+
failed_tasks = @results.select { |result| result != 0 }
|
73
|
+
status_code = if failed_tasks.length == 0
|
74
|
+
log("Task passed.", :green)
|
75
|
+
Host::PASS
|
76
|
+
else
|
77
|
+
log("#{failed_tasks.length} task(s) failed.", :red)
|
78
|
+
Host::FAIL
|
183
79
|
end
|
184
80
|
|
185
|
-
|
81
|
+
log("Total Time: #{run_time} minutes.")
|
82
|
+
status_code
|
186
83
|
end
|
187
84
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
host.unlock
|
193
|
-
rescue Errno::EPIPE
|
194
|
-
end
|
195
|
-
end
|
85
|
+
def start_task(host)
|
86
|
+
task = @task_manager.find_task
|
87
|
+
@children << fork do
|
88
|
+
start_forked_task(host, task)
|
196
89
|
end
|
197
90
|
end
|
198
91
|
|
199
|
-
def
|
200
|
-
|
201
|
-
|
202
|
-
|
92
|
+
def start_forked_task(host, task)
|
93
|
+
begin
|
94
|
+
this_host = host.dup
|
95
|
+
unless this_host.copy_codebase
|
96
|
+
@task_manager.add(task)
|
97
|
+
status = 0
|
203
98
|
end
|
99
|
+
status = this_host.run(task.command)
|
100
|
+
host.unlock
|
101
|
+
rescue Errno::EPIPE
|
102
|
+
ensure
|
103
|
+
Process.exit!(status)
|
204
104
|
end
|
205
105
|
end
|
206
|
-
end
|
207
106
|
|
208
|
-
|
209
|
-
|
210
|
-
|
107
|
+
def find_lock_and_start
|
108
|
+
@hosts = @host_manager.hosts.dup if @hosts.empty?
|
109
|
+
if host = @hosts.sample
|
110
|
+
@hosts.delete(host)
|
111
|
+
if host.lock
|
112
|
+
start_task(host)
|
113
|
+
end
|
114
|
+
end
|
211
115
|
end
|
212
116
|
|
213
|
-
def
|
214
|
-
@
|
117
|
+
def run_time
|
118
|
+
minutes = ((Time.now - @configuration.start_time) / 60).to_i
|
119
|
+
seconds = ((Time.now - @configuration.start_time) % 60).to_i
|
120
|
+
"#{minutes}:#{"%02d" % seconds}"
|
215
121
|
end
|
216
122
|
|
217
|
-
def
|
218
|
-
@
|
123
|
+
def log(message, color = :yellow)
|
124
|
+
unless @configuration.quiet
|
125
|
+
highline = HighLine.new
|
126
|
+
system("stty #{@stty_config} 2>/dev/null")
|
127
|
+
highline.say(highline.color("[Remote :: #{@configuration.identifier} :: #{run_time}] #{message}", color))
|
128
|
+
end
|
219
129
|
end
|
220
130
|
|
221
|
-
def
|
222
|
-
@
|
131
|
+
def check_for_finished
|
132
|
+
@children.each do |child_pid|
|
133
|
+
if task_is_finished?(child_pid)
|
134
|
+
@failed << child_pid unless $?.success?
|
135
|
+
@results << $?.exitstatus
|
136
|
+
@children.delete(child_pid)
|
137
|
+
end
|
138
|
+
end
|
223
139
|
end
|
224
140
|
|
225
|
-
def
|
226
|
-
|
141
|
+
def task_is_finished?(pid)
|
142
|
+
Process.waitpid(pid, Process::WNOHANG)
|
227
143
|
end
|
228
144
|
|
229
|
-
def
|
230
|
-
|
145
|
+
def display_log
|
146
|
+
now = Time.now.strftime("%S")[0]
|
147
|
+
unless now == @last_timestamp
|
148
|
+
log("Waiting on #{@task_manager.count} of #{@starting_number_of_tasks} tasks to start.") if @task_manager.count > 0
|
149
|
+
log("Waiting on #{@children.length} of #{@starting_number_of_tasks - @task_manager.count} started tasks to finish. #{@failed.size} failed.") if @children.length > 0
|
150
|
+
$stdout.flush
|
151
|
+
@last_timestamp = now
|
152
|
+
end
|
231
153
|
end
|
232
154
|
end
|
233
155
|
end
|
data/lib/remote_run/version.rb
CHANGED
data/lib/remote_run.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "remote_run", "task")
|
1
2
|
require File.join(File.dirname(__FILE__), "remote_run", "host")
|
2
3
|
require File.join(File.dirname(__FILE__), "remote_run", "runner")
|
4
|
+
require File.join(File.dirname(__FILE__), "remote_run", "configuration")
|
3
5
|
require 'highline'
|
6
|
+
|
7
|
+
module RemoteRun
|
8
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RemoteRun::Configuration::HostManager do
|
4
|
+
subject { RemoteRun::Configuration::HostManager.new }
|
5
|
+
|
6
|
+
describe "#add" do
|
7
|
+
it "adds the given host to a list of hosts" do
|
8
|
+
subject.add(stub(:host, :is_up? => true, :name => "foobar"))
|
9
|
+
subject.hosts.size.should == 1
|
10
|
+
subject.hosts.first.name.should == "foobar"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#hosts" do
|
15
|
+
it "returns all hosts in the list" do
|
16
|
+
host = stub(:host, :is_up? => true, :name => "foobar")
|
17
|
+
subject.add(host)
|
18
|
+
subject.hosts.should == [host]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#start_ssh_master_connections" do
|
23
|
+
before do
|
24
|
+
@host = stub(:host, :is_up? => true, :name => "foobar")
|
25
|
+
subject.add(@host)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "asks each host to start their ssh master connection" do
|
29
|
+
@host.should_receive(:start_ssh_master_connection)
|
30
|
+
subject.start_ssh_master_connections
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe RemoteRun::Configuration::TaskManager do
|
36
|
+
subject { RemoteRun::Configuration::TaskManager.new }
|
37
|
+
describe "#add" do
|
38
|
+
it "takes a string and puts it on a list of tasks" do
|
39
|
+
task = RemoteRun::Task.new("date")
|
40
|
+
subject.add(task)
|
41
|
+
subject.tasks.should include(task)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#find_task" do
|
46
|
+
before do
|
47
|
+
@task = RemoteRun::Task.new("date")
|
48
|
+
subject.add(@task)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "finds a task from the list, returns it and removes it" do
|
52
|
+
subject.tasks.should == [@task]
|
53
|
+
subject.find_task.should == @task
|
54
|
+
subject.tasks.should == []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#tasks" do
|
59
|
+
it "returns all of the tasks stored in the manager" do
|
60
|
+
task = RemoteRun::Task.new("foo")
|
61
|
+
task2 = RemoteRun::Task.new("bar")
|
62
|
+
subject.add(task)
|
63
|
+
subject.add(task2)
|
64
|
+
subject.tasks.should == [task, task2]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#count" do
|
69
|
+
it "returns the number of tasks stored" do
|
70
|
+
task = RemoteRun::Task.new("foo")
|
71
|
+
task2 = RemoteRun::Task.new("bar")
|
72
|
+
subject.add(task)
|
73
|
+
subject.add(task2)
|
74
|
+
subject.count.should == 2
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#has_more_tasks?" do
|
79
|
+
it "returns true when there are tasks in the list" do
|
80
|
+
task = RemoteRun::Task.new("foo")
|
81
|
+
task2 = RemoteRun::Task.new("bar")
|
82
|
+
subject.add(task)
|
83
|
+
subject.add(task2)
|
84
|
+
|
85
|
+
subject.has_more_tasks?.should be_true
|
86
|
+
subject.find_task
|
87
|
+
subject.has_more_tasks?.should be_true
|
88
|
+
subject.find_task
|
89
|
+
subject.has_more_tasks?.should be_false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Host do
|
3
|
+
describe RemoteRun::Host do
|
4
4
|
context "when locking" do
|
5
|
-
let(:host) { Host.new("localhost") }
|
5
|
+
let(:host) { RemoteRun::Host.new("localhost") }
|
6
6
|
|
7
7
|
it "can be locked" do
|
8
8
|
host.lock.should be_true
|
@@ -21,9 +21,9 @@ describe Host do
|
|
21
21
|
|
22
22
|
context "when locked by someone else" do
|
23
23
|
before { lock_file.get }
|
24
|
-
let(:host) { Host.new("localhost") }
|
24
|
+
let(:host) { RemoteRun::Host.new("localhost") }
|
25
25
|
let(:lock_file) {
|
26
|
-
lock_file = Host::LockFile.new("localhost", "myfakelocalhost", "999")
|
26
|
+
lock_file = RemoteRun::Host::LockFile.new("localhost", "myfakelocalhost", "999")
|
27
27
|
}
|
28
28
|
|
29
29
|
it "cannot be unlocked by me" do
|
@@ -33,7 +33,7 @@ describe Host do
|
|
33
33
|
|
34
34
|
context "when locked by me" do
|
35
35
|
before { host.lock }
|
36
|
-
let(:host) { Host.new("localhost") }
|
36
|
+
let(:host) { RemoteRun::Host.new("localhost") }
|
37
37
|
|
38
38
|
it "cannot be locked" do
|
39
39
|
host.lock.should be_false
|
@@ -52,7 +52,7 @@ describe Host do
|
|
52
52
|
|
53
53
|
context "when checking to see if a host is up" do
|
54
54
|
context "when using an authorized host" do
|
55
|
-
let(:host) { Host.new("localhost") }
|
55
|
+
let(:host) { RemoteRun::Host.new("localhost") }
|
56
56
|
|
57
57
|
it "returns true" do
|
58
58
|
host.is_up?.should be_true
|
@@ -60,7 +60,7 @@ describe Host do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
context "when using an unauthorized host" do
|
63
|
-
let(:host) { Host.new("foozmcbarry") }
|
63
|
+
let(:host) { RemoteRun::Host.new("foozmcbarry") }
|
64
64
|
|
65
65
|
it "returns false" do
|
66
66
|
host.is_up?.should be_false
|
@@ -74,7 +74,7 @@ describe Host do
|
|
74
74
|
host.lock
|
75
75
|
end
|
76
76
|
|
77
|
-
let(:host) { Host.new("localhost") }
|
77
|
+
let(:host) { RemoteRun::Host.new("localhost") }
|
78
78
|
|
79
79
|
context "when executing a shell command with a zero status code" do
|
80
80
|
it "returns zero" do
|
@@ -95,7 +95,7 @@ describe Host do
|
|
95
95
|
host.lock
|
96
96
|
end
|
97
97
|
|
98
|
-
let(:host) { Host.new("localhost") }
|
98
|
+
let(:host) { RemoteRun::Host.new("localhost") }
|
99
99
|
|
100
100
|
it "copies the codebase to a remote directory" do
|
101
101
|
$runner.remote_path = "/tmp/testing-remote-run"
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
@@ -3,13 +3,14 @@ require 'bundler/setup'
|
|
3
3
|
require 'rspec'
|
4
4
|
require Dir.pwd + '/lib/remote_run'
|
5
5
|
|
6
|
-
|
6
|
+
RemoteRun::Configuration.new do |config|
|
7
7
|
config.tasks = []
|
8
8
|
config.hosts = []
|
9
|
+
config.quiet = true
|
9
10
|
end
|
10
11
|
|
11
12
|
RSpec.configure do |config|
|
12
13
|
config.after(:each) do
|
13
|
-
system("rm -f #{Host::LockFile::FILE}")
|
14
|
+
system("rm -f #{RemoteRun::Host::LockFile::FILE}")
|
14
15
|
end
|
15
16
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: remote_run
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-09-13 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: highline
|
16
|
-
requirement: &
|
16
|
+
requirement: &2151951040 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2151951040
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &2151943360 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2151943360
|
36
36
|
description: Can be used as a parallel unit test runner
|
37
37
|
email:
|
38
38
|
- casecommons-dev@googlegroups.com
|
@@ -48,11 +48,15 @@ files:
|
|
48
48
|
- Readme.md
|
49
49
|
- examples/demo-remote-run
|
50
50
|
- lib/remote_run.rb
|
51
|
+
- lib/remote_run/configuration.rb
|
51
52
|
- lib/remote_run/host.rb
|
52
53
|
- lib/remote_run/runner.rb
|
54
|
+
- lib/remote_run/task.rb
|
53
55
|
- lib/remote_run/version.rb
|
54
56
|
- remote_run.gemspec
|
57
|
+
- spec/remote_run/configuration_spec.rb
|
55
58
|
- spec/remote_run/host_spec.rb
|
59
|
+
- spec/remote_run/runner_spec.rb
|
56
60
|
- spec/spec_helper.rb
|
57
61
|
homepage: https://github.com/Casecommons/remote_run
|
58
62
|
licenses: []
|
@@ -68,7 +72,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
72
|
version: '0'
|
69
73
|
segments:
|
70
74
|
- 0
|
71
|
-
hash:
|
75
|
+
hash: 3128109979642869878
|
72
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
77
|
none: false
|
74
78
|
requirements:
|
@@ -77,13 +81,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
81
|
version: '0'
|
78
82
|
segments:
|
79
83
|
- 0
|
80
|
-
hash:
|
84
|
+
hash: 3128109979642869878
|
81
85
|
requirements: []
|
82
86
|
rubyforge_project: remote_run
|
83
|
-
rubygems_version: 1.8.
|
87
|
+
rubygems_version: 1.8.10
|
84
88
|
signing_key:
|
85
89
|
specification_version: 3
|
86
90
|
summary: Run N shell scripts on a pool of remote hosts
|
87
91
|
test_files:
|
92
|
+
- spec/remote_run/configuration_spec.rb
|
88
93
|
- spec/remote_run/host_spec.rb
|
94
|
+
- spec/remote_run/runner_spec.rb
|
89
95
|
- spec/spec_helper.rb
|