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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2f749801d61882786e5c4d20fed0f0d25e49330
4
- data.tar.gz: 6d33a112ceecfc0e489d6a62ac1ede016fef30ce
3
+ metadata.gz: 4aecc4e3e0802963a8bde215f896ae5cf46c1eb8
4
+ data.tar.gz: c3e49d1dfb090a7bd99d000616990b4ffcdf4b7e
5
5
  SHA512:
6
- metadata.gz: a13063c8cf25fb05dcb0dd9fab4c0060cd9b2f25c2ae5702044089f98369f112b82f40843cdc61b198a5181fa38e2f8a3131354c752ceac48c23da1c862515f7
7
- data.tar.gz: f15b215c9e5a88645ca4ca7ce351fa589f11b09ab41c6d283d91f180ddc62d299f411d777f8bc0855cf5521612149c6d616a8de5bca0850163630ca81c8eed02
6
+ metadata.gz: 13ef598d961e075c20b26919576dfaed18c850e41ecf9be31ad799b2a6cd6298f250a80e1c19b93a8e35aa5776c9c3cd4c1ebc92a6e453b337b99f5d47854c9d
7
+ data.tar.gz: 0858eef295cd386ecfd652b10ad28fd8b7486dd682ed6c9058016cc4955bab4798addd85a79e2ddf1a6ba79fa8410e655833243d1bce89ee13a6ecb0367294d3
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop_todo.yml CHANGED
@@ -1,31 +1,35 @@
1
1
  # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2015-06-23 11:24:30 -0700 using RuboCop version 0.31.0.
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: 1
8
+ # Offense count: 3
9
9
  Metrics/AbcSize:
10
- Max: 37
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: 9
18
+ Max: 23
15
19
 
16
- # Offense count: 17
20
+ # Offense count: 34
17
21
  # Configuration parameters: AllowURI, URISchemes.
18
22
  Metrics/LineLength:
19
- Max: 130
23
+ Max: 123
20
24
 
21
- # Offense count: 1
25
+ # Offense count: 5
22
26
  # Configuration parameters: CountComments.
23
27
  Metrics/MethodLength:
24
- Max: 22
28
+ Max: 88
25
29
 
26
- # Offense count: 1
30
+ # Offense count: 2
27
31
  Metrics/PerceivedComplexity:
