robot-controller 2.0.0 → 2.0.1
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/.rspec +2 -0
- data/.rubocop_todo.yml +14 -15
- data/.travis.yml +7 -0
- data/README.md +32 -8
- data/Rakefile +2 -6
- data/VERSION +1 -1
- data/bin/controller +102 -91
- data/config/schedule_example.rb +10 -0
- data/example/config/environments/robots_development.yml +4 -3
- data/lib/robot-controller/robots.rb +67 -56
- data/lib/robot-controller/tasks.rb +8 -0
- data/lib/robot-controller/verify.rb +4 -8
- data/lib/tasks/rspec.rake +6 -0
- data/lib/tasks/rubocop.rake +6 -0
- data/lib/tasks/yard.rake +16 -0
- data/robot-controller.gemspec +3 -1
- data/spec/fixtures/matcher.yml +4 -0
- data/spec/unit/robots_spec.rb +42 -11
- data/spec/unit/verify_spec.rb +17 -0
- metadata +43 -9
- data/lib/tasks/doc.rake +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4aecc4e3e0802963a8bde215f896ae5cf46c1eb8
|
4
|
+
data.tar.gz: c3e49d1dfb090a7bd99d000616990b4ffcdf4b7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13ef598d961e075c20b26919576dfaed18c850e41ecf9be31ad799b2a6cd6298f250a80e1c19b93a8e35aa5776c9c3cd4c1ebc92a6e453b337b99f5d47854c9d
|
7
|
+
data.tar.gz: 0858eef295cd386ecfd652b10ad28fd8b7486dd682ed6c9058016cc4955bab4798addd85a79e2ddf1a6ba79fa8410e655833243d1bce89ee13a6ecb0367294d3
|
data/.rspec
ADDED
data/.rubocop_todo.yml
CHANGED
@@ -1,31 +1,35 @@
|
|
1
1
|
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
-
# on 2015-
|
2
|
+
# on 2015-07-01 12:52:10 -0700 using RuboCop version 0.32.1.
|
3
3
|
# The point is for the user to remove these configuration records
|
4
4
|
# one by one as the offenses are removed from the code base.
|
5
5
|
# Note that changes in the inspected code, or installation of new
|
6
6
|
# versions of RuboCop, may require this file to be generated again.
|
7
7
|
|
8
|
-
# Offense count:
|
8
|
+
# Offense count: 3
|
9
9
|
Metrics/AbcSize:
|
10
|
-
Max:
|
10
|
+
Max: 87
|
11
11
|
|
12
12
|
# Offense count: 1
|
13
|
+
Metrics/BlockNesting:
|
14
|
+
Max: 4
|
15
|
+
|
16
|
+
# Offense count: 2
|
13
17
|
Metrics/CyclomaticComplexity:
|
14
|
-
Max:
|
18
|
+
Max: 23
|
15
19
|
|
16
|
-
# Offense count:
|
20
|
+
# Offense count: 34
|
17
21
|
# Configuration parameters: AllowURI, URISchemes.
|
18
22
|
Metrics/LineLength:
|
19
|
-
Max:
|
23
|
+
Max: 123
|
20
24
|
|
21
|
-
# Offense count:
|
25
|
+
# Offense count: 5
|
22
26
|
# Configuration parameters: CountComments.
|
23
27
|
Metrics/MethodLength:
|
24
|
-
Max:
|
28
|
+
Max: 88
|
25
29
|
|
26
|
-
# Offense count:
|
30
|
+
# Offense count: 2
|
27
31
|
Metrics/PerceivedComplexity:
|
28
|
-
Max:
|
32
|
+
Max: 21
|
29
33
|
|
30
34
|
# Offense count: 1
|
31
35
|
# Configuration parameters: Exclude.
|
@@ -41,8 +45,3 @@ Style/FormatString:
|
|
41
45
|
# Cop supports --auto-correct.
|
42
46
|
Style/NumericLiterals:
|
43
47
|
MinDigits: 6
|
44
|
-
|
45
|
-
# Offense count: 1
|
46
|
-
# Configuration parameters: Methods.
|
47
|
-
Style/SingleLineBlockParams:
|
48
|
-
Enabled: false
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
[](https://travis-ci.org/sul-dlss/robot-controller)
|
2
|
+
[](https://gemnasium.com/sul-dlss/robot-controller)
|
3
|
+
|
4
|
+
|
1
5
|
robot-controller
|
2
6
|
================
|
3
7
|
|
@@ -19,7 +23,7 @@ Create the following configuration files based on the examples in `example/confi
|
|
19
23
|
|
20
24
|
Then to use the controller to boot the robots:
|
21
25
|
|
22
|
-
|
26
|
+
bundle exec controller boot
|
23
27
|
|
24
28
|
If you want to *override* the bluepill configuration but still use the
|
25
29
|
controller (though NOT recommended), then add:
|
@@ -34,12 +38,12 @@ controller (though NOT recommended), then add:
|
|
34
38
|
controller [--help]
|
35
39
|
|
36
40
|
Example:
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
controller boot # start bluepilld and jobs
|
42
|
+
controller status # check on status of jobs
|
43
|
+
controller verify # verify robots are running as configured
|
44
|
+
controller log robot01_01_dor_accessionWF_descriptive-metadata # view log for worker
|
45
|
+
controller stop # stop jobs
|
46
|
+
controller quit # stop bluepilld
|
43
47
|
|
44
48
|
Environment:
|
45
49
|
BLUEPILL_BASEDIR - where bluepill stores its state (default: run/bluepill)
|
@@ -51,6 +55,7 @@ controller (though NOT recommended), then add:
|
|
51
55
|
* `v1.0.0`: Initial version
|
52
56
|
* `v1.0.1`: Add 'rake' as dependency
|
53
57
|
* `v2.0.0`: Added 'verify' command
|
58
|
+
* `v2.0.1`: Added rake robots:verify and support for whenever gem
|
54
59
|
|
55
60
|
### `verify` command
|
56
61
|
|
@@ -92,4 +97,23 @@ The various states are determined as follows:
|
|
92
97
|
- If the robot is unknown by the suite:
|
93
98
|
- `ERROR`: always
|
94
99
|
|
95
|
-
NOTE: The queues on which the robots are running are NOT verified.
|
100
|
+
NOTE: The queues on which the robots are running are NOT verified.
|
101
|
+
|
102
|
+
### Running `verify` command via crontab
|
103
|
+
|
104
|
+
In `Capfile` add:
|
105
|
+
|
106
|
+
require 'whenever/capistrano'
|
107
|
+
|
108
|
+
In `config/deploy.rb` add:
|
109
|
+
|
110
|
+
set :whenever_identifier, ->{ "#{fetch(:application)}_#{fetch(:stage)}" }
|
111
|
+
|
112
|
+
In `config/schedule.rb` add:
|
113
|
+
|
114
|
+
every 5.minutes do
|
115
|
+
# cannot use :output with Hash/String because we don't want append behavior
|
116
|
+
set :output, proc { '> log/verify.log 2> log/cron.log' }
|
117
|
+
set :environment_variable, 'ROBOT_ENVIRONMENT'
|
118
|
+
rake 'robots:verify'
|
119
|
+
end
|
data/Rakefile
CHANGED
@@ -1,13 +1,9 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
+
require 'rspec/core/rake_task'
|
3
4
|
require 'version_bumper'
|
4
5
|
require 'robot-controller/tasks'
|
5
6
|
|
6
7
|
Dir.glob('lib/tasks/*.rake').each { |r| import r }
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
desc 'Run specs'
|
11
|
-
RSpec::Core::RakeTask.new(:spec)
|
12
|
-
|
13
|
-
task default: [:yard, :spec]
|
9
|
+
task default: [:spec, :rubocop, :yard]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.
|
1
|
+
2.0.1
|
data/bin/controller
CHANGED
@@ -2,110 +2,121 @@
|
|
2
2
|
require 'yaml'
|
3
3
|
require 'robot-controller'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
# bin/controller tool
|
6
|
+
module RobotController
|
7
|
+
#
|
8
|
+
class CLI
|
9
|
+
# meta command
|
10
|
+
def self.run(args)
|
11
|
+
if args.size == 0 || args.first == '--help'
|
12
|
+
puts '
|
13
|
+
Usage: controller ( boot | quit )
|
14
|
+
controller ( start | status | stop | restart | log ) [worker]
|
15
|
+
controller verify [--verbose]
|
16
|
+
controller [--help]
|
11
17
|
|
12
|
-
Example:
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
Example:
|
19
|
+
% controller boot # start bluepilld and jobs
|
20
|
+
% controller status # check on status of jobs
|
21
|
+
% controller log dor_accessionWF_descriptive-metadata # view log for worker
|
22
|
+
% controller verify # verify robots are running as configured
|
23
|
+
% controller stop # stop jobs
|
24
|
+
% controller quit # stop bluepilld
|
19
25
|
|
20
|
-
Environment:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
'
|
25
|
-
|
26
|
-
end
|
26
|
+
Environment:
|
27
|
+
BLUEPILL_BASEDIR - where bluepill stores its state (default: run/bluepill)
|
28
|
+
BLUEPILL_LOGFILE - output log (default: log/bluepill.log)
|
29
|
+
ROBOT_ENVIRONMENT - (default: development)
|
30
|
+
'
|
31
|
+
exit(-1)
|
32
|
+
end
|
27
33
|
|
28
|
-
ENV['ROBOT_ENVIRONMENT'] ||= 'development'
|
29
|
-
ENV['BLUEPILL_BASE_DIR'] ||= File.expand_path('run/bluepill')
|
30
|
-
ENV['BLUEPILL_LOGFILE'] ||= File.expand_path('log/bluepill.log')
|
34
|
+
ENV['ROBOT_ENVIRONMENT'] ||= 'development'
|
35
|
+
ENV['BLUEPILL_BASE_DIR'] ||= File.expand_path('run/bluepill')
|
36
|
+
ENV['BLUEPILL_LOGFILE'] ||= File.expand_path('log/bluepill.log')
|
31
37
|
|
32
|
-
fail 'bluepill requires config directory' unless File.directory?('config')
|
33
|
-
fail 'bluepill requires run directory' unless File.directory?(File.dirname(ENV['BLUEPILL_BASE_DIR']))
|
34
|
-
fail 'bluepill requires log directory' unless File.directory?(File.dirname(ENV['BLUEPILL_LOGFILE']))
|
38
|
+
fail 'bluepill requires config directory' unless File.directory?('config')
|
39
|
+
fail 'bluepill requires run directory' unless File.directory?(File.dirname(ENV['BLUEPILL_BASE_DIR']))
|
40
|
+
fail 'bluepill requires log directory' unless File.directory?(File.dirname(ENV['BLUEPILL_LOGFILE']))
|
35
41
|
|
36
|
-
cmd = 'bluepill'
|
37
|
-
cmd << ' --no-privileged'
|
38
|
-
cmd << " --base-dir #{ENV['BLUEPILL_BASE_DIR']}"
|
39
|
-
cmd << " --logfile #{ENV['BLUEPILL_LOGFILE']}"
|
42
|
+
cmd = 'bluepill'
|
43
|
+
cmd << ' --no-privileged'
|
44
|
+
cmd << " --base-dir #{ENV['BLUEPILL_BASE_DIR']}"
|
45
|
+
cmd << " --logfile #{ENV['BLUEPILL_LOGFILE']}"
|
40
46
|
|
41
|
-
case
|
42
|
-
when 'boot'
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
when 'verify'
|
53
|
-
|
47
|
+
case args.first.downcase
|
48
|
+
when 'boot'
|
49
|
+
fn = 'config/bluepill.rb' # allow override
|
50
|
+
fn = RobotController.bluepill_config unless File.file?(fn)
|
51
|
+
if File.file?(fn)
|
52
|
+
# puts "Loading #{fn}"
|
53
|
+
exec "#{cmd} load #{fn}"
|
54
|
+
# NOTREACHED
|
55
|
+
end
|
56
|
+
puts "ERROR: Cannot find bluepill configuration file for #{ENV['ROBOT_ENVIRONMENT']}"
|
57
|
+
exit(-1)
|
58
|
+
when 'verify'
|
59
|
+
verbose = (args[1] == '--verbose')
|
54
60
|
|
55
|
-
|
56
|
-
|
57
|
-
|
61
|
+
# load list of all possible robots
|
62
|
+
robots = YAML.load_file('config/robots.yml')
|
63
|
+
fail ArgumentError unless robots.is_a? Array
|
58
64
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
# determine how many processes should be running for each robot
|
66
|
+
running = {}
|
67
|
+
robots.each do |robot|
|
68
|
+
running[robot] = 0
|
69
|
+
end
|
70
|
+
RobotController::Parser.load("robots_#{ENV['ROBOT_ENVIRONMENT']}.yml").each do |h|
|
71
|
+
running[h[:robot]] = h[:n]
|
72
|
+
end
|
67
73
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
74
|
+
# verify that all robots running are known to the config/robots.yml
|
75
|
+
running.each_key do |robot|
|
76
|
+
unless robots.include?(robot)
|
77
|
+
puts "ERROR: '#{robot}' robot is unknown to the suite. Check config/robots.yml"
|
78
|
+
running.delete(robot)
|
79
|
+
end
|
80
|
+
end
|
75
81
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
# verify suite
|
83
|
+
verify = RobotController::Verify.new(running)
|
84
|
+
begin
|
85
|
+
statuses = verify.verify
|
86
|
+
rescue => e
|
87
|
+
puts "ERROR: Cannot run verification for any robots. #{e.class}: #{e.message}"
|
88
|
+
exit(-1)
|
89
|
+
end
|
84
90
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
# print output for each status
|
92
|
+
ok = true
|
93
|
+
puts 'ERROR: No robots to verify?' if statuses.size == 0
|
94
|
+
statuses.each_pair do |robot, status|
|
95
|
+
case status[:state]
|
96
|
+
when :up
|
97
|
+
puts "OK: #{robot} is up (#{status[:running]} running)" if verbose
|
98
|
+
when :not_enabled
|
99
|
+
if status[:running] == 0
|
100
|
+
puts "OK: #{robot} is not enabled (0 running)" if verbose
|
101
|
+
else
|
102
|
+
puts "ERROR: #{robot} is not enabled (but #{status[:running]} running)"
|
103
|
+
end
|
104
|
+
when :down
|
105
|
+
ok = false
|
106
|
+
puts "ERROR: #{robot} is down (#{status[:running]} of #{running[robot]} running)"
|
107
|
+
else
|
108
|
+
ok = false
|
109
|
+
puts "ERROR: #{robot} cannot be verified (state=#{status[:state]})"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
puts 'OK' if ok && !verbose && statuses.size > 0
|
113
|
+
exit(0)
|
95
114
|
else
|
96
|
-
|
115
|
+
exec "#{cmd} #{args.join(' ')}"
|
116
|
+
# NOTREACHED
|
97
117
|
end
|
98
|
-
when :down
|
99
|
-
ok = false
|
100
|
-
puts "ERROR: #{robot} is down (#{status[:running]} of #{running[robot]} running)"
|
101
|
-
else
|
102
|
-
ok = false
|
103
|
-
puts "ERROR: #{robot} cannot be verified (state=#{status[:state]})"
|
104
118
|
end
|
105
119
|
end
|
106
|
-
puts 'OK' if ok && !verbose && statuses.size > 0
|
107
|
-
exit(0)
|
108
|
-
else
|
109
|
-
exec "#{cmd} #{ARGV.join(' ')}"
|
110
|
-
# NOTREACHED
|
111
120
|
end
|
121
|
+
|
122
|
+
RobotController::CLI.run(ARGV)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Use this file to easily define all of your cron jobs.
|
2
|
+
|
3
|
+
every 5.minutes do
|
4
|
+
# cannot use :output with Hash/String because we don't want append behavior
|
5
|
+
set :output, proc { '> log/verify.log 2> log/cron.log' }
|
6
|
+
set :environment_variable, 'ROBOT_ENVIRONMENT'
|
7
|
+
rake 'robots:verify'
|
8
|
+
end
|
9
|
+
|
10
|
+
# Learn more: http://github.com/javan/whenever
|
@@ -14,6 +14,7 @@
|
|
14
14
|
# - a list of identiers ('A,B,C'), or
|
15
15
|
# - an asterix (*).
|
16
16
|
# 3. instances is a single integer.
|
17
|
+
# 4. host is the hostname or '*' for any host
|
17
18
|
#
|
18
19
|
# Both lane and instances are optional. Lane defaults to 'default', and
|
19
20
|
# instances defaults to 1.
|
@@ -43,7 +44,7 @@
|
|
43
44
|
#
|
44
45
|
# Robot 1 (8 CPU) hosts shelving and publish only
|
45
46
|
#
|
46
|
-
|
47
|
+
robot1:
|
47
48
|
- dor_accessionWF_shelve:A # 1 robot for lane A
|
48
49
|
- dor_accessionWF_shelve:B:3 # 3 robots for lane B
|
49
50
|
- dor_accessionWF_shelve:C:3 # 3 robots for lane C
|
@@ -64,7 +65,7 @@ sul-robots1-dev:
|
|
64
65
|
#
|
65
66
|
# Robot 2 (16 CPU) hosts technical metadata creation only
|
66
67
|
#
|
67
|
-
|
68
|
+
robot2:
|
68
69
|
- dor_accessionWF_technical-metadata:A:5 # 5 robots for lane A
|
69
70
|
- dor_accessionWF_technical-metadata:B:5 # 5 robots for lane B
|
70
71
|
- dor_accessionWF_technical-metadata:C,D,E:5 # 5 robots for lanes C, D, and E
|
@@ -72,7 +73,7 @@ sul-robots2-dev:
|
|
72
73
|
#
|
73
74
|
# Robot 3 (4 CPU) hosts helper robots for all accessioning workflows
|
74
75
|
#
|
75
|
-
|
76
|
+
robot3:
|
76
77
|
- dor_accessionWF_descriptive-metadata # 1 robot for default lane
|
77
78
|
- dor_accessionWF_rights-metadata # 1 robot for default lane
|
78
79
|
- dor_accessionWF_content-metadata # 1 robot for default lane
|
@@ -4,6 +4,7 @@ require 'yaml'
|
|
4
4
|
module RobotController
|
5
5
|
#
|
6
6
|
class Parser
|
7
|
+
# maximum number of processes a single robot can have
|
7
8
|
ROBOT_INSTANCE_MAX = 16
|
8
9
|
|
9
10
|
class << self
|
@@ -13,80 +14,90 @@ module RobotController
|
|
13
14
|
robots_fn = File.join(dir, robots_fn) if dir
|
14
15
|
fail "FileNotFound: #{robots_fn}" unless File.file?(robots_fn)
|
15
16
|
|
16
|
-
# read the YAML file
|
17
|
-
# puts "Loading #{robots_fn}"
|
17
|
+
# read the YAML file with the configuration of all the robots to run
|
18
18
|
robots = YAML.load_file(robots_fn)
|
19
|
-
# puts robots
|
20
19
|
|
21
|
-
# determine current host
|
22
|
-
host
|
23
|
-
# puts host
|
20
|
+
# determine current host if not provided
|
21
|
+
host ||= `hostname -s`.strip
|
24
22
|
|
25
|
-
#
|
26
|
-
|
27
|
-
|
23
|
+
# if the config lists this specific host, use it;
|
24
|
+
# else check to see if '*' is a matching host
|
25
|
+
unless robots.include?(host)
|
26
|
+
if robots.include?('*')
|
27
|
+
host = '*'
|
28
|
+
else
|
29
|
+
fail "HostMismatch: #{host} not defined in #{robots_fn}"
|
30
|
+
end
|
31
|
+
end
|
28
32
|
|
29
|
-
|
33
|
+
# parse the host-specific YAML configuration
|
34
|
+
parse_robots_configuration(robots[host])
|
30
35
|
end
|
31
36
|
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
+
# validates that the instances value is within range, e.g.,
|
38
|
+
#
|
39
|
+
# instances_valid?(1) == 1
|
40
|
+
# instances_valid?(16) == 16
|
41
|
+
# instances_valid?(0) == 1 # out of range low, enforce minimum
|
42
|
+
# instances_valid?(99) => RuntimeError # out of range high, error out
|
43
|
+
def instances_valid?(n)
|
37
44
|
fail "TooManyInstances: #{n} > #{ROBOT_INSTANCE_MAX}" if n > ROBOT_INSTANCE_MAX
|
38
|
-
n
|
39
|
-
n
|
45
|
+
(n < 1) ? 1 : n
|
40
46
|
end
|
41
47
|
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
48
|
+
# parse the lane values designator using the following syntax:
|
49
|
+
#
|
50
|
+
# parse_lanes('') == ['default']
|
51
|
+
# parse_lanes(' ') == ['default']
|
52
|
+
# parse_lanes(' , ') == ['default']
|
53
|
+
# parse_lanes(' , ,') == ['default']
|
54
|
+
# parse_lanes('*') == ['*']
|
55
|
+
# parse_lanes('1') == ['1']
|
56
|
+
# parse_lanes('A') == ['A']
|
57
|
+
# parse_lanes('A , B') == ['A', 'B']
|
58
|
+
# parse_lanes('A,B,C') == ['A','B','C']
|
59
|
+
# parse_lanes('A-C,E') == ['A-C', 'E']
|
52
60
|
def parse_lanes(lanes_spec)
|
53
|
-
|
54
|
-
|
61
|
+
lanes = lanes_spec.split(/,/).collect(&:strip).uniq
|
62
|
+
lanes.join('') == '' ? ['default'] : lanes
|
55
63
|
end
|
56
64
|
|
57
|
-
#
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
queues
|
65
|
+
# generate the queue names for all given lanes, e.g.,
|
66
|
+
#
|
67
|
+
# queue_names('z','A') => ['z_A']
|
68
|
+
# queue_names('z','A,C') => ['z_A', 'z_C']
|
69
|
+
def queue_names(robot, lanes)
|
70
|
+
parse_lanes(lanes).collect { |lane| robot + '_' + lane }
|
65
71
|
end
|
66
72
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
73
|
+
# parse YAML lines for host where line is robot[:lane[:instances]]
|
74
|
+
#
|
75
|
+
# @return [Array<Hash>]
|
76
|
+
# [{
|
77
|
+
# robot: 'foo',
|
78
|
+
# queues: ['foo_default'],
|
79
|
+
# n: 2
|
80
|
+
# }, ... ]
|
81
|
+
def parse_robots_configuration(robots)
|
82
|
+
[].tap do |r|
|
83
|
+
robots.each do |line|
|
84
|
+
robot = line.split(/:/).collect(&:strip)
|
85
|
+
robot.each do |j|
|
86
|
+
fail "SyntaxError: '#{line}' is missing arguments" if j.strip == ''
|
87
|
+
end
|
75
88
|
|
76
|
-
|
77
|
-
|
78
|
-
|
89
|
+
# add defaults
|
90
|
+
robot << 'default' if robot.size == 1
|
91
|
+
robot << '1' if robot.size == 2
|
79
92
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
queues = build_queues(robot[0], robot[1])
|
85
|
-
# puts queues
|
93
|
+
# build queues for robot instances
|
94
|
+
fail "SyntaxError: '#{line}' is missing arguments" unless robot.size == 3
|
95
|
+
robot[2] = instances_valid?(robot[2].to_i)
|
96
|
+
queues = queue_names(robot[0], robot[1])
|
86
97
|
|
87
|
-
|
98
|
+
r << { robot: robot[0], queues: queues, n: robot[2] }
|
99
|
+
end
|
88
100
|
end
|
89
|
-
r
|
90
101
|
end
|
91
102
|
end
|
92
103
|
end
|
@@ -49,7 +49,7 @@ module RobotController
|
|
49
49
|
fail ArgumentError if nprocesses.nil? || !nprocesses.is_a?(Hash)
|
50
50
|
fail ArgumentError, 'Empty argument' if nprocesses.size == 0
|
51
51
|
@running = nprocesses
|
52
|
-
@robots = @running.
|
52
|
+
@robots = @running.keys
|
53
53
|
@status = nil
|
54
54
|
end
|
55
55
|
|
@@ -71,11 +71,7 @@ module RobotController
|
|
71
71
|
# }
|
72
72
|
def verify(reload = true)
|
73
73
|
@status = nil if reload
|
74
|
-
|
75
|
-
robots.each do |robot|
|
76
|
-
r[robot] = robot_status(robot)
|
77
|
-
end
|
78
|
-
r
|
74
|
+
{}.tap { |hash| robots.each { |robot| hash[robot] = robot_status(robot) } }
|
79
75
|
end
|
80
76
|
|
81
77
|
# @param [String] robot name
|
@@ -84,8 +80,6 @@ module RobotController
|
|
84
80
|
@running[robot]
|
85
81
|
end
|
86
82
|
|
87
|
-
protected
|
88
|
-
|
89
83
|
# @param [String] robot name
|
90
84
|
# @return [Hash] { state: :up | :down | :not_enabled, running: n }
|
91
85
|
def robot_status(robot)
|
@@ -96,6 +90,8 @@ module RobotController
|
|
96
90
|
end
|
97
91
|
end
|
98
92
|
|
93
|
+
protected
|
94
|
+
|
99
95
|
#
|
100
96
|
# @return [Hash] status
|
101
97
|
# {
|
data/lib/tasks/yard.rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
begin
|
2
|
+
require 'yard'
|
3
|
+
YARD::Rake::YardocTask.new do |t|
|
4
|
+
t.files = ['lib/**/*.rb', 'bin/controller']
|
5
|
+
t.options = ['--readme', 'README.md', '-m', 'markdown']
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :yard do
|
9
|
+
desc 'Clean up documentation'
|
10
|
+
task :clean do
|
11
|
+
FileUtils.rm_rf('doc')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
abort 'Please install the YARD gem to generate doc.'
|
16
|
+
end
|
data/robot-controller.gemspec
CHANGED
@@ -25,11 +25,13 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_dependency 'bluepill', '0.0.68' # pin bluepill to prevent nil status output regression
|
26
26
|
s.add_dependency 'resque', '~> 1.25.2'
|
27
27
|
s.add_dependency 'rake', '~> 10.3.2'
|
28
|
+
s.add_dependency 'whenever', '~> 0.9.2'
|
28
29
|
|
29
|
-
s.add_development_dependency 'awesome_print'
|
30
30
|
s.add_development_dependency 'pry'
|
31
31
|
s.add_development_dependency 'rspec'
|
32
32
|
s.add_development_dependency 'redcarpet' # provides Markdown
|
33
|
+
s.add_development_dependency 'rubocop'
|
34
|
+
s.add_development_dependency 'simplecov'
|
33
35
|
s.add_development_dependency 'version_bumper'
|
34
36
|
s.add_development_dependency 'yard'
|
35
37
|
end
|
data/spec/unit/robots_spec.rb
CHANGED
@@ -6,7 +6,7 @@ describe RobotController::Parser do
|
|
6
6
|
RobotController::Parser.load('standard.yml', 'spec/fixtures', 'host1')
|
7
7
|
end
|
8
8
|
|
9
|
-
it '
|
9
|
+
it 'parses correctly' do
|
10
10
|
expect(subject).to eq [
|
11
11
|
{ robot: 'X', queues: ['X_default'], n: 1 },
|
12
12
|
{ robot: 'Y', queues: ['Y_B'], n: 1 },
|
@@ -20,7 +20,7 @@ describe RobotController::Parser do
|
|
20
20
|
RobotController::Parser.load('standard.yml', 'spec/fixtures', 'host2')
|
21
21
|
end
|
22
22
|
|
23
|
-
it '
|
23
|
+
it 'parses correctly' do
|
24
24
|
expect(subject).to eq [
|
25
25
|
{ robot: 'A', queues: ['A_*'], n: 1 },
|
26
26
|
{ robot: 'B', queues: %w(B_X B_Y), n: 1 },
|
@@ -30,19 +30,50 @@ describe RobotController::Parser do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
context '
|
33
|
+
context 'expanded mismatched host' do
|
34
|
+
it 'reports error' do
|
35
|
+
expect do
|
36
|
+
RobotController::Parser.load('standard.yml', 'spec/fixtures', 'host3')
|
37
|
+
end.to raise_error(RuntimeError)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'matcher' do
|
42
|
+
subject do
|
43
|
+
RobotController::Parser.load('matcher.yml', 'spec/fixtures', 'host3')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'parses correctly' do
|
47
|
+
expect(subject).to eq [
|
48
|
+
{ robot: 'M', queues: ['M_default'], n: 1 },
|
49
|
+
{ robot: 'N', queues: ['N_B'], n: 2 },
|
50
|
+
{ robot: 'O', queues: ['O_C'], n: 3 }
|
51
|
+
]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'file-not-found' do
|
56
|
+
it 'reports error' do
|
57
|
+
expect do
|
58
|
+
RobotController::Parser.load('nofile.yml', 'spec/fixtures', 'host3')
|
59
|
+
end.to raise_error(RuntimeError)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'instances_valid?' do
|
34
64
|
subject do
|
35
65
|
RobotController::Parser
|
36
66
|
end
|
37
67
|
it 'valid inputs' do
|
38
|
-
expect(subject.
|
39
|
-
expect(subject.
|
40
|
-
expect(subject.
|
68
|
+
expect(subject.instances_valid?(0)).to eq 1
|
69
|
+
expect(subject.instances_valid?(1)).to eq 1
|
70
|
+
expect(subject.instances_valid?(8)).to eq 8
|
71
|
+
expect(subject.instances_valid?(16)).to eq 16
|
41
72
|
end
|
42
73
|
|
43
74
|
it 'invalid inputs' do
|
44
75
|
expect do
|
45
|
-
subject.
|
76
|
+
subject.instances_valid?(17)
|
46
77
|
end.to raise_error RuntimeError
|
47
78
|
end
|
48
79
|
end
|
@@ -70,15 +101,15 @@ describe RobotController::Parser do
|
|
70
101
|
end
|
71
102
|
end
|
72
103
|
|
73
|
-
context '
|
104
|
+
context 'queue_names' do
|
74
105
|
subject do
|
75
106
|
RobotController::Parser
|
76
107
|
end
|
77
108
|
|
78
109
|
it 'valid inputs' do
|
79
|
-
expect(subject.
|
80
|
-
expect(subject.
|
81
|
-
expect(subject.
|
110
|
+
expect(subject.queue_names('z', '*')).to eq ['z_*']
|
111
|
+
expect(subject.queue_names('z', 'default')).to eq ['z_default']
|
112
|
+
expect(subject.queue_names('z', 'A,B,C')).to eq %w(z_A z_B z_C)
|
82
113
|
end
|
83
114
|
end
|
84
115
|
end
|
data/spec/unit/verify_spec.rb
CHANGED
@@ -94,6 +94,23 @@ describe RobotController::Verify do
|
|
94
94
|
)
|
95
95
|
end
|
96
96
|
|
97
|
+
it 'runs controller status with errors' do
|
98
|
+
allow(subject).to receive(:controller_status).and_return([
|
99
|
+
'robot01_01_dor_gisAssemblyWF_assign-placenamesMISMATCH(pid:29481): down'
|
100
|
+
])
|
101
|
+
expect(subject.robot_status('dor_gisAssemblyWF_assign-placenames')).to eq(
|
102
|
+
state: :unknown, running: 0
|
103
|
+
)
|
104
|
+
# expect(subject.robot_status('dor_gisAssemblyWF_assign-placenamesMISMATCH')).to eq(
|
105
|
+
# { state: :not_enabled, running: 1 }
|
106
|
+
# )
|
107
|
+
expect { subject.robot_status('garbage') }.to raise_error(RuntimeError)
|
108
|
+
expect(subject.verify).to eq(
|
109
|
+
# 'dor_gisAssemblyWF_assign-placenamesMISMATCH' => { state: :not_enabled, running: 1 },
|
110
|
+
'dor_gisAssemblyWF_assign-placenames' => { state: :unknown, running: 0 }
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
97
114
|
it 'runs controller status even when broken' do
|
98
115
|
allow(subject).to receive(:controller_status).and_return([])
|
99
116
|
expect { subject.verify }.to raise_error(RuntimeError)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: robot-controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darren Hardy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bluepill
|
@@ -53,7 +53,21 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 10.3.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: whenever
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.2
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.9.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - '>='
|
@@ -67,7 +81,7 @@ dependencies:
|
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: rspec
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - '>='
|
@@ -81,7 +95,7 @@ dependencies:
|
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
98
|
+
name: redcarpet
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - '>='
|
@@ -95,7 +109,21 @@ dependencies:
|
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
100
128
|
requirements:
|
101
129
|
- - '>='
|
@@ -145,8 +173,10 @@ extensions: []
|
|
145
173
|
extra_rdoc_files: []
|
146
174
|
files:
|
147
175
|
- .gitignore
|
176
|
+
- .rspec
|
148
177
|
- .rubocop.yml
|
149
178
|
- .rubocop_todo.yml
|
179
|
+
- .travis.yml
|
150
180
|
- Gemfile
|
151
181
|
- LICENSE
|
152
182
|
- README.md
|
@@ -155,6 +185,7 @@ files:
|
|
155
185
|
- bin/controller
|
156
186
|
- config/environments/robots_development.yml
|
157
187
|
- config/robots.yml
|
188
|
+
- config/schedule_example.rb
|
158
189
|
- example/config/environments/robots_development.yml
|
159
190
|
- example/lib/tasks/environment.rake
|
160
191
|
- lib/robot-controller.rb
|
@@ -162,8 +193,11 @@ files:
|
|
162
193
|
- lib/robot-controller/robots.rb
|
163
194
|
- lib/robot-controller/tasks.rb
|
164
195
|
- lib/robot-controller/verify.rb
|
165
|
-
- lib/tasks/
|
196
|
+
- lib/tasks/rspec.rake
|
197
|
+
- lib/tasks/rubocop.rake
|
198
|
+
- lib/tasks/yard.rake
|
166
199
|
- robot-controller.gemspec
|
200
|
+
- spec/fixtures/matcher.yml
|
167
201
|
- spec/fixtures/standard.yml
|
168
202
|
- spec/spec_helper.rb
|
169
203
|
- spec/unit/robots_spec.rb
|
@@ -189,14 +223,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
223
|
version: 1.3.6
|
190
224
|
requirements: []
|
191
225
|
rubyforge_project:
|
192
|
-
rubygems_version: 2.
|
226
|
+
rubygems_version: 2.0.14
|
193
227
|
signing_key:
|
194
228
|
specification_version: 4
|
195
229
|
summary: Monitors and controls running workflow robots off of priority queues and
|
196
230
|
within a cluster
|
197
231
|
test_files:
|
232
|
+
- spec/fixtures/matcher.yml
|
198
233
|
- spec/fixtures/standard.yml
|
199
234
|
- spec/spec_helper.rb
|
200
235
|
- spec/unit/robots_spec.rb
|
201
236
|
- spec/unit/verify_spec.rb
|
202
|
-
has_rdoc: true
|
data/lib/tasks/doc.rake
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
desc 'Generate RDoc'
|
2
|
-
task doc: ['doc:generate']
|
3
|
-
|
4
|
-
namespace :doc do
|
5
|
-
project_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
6
|
-
doc_destination = File.join(project_root, 'rdoc')
|
7
|
-
|
8
|
-
begin
|
9
|
-
require 'yard'
|
10
|
-
require 'yard/rake/yardoc_task'
|
11
|
-
|
12
|
-
YARD::Rake::YardocTask.new(:generate) do |yt|
|
13
|
-
yt.files = Dir.glob(File.join(project_root, 'lib', '*.rb')) +
|
14
|
-
Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
|
15
|
-
[File.join(project_root, 'README.rdoc')]
|
16
|
-
|
17
|
-
yt.options = ['--output-dir', doc_destination, '--readme', 'README.md']
|
18
|
-
end
|
19
|
-
rescue LoadError
|
20
|
-
desc 'Generate YARD Documentation'
|
21
|
-
task :generate do
|
22
|
-
abort 'Please install the YARD gem to generate rdoc.'
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
desc 'Remove generated documenation'
|
27
|
-
task :clean do
|
28
|
-
rm_r doc_destination if File.exist?(doc_destination)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
desc 'Build Yard documentation'
|
33
|
-
task :yard do
|
34
|
-
YARD::Rake::YardocTask.new do |t|
|
35
|
-
t.files = ['lib/**/*.rb', 'bin/**/*.rb']
|
36
|
-
end
|
37
|
-
end
|