robot_sweatshop 0.3.3 → 0.4.0
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/.gitignore +1 -0
- data/README.md +11 -11
- data/Rakefile +1 -1
- data/bin/README.md +23 -0
- data/bin/sweatshop +1 -1
- data/bin/sweatshop-assembler +101 -0
- data/bin/sweatshop-conveyor +64 -0
- data/bin/{sweatshop-input-http → sweatshop-input} +13 -7
- data/bin/sweatshop-job-dictionary +53 -0
- data/bin/sweatshop-payload-parser +28 -28
- data/bin/sweatshop-worker +58 -0
- data/config.defaults.yaml +6 -4
- data/lib/README.md +10 -13
- data/lib/robot_sweatshop/cli/common.rb +1 -1
- data/lib/robot_sweatshop/cli/config.rb +1 -1
- data/lib/robot_sweatshop/cli/job.rb +1 -1
- data/lib/robot_sweatshop/cli/start.rb +2 -2
- data/lib/robot_sweatshop/config.rb +2 -0
- data/lib/robot_sweatshop/connections.rb +17 -0
- data/lib/robot_sweatshop/create-config-directories.rb +0 -1
- data/lib/robot_sweatshop.rb +1 -2
- data/robot_sweatshop.eye +30 -32
- data/robot_sweatshop.gemspec +8 -6
- data/test/README.md +3 -1
- data/test/all.rb +35 -0
- data/test/assembler_spec.rb +89 -0
- data/test/conveyor_spec.rb +67 -0
- data/test/data/weird_job.yaml +6 -0
- data/test/end-to-end_spec.rb +19 -13
- data/test/input_spec.rb +49 -0
- data/test/job_dictionary_spec.rb +70 -0
- data/test/payload_parser_spec.rb +37 -26
- data/test/shared/helpers/input.rb +73 -0
- data/test/shared/helpers/output.rb +14 -0
- data/test/shared/helpers.rb +4 -83
- data/test/shared/scaffolding.rb +42 -0
- data/test/shared/stub.rb +44 -0
- data/test/worker_spec.rb +59 -0
- metadata +76 -44
- data/bin/sweatshop-job-assembler +0 -82
- data/bin/sweatshop-job-worker +0 -45
- data/bin/sweatshop-queue-broadcaster +0 -20
- data/bin/sweatshop-queue-handler +0 -24
- data/bin/sweatshop-queue-watcher +0 -18
- data/lib/robot_sweatshop/moneta-queue.rb +0 -49
- data/lib/robot_sweatshop/queue-helper.rb +0 -32
- data/robot_sweatshop.production.eye +0 -7
- data/robot_sweatshop.testing.eye +0 -7
- data/test/input_http_spec.rb +0 -49
- data/test/job_assembler_spec.rb +0 -80
- data/test/job_worker_spec.rb +0 -65
- data/test/moneta-queue_spec.rb +0 -49
- data/test/queue_broadcaster_spec.rb +0 -39
- data/test/queue_handler_spec.rb +0 -54
- data/test/run_all.rb +0 -3
- data/test/shared/process_spawning.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0549401a67fbd6e2dc3b5b22017d42700958f9c9
|
4
|
+
data.tar.gz: 5f741330b8b0999cb078cf0508e9440f595e02e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1c7523aea2b3006952303d6e3dfb9f13886cd304479ecec7c5841cfaba5c1455aed8a6ff13815b18ae7ec6af7904dee785333d30ad1a4a4af52badebef6ca5a
|
7
|
+
data.tar.gz: 83b86d2de23a74dc9a4623fb23417e37d822271b42e96b05cb3c7cc6f62eb764924d11beb8b1b0417199ac5cdefaa2b09918f670f930b84d24c20558a09993ac
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -10,20 +10,20 @@ Robot Sweatshop is a single-purpose CI server that runs collections of arbitrary
|
|
10
10
|
|
11
11
|
- install [ZMQ as described in the EZMQ gem](https://github.com/colstrom/ezmq#operating-system-notes)
|
12
12
|
- `gem install robot_sweatshop`
|
13
|
-
- `sweatshop start`
|
13
|
+
- `sweatshop start`
|
14
14
|
- `sweatshop job example --auto`
|
15
|
-
- `curl -
|
15
|
+
- `curl -d '' localhost:8080/run/example`
|
16
16
|
- `cat .robot_sweatshop/log/job-worker.log`
|
17
17
|
|
18
18
|
# Usage
|
19
19
|
|
20
|
-
Drop the `--auto` flag to create the job interactively. You can specify which branches will trigger the job, which commands will be run, and any environment variables you might need.
|
20
|
+
Drop the `--auto` flag to create the job interactively. You can specify which branches will trigger the job, which commands will be run, and any environment variables you might need. See [the wiki](https://github.com/JScott/robot_sweatshop/wiki/Job-configuration) for more details.
|
21
21
|
|
22
22
|
Robot Sweatshop uses [Eye](https://github.com/kostya/eye) to handle its processes so you can use its commandline tool to monitor their status.
|
23
23
|
|
24
24
|
# Configuration
|
25
25
|
|
26
|
-
By default, Robot Sweatshop looks in your current working path to configure and run. You can supply a custom configuration with `sweatshop config [local|user|system]`.
|
26
|
+
By default, Robot Sweatshop looks in your current working path to configure and run. You can supply a custom configuration with `sweatshop config [local|user|system]`. See [the wiki](https://github.com/JScott/robot_sweatshop/wiki) for more information.
|
27
27
|
|
28
28
|
# Supported payload formats
|
29
29
|
|
@@ -34,21 +34,21 @@ By default, Robot Sweatshop looks in your current working path to configure and
|
|
34
34
|
|
35
35
|
# Security
|
36
36
|
|
37
|
-
You probably don't want to run Robot Sweatshop as a sudo user. Create a testing user and group and
|
37
|
+
You probably don't want to run Robot Sweatshop as a sudo user. Create a testing user and group and run `sweatshop start` as them.
|
38
38
|
|
39
39
|
# Roadmap
|
40
40
|
|
41
41
|
1.0
|
42
42
|
|
43
|
-
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
- Support for multiple workers
|
43
|
+
- Docs on architecture
|
44
|
+
- Easier way to run multiple workers
|
45
|
+
- Mascot
|
48
46
|
|
49
47
|
Beyond
|
50
48
|
|
49
|
+
- Jenkins-to-Sweatshop job converting script
|
50
|
+
- Take a look at beefcake for data versioning/serialization
|
51
51
|
- Better logging for the processes
|
52
52
|
- CLI configuration via chomp and/or flags
|
53
|
-
- Common scrips such as git repo syncing and creating a job run ID
|
53
|
+
- Common scrips such as git repo syncing and creating a job run ID (see: [sweatshop-tears](https://github.com/JScott/sweatshop-tears))
|
54
54
|
- Use [eye-http](https://github.com/kostya/eye-http) for the '/' route?
|
data/Rakefile
CHANGED
data/bin/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Services
|
2
|
+
|
3
|
+
sweatshop-conveyor
|
4
|
+
```
|
5
|
+
{ method:, data: }
|
6
|
+
|
7
|
+
id = enqueue(item)
|
8
|
+
id = dequeue
|
9
|
+
item = lookup(id)
|
10
|
+
something = finish(id)
|
11
|
+
```
|
12
|
+
|
13
|
+
sweatshop-payload-parser
|
14
|
+
```
|
15
|
+
req: { payload:, user_agent: }
|
16
|
+
rep: { data:, error: }
|
17
|
+
```
|
18
|
+
|
19
|
+
sweatshop-job-dictionary
|
20
|
+
```
|
21
|
+
req: { job_name: }
|
22
|
+
rep: { data:, error: }
|
23
|
+
```
|
data/bin/sweatshop
CHANGED
@@ -8,7 +8,7 @@ require 'robot_sweatshop/config'
|
|
8
8
|
require 'robot_sweatshop/create-config-directories'
|
9
9
|
|
10
10
|
program :name, 'Robot Sweatshop'
|
11
|
-
program :version, '0.
|
11
|
+
program :version, '0.4.0'
|
12
12
|
program :description, 'A lightweight, unopinionated CI server'
|
13
13
|
program :help, 'Author', 'Justin Scott <jvscott@gmail.com>'
|
14
14
|
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'yaml'
|
4
|
+
require 'contracts'
|
5
|
+
require 'exponential_backoff'
|
6
|
+
require 'ezmq'
|
7
|
+
require 'robot_sweatshop/config'
|
8
|
+
require 'robot_sweatshop/connections'
|
9
|
+
$stdout.sync = true
|
10
|
+
include Contracts
|
11
|
+
using ExtendedEZMQ
|
12
|
+
|
13
|
+
Contract Hash => Hash
|
14
|
+
def sanitize(data)
|
15
|
+
data = data.map { |key, value| {key => value.to_s} }
|
16
|
+
data.reduce(:merge)
|
17
|
+
end
|
18
|
+
|
19
|
+
Contract Hash, Hash => Hash
|
20
|
+
def job_context(job_environment, context_from_payload)
|
21
|
+
sanitize job_environment.merge(context_from_payload)
|
22
|
+
end
|
23
|
+
|
24
|
+
Contract Hash, Hash, Hash => Hash
|
25
|
+
def assemble(job, payload, definition)
|
26
|
+
{
|
27
|
+
commands: definition['commands'],
|
28
|
+
context: job_context(definition['environment'] || {}, payload),
|
29
|
+
job_name: job[:job_name],
|
30
|
+
job_id: job[:job_id]
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
Contract None => Or[Hash,nil]
|
35
|
+
def request_job
|
36
|
+
job_id = @sockets[:conveyor].request({method: 'dequeue'}, {})
|
37
|
+
return nil if job_id.nil?
|
38
|
+
raw_job = @sockets[:conveyor].request({method: 'lookup', data: job_id}, {})
|
39
|
+
puts "Assembling: '#{raw_job}'"
|
40
|
+
raw_job.merge job_id: job_id
|
41
|
+
end
|
42
|
+
|
43
|
+
Contract EZMQ::Socket, Any => Hash
|
44
|
+
def request(socket, data)
|
45
|
+
response = socket.request data, {}
|
46
|
+
raise response[:error] unless response[:error].empty?
|
47
|
+
response[:data]
|
48
|
+
end
|
49
|
+
|
50
|
+
Contract Hash => [Hash, Hash]
|
51
|
+
def request_data_for(job)
|
52
|
+
payload = request @sockets[:parser], job
|
53
|
+
definition = request @sockets[:dictionary], job[:job_name]
|
54
|
+
[payload, definition]
|
55
|
+
end
|
56
|
+
|
57
|
+
Contract Or[Array,nil], Or[String, nil] => nil
|
58
|
+
def check_whitelist(whitelist, branch)
|
59
|
+
return if whitelist.nil?
|
60
|
+
raise 'Branch not whitelisted' unless whitelist.include? branch
|
61
|
+
end
|
62
|
+
|
63
|
+
Contract Fixnum => Bool
|
64
|
+
def finish(job_id)
|
65
|
+
@sockets[:conveyor].request({method: 'finish', data: job_id}, {})
|
66
|
+
end
|
67
|
+
|
68
|
+
job_search = Fiber.new do
|
69
|
+
# TODO: profiler to get a better idea of how long we should wait based on historical information
|
70
|
+
timer = ExponentialBackoff.new 0.1, 3
|
71
|
+
loop do
|
72
|
+
sleep timer.next_interval
|
73
|
+
job = request_job
|
74
|
+
next if job.nil?
|
75
|
+
timer.clear
|
76
|
+
Fiber.yield job
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
puts 'Started'
|
81
|
+
@sockets = {
|
82
|
+
conveyor: EZMQ::Client.new(port: configatron.conveyor_port),
|
83
|
+
worker: EZMQ::Pusher.new(:bind, port: configatron.worker_port),
|
84
|
+
parser: EZMQ::Client.new(port: configatron.payload_parser_port),
|
85
|
+
dictionary: EZMQ::Client.new(port: configatron.job_dictionary_port)
|
86
|
+
}
|
87
|
+
@sockets.each { |_key, socket| socket.serialize_with_json! }
|
88
|
+
|
89
|
+
loop do
|
90
|
+
job = job_search.resume
|
91
|
+
begin
|
92
|
+
payload, definition = request_data_for job
|
93
|
+
check_whitelist definition['branch_whitelist'], payload['branch']
|
94
|
+
assembled_job = assemble job, payload, definition
|
95
|
+
@sockets[:worker].send(assembled_job, {})
|
96
|
+
rescue RuntimeError => error
|
97
|
+
puts error.message
|
98
|
+
finish job[:job_id]
|
99
|
+
next
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'ezmq'
|
4
|
+
require 'stubborn_queue'
|
5
|
+
require 'oj'
|
6
|
+
require 'contracts'
|
7
|
+
require 'robot_sweatshop/config'
|
8
|
+
require 'robot_sweatshop/connections'
|
9
|
+
using ExtendedEZMQ
|
10
|
+
|
11
|
+
queue_settings = {
|
12
|
+
name: 'test',
|
13
|
+
timeout: configatron.job_timeout_length,
|
14
|
+
file: "#{configatron.database_path}/conveyor.db"
|
15
|
+
}
|
16
|
+
@items = StubbornQueue.new queue_settings
|
17
|
+
$stdout.sync = true
|
18
|
+
include Contracts
|
19
|
+
|
20
|
+
Contract Hash => Fixnum
|
21
|
+
def enqueue(item)
|
22
|
+
puts "enqueue #{item}"
|
23
|
+
@items.enqueue Oj.dump(item)
|
24
|
+
end
|
25
|
+
|
26
|
+
Contract None => Or[Fixnum,nil]
|
27
|
+
def dequeue
|
28
|
+
puts "dequeue"
|
29
|
+
@items.dequeue
|
30
|
+
end
|
31
|
+
|
32
|
+
Contract Fixnum => Hash
|
33
|
+
def lookup(id)
|
34
|
+
puts "lookup #{id}"
|
35
|
+
Oj.load @items.lookup(id)
|
36
|
+
end
|
37
|
+
|
38
|
+
Contract Fixnum => Bool
|
39
|
+
def finish(id)
|
40
|
+
puts "finish #{id}"
|
41
|
+
@items.finish id
|
42
|
+
end
|
43
|
+
|
44
|
+
Contract String => Bool
|
45
|
+
def supported?(method)
|
46
|
+
%w(enqueue dequeue lookup finish).include? method
|
47
|
+
end
|
48
|
+
|
49
|
+
Contract Hash => Any
|
50
|
+
def complete(request)
|
51
|
+
arguments = []
|
52
|
+
return send(request[:method], request[:data]) if request[:data]
|
53
|
+
send(request[:method])
|
54
|
+
end
|
55
|
+
|
56
|
+
puts 'Starting the Conveyor'
|
57
|
+
server = EZMQ::Server.new port: configatron.conveyor_port
|
58
|
+
server.serialize_with_json!
|
59
|
+
server.listen do |request|
|
60
|
+
puts "Received: #{request.inspect}"
|
61
|
+
next unless request.is_a? Hash
|
62
|
+
next unless supported? request[:method]
|
63
|
+
complete request
|
64
|
+
end
|
@@ -2,28 +2,34 @@
|
|
2
2
|
require 'bundler/setup'
|
3
3
|
require 'sinatra'
|
4
4
|
require 'ezmq'
|
5
|
-
require 'json'
|
6
5
|
require 'robot_sweatshop/config'
|
6
|
+
require 'robot_sweatshop/connections'
|
7
|
+
using ExtendedEZMQ
|
7
8
|
|
8
9
|
configure do
|
9
10
|
set :port, configatron.http_port
|
10
11
|
set :bind, configatron.http_bind
|
11
12
|
set :run, true
|
13
|
+
set :conveyor, EZMQ::Client.new(port: configatron.conveyor_port)
|
14
|
+
settings.conveyor.serialize_with_json!
|
12
15
|
end
|
13
16
|
|
14
17
|
get '/' do
|
15
18
|
'Everything\'s on schedule!'
|
16
19
|
end
|
17
20
|
|
18
|
-
post '/
|
21
|
+
post '/run/:job_name' do
|
19
22
|
puts "Received payload for #{params['job_name']}"
|
20
23
|
request.body.rewind
|
21
24
|
payload = request.body.read
|
22
25
|
hash = {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
method: 'enqueue',
|
27
|
+
data: {
|
28
|
+
payload: payload,
|
29
|
+
user_agent: request.env['HTTP_USER_AGENT'],
|
30
|
+
job_name: params['job_name']
|
31
|
+
}
|
26
32
|
}
|
27
|
-
|
28
|
-
|
33
|
+
settings.conveyor.request hash, {}
|
34
|
+
200
|
29
35
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'oj'
|
4
|
+
require 'ezmq'
|
5
|
+
require 'contracts'
|
6
|
+
require 'robot_sweatshop/config'
|
7
|
+
require 'robot_sweatshop/connections'
|
8
|
+
$stdout.sync = true
|
9
|
+
include Contracts
|
10
|
+
using ExtendedEZMQ
|
11
|
+
|
12
|
+
Contract None => String
|
13
|
+
def job_path
|
14
|
+
File.expand_path configatron.job_path
|
15
|
+
end
|
16
|
+
|
17
|
+
Contract String => Or[Hash, nil]
|
18
|
+
def load_if_exists(config_path)
|
19
|
+
puts "Reading job configuration from #{config_path}"
|
20
|
+
YAML.load_file config_path if File.exists? config_path
|
21
|
+
end
|
22
|
+
|
23
|
+
Contract None => Hash
|
24
|
+
def empty_config
|
25
|
+
puts "Job configuration not found or empty"
|
26
|
+
{}
|
27
|
+
end
|
28
|
+
|
29
|
+
Contract String => Hash
|
30
|
+
def load_config_for(job_name)
|
31
|
+
load_if_exists("#{job_path}/#{job_name}.yaml") || empty_config
|
32
|
+
end
|
33
|
+
|
34
|
+
Contract Hash, Hash => Hash
|
35
|
+
def formatted(payload={}, error:'')
|
36
|
+
{data: payload, error: error}
|
37
|
+
end
|
38
|
+
|
39
|
+
Contract String => Hash
|
40
|
+
def define(job_name)
|
41
|
+
config = load_config_for job_name
|
42
|
+
return formatted error: 'Job not found or empty' if config.empty?
|
43
|
+
return formatted error: 'No commands' unless config['commands'].is_a? Array
|
44
|
+
formatted config
|
45
|
+
end
|
46
|
+
|
47
|
+
puts 'Started'
|
48
|
+
server = EZMQ::Server.new port: configatron.job_dictionary_port
|
49
|
+
server.serialize_with_json!
|
50
|
+
server.listen do |job_name|
|
51
|
+
puts "Looking up: #{job_name}"
|
52
|
+
define job_name
|
53
|
+
end
|
@@ -1,20 +1,21 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'bundler/setup'
|
3
|
-
require '
|
3
|
+
require 'oj'
|
4
4
|
require 'ezmq'
|
5
5
|
require 'contracts'
|
6
6
|
require 'robot_sweatshop/config'
|
7
7
|
require 'robot_sweatshop/payload'
|
8
|
-
|
8
|
+
require 'robot_sweatshop/connections'
|
9
9
|
$stdout.sync = true
|
10
10
|
include Contracts
|
11
|
+
using ExtendedEZMQ
|
11
12
|
|
12
13
|
Contract String => Bool
|
13
14
|
def json?(string)
|
14
15
|
begin
|
15
|
-
|
16
|
+
Oj.load string
|
16
17
|
true
|
17
|
-
rescue
|
18
|
+
rescue Oj::ParseError => e
|
18
19
|
false
|
19
20
|
end
|
20
21
|
end
|
@@ -31,42 +32,41 @@ end
|
|
31
32
|
|
32
33
|
Contract Hash => String
|
33
34
|
def detect_format_of(request)
|
34
|
-
return 'empty' if request[
|
35
|
-
return 'Github' if from_github? request[
|
36
|
-
return 'Bitbucket' if from_bitbucket? request[
|
37
|
-
return 'JSON' if json? request[
|
35
|
+
return 'empty' if request[:payload].empty?
|
36
|
+
return 'Github' if from_github? request[:user_agent]
|
37
|
+
return 'Bitbucket' if from_bitbucket? request[:user_agent]
|
38
|
+
return 'JSON' if json? request[:payload]
|
38
39
|
'unsupported'
|
39
40
|
end
|
40
41
|
|
41
42
|
Contract String, String => Or[Hash, nil]
|
42
43
|
def payload_hash_from(payload, format)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
begin
|
45
|
+
Object.const_get("#{format}Payload").new(payload).to_hash
|
46
|
+
rescue NameError
|
47
|
+
nil
|
48
|
+
end
|
48
49
|
end
|
49
50
|
|
50
|
-
Contract Hash => Hash
|
51
|
-
def
|
52
|
-
|
53
|
-
return {payload: {}, error: ''} if format == 'empty'
|
54
|
-
payload = payload_hash_from request['payload'], format
|
55
|
-
return {payload: {}, error: "Can't parse #{format} payload"} if payload.nil?
|
56
|
-
{payload: payload, error: ''}
|
51
|
+
Contract Hash, Hash => Hash
|
52
|
+
def formatted(payload={}, error:'')
|
53
|
+
{data: payload, error: error}
|
57
54
|
end
|
58
55
|
|
59
|
-
Contract
|
60
|
-
def
|
61
|
-
return
|
62
|
-
|
56
|
+
Contract String, String => Hash
|
57
|
+
def parse(raw_payload, format)
|
58
|
+
return formatted if format == 'empty'
|
59
|
+
return formatted error: 'Unknown format' if format == 'unsupported'
|
60
|
+
payload = payload_hash_from raw_payload, format
|
61
|
+
return formatted error: "Invalid #{format} payload" if payload.nil?
|
62
|
+
formatted payload
|
63
63
|
end
|
64
64
|
|
65
65
|
puts 'Started'
|
66
|
-
server = EZMQ::Server.new port: configatron.payload_parser_port
|
66
|
+
server = EZMQ::Server.new port: configatron.payload_parser_port
|
67
|
+
server.serialize_with_json!
|
67
68
|
server.listen do |request|
|
68
69
|
puts "Parsing: #{request}"
|
69
|
-
|
70
|
-
|
71
|
-
JSON.dump request
|
70
|
+
format = detect_format_of request
|
71
|
+
parse request[:payload], format
|
72
72
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'ezmq'
|
4
|
+
require 'faker'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'contracts'
|
7
|
+
require 'robot_sweatshop/config'
|
8
|
+
require 'robot_sweatshop/connections'
|
9
|
+
using ExtendedEZMQ
|
10
|
+
$stdout.sync = true
|
11
|
+
$stderr.sync = true
|
12
|
+
include Contracts
|
13
|
+
|
14
|
+
# TODO: check existing worker ids. it'd be a problem to share a workspace
|
15
|
+
@worker_id = ARGV[0] || "#{Faker::Name.first_name}"
|
16
|
+
|
17
|
+
Contract Hash, Proc => Any
|
18
|
+
def from_workspace(named:)
|
19
|
+
workspace = "#{named}-#{@worker_id}"
|
20
|
+
puts "Workspace: #{workspace}"
|
21
|
+
path = File.expand_path "#{configatron.workspace_path}/#{workspace}"
|
22
|
+
FileUtils.mkpath path
|
23
|
+
Dir.chdir(path) { yield if block_given? }
|
24
|
+
end
|
25
|
+
|
26
|
+
Contract Hash, String => nil
|
27
|
+
def execute(context, command)
|
28
|
+
puts "Executing '#{command}'..."
|
29
|
+
# TODO: path.split(' ') to bypass the shell when we're not using env vars
|
30
|
+
IO.popen(context, command) do |io_stream|
|
31
|
+
while line = io_stream.gets
|
32
|
+
puts line
|
33
|
+
end
|
34
|
+
end
|
35
|
+
puts "Execution complete with exit status: #{$?.exitstatus}"
|
36
|
+
end
|
37
|
+
|
38
|
+
Contract Fixnum => nil
|
39
|
+
def finish(id)
|
40
|
+
@sockets[:conveyor].request({method: 'finish', data: id}, {})
|
41
|
+
puts "Job finished.\n\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
puts 'Starting'
|
45
|
+
@sockets = {
|
46
|
+
conveyor: EZMQ::Client.new(port: configatron.conveyor_port),
|
47
|
+
puller: EZMQ::Puller.new(:connect, port: configatron.worker_port)
|
48
|
+
}
|
49
|
+
@sockets.each { |_key, socket| socket.serialize_with_json! }
|
50
|
+
|
51
|
+
@sockets[:puller].listen do |data|
|
52
|
+
puts "Running: #{data}"
|
53
|
+
from_workspace named: data[:job_name] do
|
54
|
+
context = data[:context] || {}
|
55
|
+
data[:commands].each { |command| execute context, command }
|
56
|
+
end
|
57
|
+
finish data[:job_id]
|
58
|
+
end
|
data/config.defaults.yaml
CHANGED
@@ -3,9 +3,11 @@ pidfile_path: .robot_sweatshop/run
|
|
3
3
|
logfile_path: .robot_sweatshop/log
|
4
4
|
job_path: .robot_sweatshop/jobs
|
5
5
|
workspace_path: .robot_sweatshop/workspaces
|
6
|
-
|
6
|
+
database_path: .robot_sweatshop/db
|
7
7
|
http_port: 8080
|
8
8
|
http_bind: 0.0.0.0
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
conveyor_port: 5555
|
10
|
+
payload_parser_port: 5556
|
11
|
+
job_dictionary_port: 5557
|
12
|
+
worker_port: 5558
|
13
|
+
job_timeout_length: 300 # 5 minutes
|
data/lib/README.md
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
Job lifecycle:
|
2
2
|
|
3
3
|
```
|
4
|
-
|
5
|
-
|
6
|
-
job-worker
|
7
|
-
```
|
8
|
-
|
9
|
-
All passing done via the moneta core in queue/*
|
4
|
+
input -> conveyor
|
5
|
+
{ payload:, user_agent:, job_name: }
|
10
6
|
|
11
|
-
|
7
|
+
assembler <-> conveyor
|
12
8
|
|
13
|
-
|
9
|
+
assembler <-> payload-parser
|
10
|
+
assembler <-> job-dictionary
|
11
|
+
{ payload:, user_agent: } <-> { payload:, error: }
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
```
|
18
|
-
{ payload:, user_agent: } -> payload-parser -> { payload:, error: }
|
13
|
+
assembler -> worker
|
14
|
+
{ context:, commands:, job_name:, job_id }
|
19
15
|
```
|
20
16
|
|
21
|
-
|
17
|
+
context is passed around with string keys because it's user provided
|
18
|
+
everything else is passed with symbol keys
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
|
2
|
+
require 'robot_sweatshop/config'
|
3
3
|
|
4
4
|
def store_config_for_eye
|
5
5
|
config = configatron.to_h
|
@@ -12,7 +12,7 @@ end
|
|
12
12
|
|
13
13
|
def start_sweatshop(for_environment:)
|
14
14
|
store_config_for_eye
|
15
|
-
eye_config = File.expand_path "#{__dir__}/../../../robot_sweatshop
|
15
|
+
eye_config = File.expand_path "#{__dir__}/../../../robot_sweatshop.eye"
|
16
16
|
output = `eye load #{eye_config}`
|
17
17
|
if $?.exitstatus != 0
|
18
18
|
notify :failure, output
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'ezmq'
|
2
|
+
require 'oj'
|
3
|
+
require 'robot_sweatshop/config'
|
4
|
+
|
5
|
+
module ExtendedEZMQ
|
6
|
+
refine EZMQ::Socket do
|
7
|
+
def serialize_with_json!
|
8
|
+
self.encode = -> message { Oj.dump message }
|
9
|
+
self.decode = -> message { Oj.load message }
|
10
|
+
end
|
11
|
+
|
12
|
+
def close
|
13
|
+
self.socket.close
|
14
|
+
self.context.terminate
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/robot_sweatshop.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require_relative 'robot_sweatshop/moneta-queue'
|
2
|
-
require_relative 'robot_sweatshop/queue-helper'
|
3
1
|
require_relative 'robot_sweatshop/config'
|
4
2
|
require_relative 'robot_sweatshop/payload'
|
5
3
|
require_relative 'robot_sweatshop/cli'
|
4
|
+
require_relative 'robot_sweatshop/connections'
|