28
- Max: 9
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
@@ -0,0 +1,7 @@
1
+ notifications:
2
+ email: false
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.2
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ [![Build Status](https://travis-ci.org/sul-dlss/robot-controller.svg?branch=master)](https://travis-ci.org/sul-dlss/robot-controller)
2
+ [![Dependency Status](https://gemnasium.com/sul-dlss/robot-controller.svg)](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
- % bundle exec controller boot
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
- % controller boot # start bluepilld and jobs
38
- % controller status # check on status of jobs
39
- % controller verify # verify robots are running as configured
40
- % controller log robot01_01_dor_accessionWF_descriptive-metadata # view log for worker
41
- % controller stop # stop jobs
42
- % controller quit # stop bluepilld
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
- require 'rspec/core/rake_task'
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.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
- if ARGV.size == 0
6
- puts '
7
- Usage: controller ( boot | quit )
8
- controller ( start | status | stop | restart | log ) [worker]
9
- controller verify [--verbose]
10
- controller [--help]
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
- % controller boot # start bluepilld and jobs
14
- % controller status # check on status of jobs
15
- % controller log dor_accessionWF_descriptive-metadata # view log for worker
16
- % controller verify # verify robots are running as configured
17
- % controller stop # stop jobs
18
- % controller quit # stop bluepilld
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
- BLUEPILL_BASEDIR - where bluepill stores its state (default: run/bluepill)
22
- BLUEPILL_LOGFILE - output log (default: log/bluepill.log)
23
- ROBOT_ENVIRONMENT - (default: development)
24
- '
25
- exit(-1)
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 ARGV[0].downcase
42
- when 'boot'
43
- fn = 'config/bluepill.rb' # allow override
44
- fn = RobotController.bluepill_config unless File.file?(fn)
45
- if File.file?(fn)
46
- # puts "Loading #{fn}"
47
- exec "#{cmd} load #{fn}"
48
- # NOTREACHED
49
- end
50
- puts "ERROR: Cannot find bluepill configuration file for #{ENV['ROBOT_ENVIRONMENT']}"
51
- exit(-1)
52
- when 'verify'
53
- verbose = (ARGV[1] == '--verbose')
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
- # load list of all possible robots
56
- robots = YAML.load_file('config/robots.yml')
57
- fail ArgumentError unless robots.is_a? Array
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
- # determine how many processes should be running for each robot
60
- running = {}
61
- robots.each do |robot|
62
- running[robot] = 0
63
- end
64
- RobotController::Parser.load("robots_#{ENV['ROBOT_ENVIRONMENT']}.yml").each do |h|
65
- running[h[:robot]] = h[:n]
66
- end
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
- # verify that all robots running are known to the config/robots.yml
69
- running.each_key do |robot|
70
- if !robots.include?(robot)
71
- puts "ERROR: '#{robot}' robot is unknown to the suite. Check config/robots.yml"
72
- running.delete(robot)
73
- end
74
- end
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
- # verify suite
77
- verify = RobotController::Verify.new(running)
78
- begin
79
- statuses = verify.verify
80
- rescue => e
81
- puts "ERROR: Cannot run verification for any robots. #{e.class}: #{e.message}"
82
- exit(-1)
83
- end
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
- # print output for each status
86
- ok = true
87
- puts 'ERROR: No robots to verify?' if statuses.size == 0
88
- statuses.each_pair do |robot, status|
89
- case status[:state]
90
- when :up
91
- puts "OK: #{robot} is up (#{status[:running]} running)" if verbose
92
- when :not_enabled
93
- if status[:running] == 0
94
- puts "OK: #{robot} is not enabled (0 running)" if verbose
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
- puts "ERROR: #{robot} is not enabled (but #{status[:running]} running)"
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
- sul-robots1-dev:
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
- sul-robots2-dev:
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
- sul-robots3-dev:
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 = `hostname -s`.strip unless host
23
- # puts host
20
+ # determine current host if not provided
21
+ host ||= `hostname -s`.strip
24
22
 
25
- # host = 'sul-robots1-dev' # XXX
26
- fail "HostMismatch: #{host} not defined in #{robots_fn}" unless robots.include?(host) || robots.include?('*')
27
- host = '*' unless robots.include?(host)
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
- parse_yaml(robots[host])
33
+ # parse the host-specific YAML configuration
34
+ parse_robots_configuration(robots[host])
30
35
  end
31
36
 
32
- # parse_instances(1) == 1
33
- # parse_instances(16) == 16
34
- # parse_instances(0) == 1
35
- # parse_instances(99) => RuntimeError
36
- def parse_instances(n)
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 = 1 if n < 1
39
- n
45
+ (n < 1) ? 1 : n
40
46
  end
41
47
 
42
- # parse_lanes('') == ['default']
43
- # parse_lanes(' ') == ['default']
44
- # parse_lanes(' , ') == ['default']
45
- # parse_lanes(' , ,') == ['default']
46
- # parse_lanes('*') == ['*']
47
- # parse_lanes('1') == ['1']
48
- # parse_lanes('A') == ['A']
49
- # parse_lanes('A , B') == ['A', 'B']
50
- # parse_lanes('A,B,C') == ['A','B','C']
51
- # parse_lanes('A-C,E') == ['A-C', 'E']
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
- return ['default'] if lanes_spec.split(/,/).collect(&:strip).join('') == ''
54
- lanes_spec.split(/,/).collect(&:strip).uniq
61
+ lanes = lanes_spec.split(/,/).collect(&:strip).uniq
62
+ lanes.join('') == '' ? ['default'] : lanes
55
63
  end
56
64
 
57
- # build_queues('z','A') => ['z_A']
58
- # build_queues('z','A,C') => ['z_A', 'z_C']
59
- def build_queues(robot, lanes)
60
- queues = []
61
- parse_lanes(lanes).each do |i|
62
- queues << [robot, i].join('_')
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
- def parse_yaml(robots)
68
- # parse YAML lines for host where i is robot[:lane[:instances]]
69
- r = []
70
- robots.each do |i|
71
- robot = i.split(/:/).collect(&:strip)
72
- robot.each do |j|
73
- fail "SyntaxError: #{i}" if j.strip == ''
74
- end
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
- # add defaults
77
- robot << 'default' if robot.size == 1
78
- robot << '1' if robot.size == 2
89
+ # add defaults
90
+ robot << 'default' if robot.size == 1
91
+ robot << '1' if robot.size == 2
79
92
 
80
- # build queues for robot instances
81
- fail "SyntaxError: #{i}" unless robot.size == 3
82
- robot[2] = parse_instances(robot[2].to_i)
83
- # puts robot.join(' : ')
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
- r << { robot: robot[0], queues: queues, n: robot[2] }
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
@@ -1 +1,9 @@
1
1
  require 'resque/tasks'
2
+
3
+ desc 'Verify that robots are running as configured'
4
+ namespace :robots do
5
+ task :verify, :verbose do |_t, args|
6
+ args.with_defaults(verbose: nil)
7
+ system "bundle exec controller verify #{args[:verbose] ? '--verbose' : ''}"
8
+ end
9
+ 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.each_key.to_a
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
- r = {}
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
  # {
@@ -0,0 +1,6 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+ rescue LoadError
5
+ puts 'no rspec available'
6
+ end
@@ -0,0 +1,6 @@
1
+ begin
2
+ require 'rubocop/rake_task'
3
+ RuboCop::RakeTask.new
4
+ rescue LoadError
5
+ puts 'Unable to load RuboCop.'
6
+ end
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ '*':
2
+ - M:default:1
3
+ - N:B:2
4
+ - O:C:3
@@ -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 'pass1' do
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 'pass2' do
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 'parse_instances' do
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.parse_instances(0)).to eq 1
39
- expect(subject.parse_instances(1)).to eq 1
40
- expect(subject.parse_instances(16)).to eq 16
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.parse_instances(17)
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 'build_queues' do
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.build_queues('z', '*')).to eq ['z_*']
80
- expect(subject.build_queues('z', 'default')).to eq ['z_default']
81
- expect(subject.build_queues('z', 'A,B,C')).to eq %w(z_A z_B z_C)
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
@@ -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.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-06-25 00:00:00.000000000 Z
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: awesome_print
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: pry
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: rspec
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: redcarpet
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/doc.rake
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.4.5
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