gearman_admin_client 0.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.
- data/README.markdown +27 -0
- data/lib/gearman_admin_client.rb +130 -0
- data/lib/gearman_admin_client/connection.rb +37 -0
- data/lib/gearman_admin_client/registered_function.rb +13 -0
- data/lib/gearman_admin_client/version.rb +3 -0
- data/lib/gearman_admin_client/worker.rb +13 -0
- data/spec/gearman_admin_client_spec.rb +161 -0
- metadata +117 -0
data/README.markdown
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# GearmanAdminClient
|
2
|
+
|
3
|
+
Connect and issue administrative commands to a [Gearman](http://gearman.org) server. `GearmanAdminClient`'s API follows the Administrative Protocol closely. You can read more about the Adminstrative Protocol under the "Administrative Protocol" section of the [Gearman protocol specification](http://gearman.org/protocol).
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
client = GearmanAdminClient.new('localhost:4730')
|
9
|
+
|
10
|
+
# list registered workers
|
11
|
+
client.workers
|
12
|
+
|
13
|
+
# list registered functions
|
14
|
+
client.status
|
15
|
+
|
16
|
+
# set the maximum queue size for a function
|
17
|
+
client.max_queue_size('function_name', 1_000)
|
18
|
+
|
19
|
+
# get the version of the server
|
20
|
+
client.server_version
|
21
|
+
|
22
|
+
# shutdown the server gracefully
|
23
|
+
client.shutdown graceful: true
|
24
|
+
|
25
|
+
# shutdown the server forcefully
|
26
|
+
client.shutdown
|
27
|
+
```
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
require_relative 'gearman_admin_client/worker'
|
4
|
+
require_relative 'gearman_admin_client/registered_function'
|
5
|
+
require_relative 'gearman_admin_client/connection'
|
6
|
+
|
7
|
+
class GearmanAdminClient
|
8
|
+
|
9
|
+
DISCONNECTED = :DISCONNECTED unless defined? DISCONNECTED
|
10
|
+
|
11
|
+
class BadAddress < RuntimeError ; end
|
12
|
+
|
13
|
+
attr_reader :address
|
14
|
+
|
15
|
+
def initialize(address)
|
16
|
+
@address = address
|
17
|
+
@connection = DISCONNECTED
|
18
|
+
end
|
19
|
+
|
20
|
+
def workers
|
21
|
+
connect! do |connection|
|
22
|
+
connection.write('workers')
|
23
|
+
output = connection.drain.split("\n")
|
24
|
+
|
25
|
+
output.map! do |line|
|
26
|
+
segments = line.split(':')
|
27
|
+
|
28
|
+
function_names = segments.pop.strip.split(' ')
|
29
|
+
|
30
|
+
remainder = segments.join(':')
|
31
|
+
|
32
|
+
fd, ip_address, client_id = remainder.split(' ').map(&:strip)
|
33
|
+
|
34
|
+
unless function_names.include?('-')
|
35
|
+
Worker.new(
|
36
|
+
:file_descriptor => fd,
|
37
|
+
:ip_address => ip_address,
|
38
|
+
:client_id => client_id,
|
39
|
+
:function_names => function_names
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
output.compact!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def status
|
49
|
+
connect! do |connection|
|
50
|
+
connection.write('status')
|
51
|
+
output = connection.drain.split("\n")
|
52
|
+
|
53
|
+
output.map do |line|
|
54
|
+
function_name, total, running, workers = line.split("\t")
|
55
|
+
|
56
|
+
RegisteredFunction.new(
|
57
|
+
:name => function_name,
|
58
|
+
:jobs_in_queue => total,
|
59
|
+
:running_jobs => running,
|
60
|
+
:available_workers => workers
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def server_version
|
67
|
+
connect! do |connection|
|
68
|
+
connection.write('version')
|
69
|
+
connection.read.strip
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def shutdown(options = {})
|
74
|
+
connect! do |connection|
|
75
|
+
command = ['shutdown']
|
76
|
+
|
77
|
+
if options.fetch(:graceful, false)
|
78
|
+
command << 'graceful'
|
79
|
+
end
|
80
|
+
|
81
|
+
connection.write(command.join(' '))
|
82
|
+
connection.read.strip
|
83
|
+
|
84
|
+
connection.eof? && disconnect!
|
85
|
+
end
|
86
|
+
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
def max_queue_size(function_name, queue_size = nil)
|
91
|
+
connect! do |connection|
|
92
|
+
command = ['maxqueue', function_name, queue_size].compact
|
93
|
+
|
94
|
+
connection.write(command.join(' '))
|
95
|
+
connection.read.strip
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def disconnected?
|
100
|
+
DISCONNECTED == @connection
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def disconnect!
|
106
|
+
@connection.close
|
107
|
+
@connection = DISCONNECTED
|
108
|
+
end
|
109
|
+
|
110
|
+
def connect!(&and_then)
|
111
|
+
if disconnected?
|
112
|
+
just_open_a_socket do |socket|
|
113
|
+
@connection = Connection.new(socket)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
and_then.call(@connection)
|
118
|
+
end
|
119
|
+
|
120
|
+
def just_open_a_socket
|
121
|
+
host, port = address.split(':')
|
122
|
+
|
123
|
+
if host && port
|
124
|
+
yield TCPSocket.new(host, port)
|
125
|
+
else
|
126
|
+
raise BadAddress, "expected address to look like HOST:PORT"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class GearmanAdminClient
|
4
|
+
class Connection
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :io, :close, :closed?, :eof?
|
8
|
+
|
9
|
+
attr_reader :io
|
10
|
+
|
11
|
+
def initialize(io)
|
12
|
+
@io = io
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(command)
|
16
|
+
IO::select([], [io])
|
17
|
+
io.puts(command)
|
18
|
+
end
|
19
|
+
|
20
|
+
def read
|
21
|
+
IO::select([io])
|
22
|
+
io.gets
|
23
|
+
end
|
24
|
+
|
25
|
+
def drain
|
26
|
+
output = ''
|
27
|
+
|
28
|
+
while line = read
|
29
|
+
break if line.chop == '.'
|
30
|
+
output << line
|
31
|
+
end
|
32
|
+
|
33
|
+
output
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
|
3
|
+
class GearmanAdminClient
|
4
|
+
class RegisteredFunction
|
5
|
+
include Virtus::ValueObject
|
6
|
+
|
7
|
+
attribute :name, String
|
8
|
+
attribute :jobs_in_queue, Integer
|
9
|
+
attribute :running_jobs, Integer
|
10
|
+
attribute :available_workers, Integer
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'gearmand_control'
|
2
|
+
require 'gearman_admin_client'
|
3
|
+
|
4
|
+
require 'timeout'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
describe GearmanAdminClient do
|
8
|
+
|
9
|
+
before do
|
10
|
+
@sockets = []
|
11
|
+
@gearmand = GearmandControl.new(4730)
|
12
|
+
@gearmand.start
|
13
|
+
@gearmand.test!
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
@sockets.each(&:close)
|
18
|
+
@gearmand.stop
|
19
|
+
end
|
20
|
+
|
21
|
+
def can_do(server_address, *function_names)
|
22
|
+
host, port = server_address.split(':')
|
23
|
+
socket = TCPSocket.new(host, port)
|
24
|
+
|
25
|
+
@sockets << socket
|
26
|
+
|
27
|
+
function_names.each do |function_name|
|
28
|
+
request(socket, 1, [function_name])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def submit_job_bg(server_address, *function_names)
|
33
|
+
host, port = server_address.split(':')
|
34
|
+
socket = TCPSocket.new(host, port)
|
35
|
+
|
36
|
+
@sockets << socket
|
37
|
+
|
38
|
+
function_names.each do |function_name|
|
39
|
+
id = SecureRandom.hex
|
40
|
+
request(socket, 18, [function_name, id, 'arg'])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def request(socket, type, arguments)
|
45
|
+
body = arguments.join("\0")
|
46
|
+
header = ["\0REQ", type, body.size].pack('a4NN')
|
47
|
+
|
48
|
+
IO::select([], [socket])
|
49
|
+
socket.write(header + body)
|
50
|
+
|
51
|
+
read_a_little_from(socket)
|
52
|
+
end
|
53
|
+
|
54
|
+
def read_a_little_from(socket)
|
55
|
+
begin
|
56
|
+
Timeout.timeout(1.0e-6, RuntimeError) do
|
57
|
+
IO::select([socket])
|
58
|
+
socket.read
|
59
|
+
end
|
60
|
+
rescue
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'knows the server version' do
|
65
|
+
client = GearmanAdminClient.new(@gearmand.address)
|
66
|
+
|
67
|
+
expect(client.server_version).to match(/OK \d+\.?+/)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'can shutdown the server gracefully' do
|
71
|
+
client = GearmanAdminClient.new(@gearmand.address)
|
72
|
+
|
73
|
+
client.shutdown :graceful => true
|
74
|
+
|
75
|
+
expect do
|
76
|
+
@gearmand.test!
|
77
|
+
end.to raise_error(GearmandControl::TestFailed)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'can shutdown the server forcefully' do
|
81
|
+
client = GearmanAdminClient.new(@gearmand.address)
|
82
|
+
|
83
|
+
client.shutdown
|
84
|
+
|
85
|
+
expect do
|
86
|
+
@gearmand.test!
|
87
|
+
end.to raise_error(GearmandControl::TestFailed)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'can list registered workers' do
|
91
|
+
client = GearmanAdminClient.new(@gearmand.address)
|
92
|
+
|
93
|
+
expect(client.workers).to be_empty
|
94
|
+
|
95
|
+
can_do(@gearmand.address, 'function_1', 'function_2')
|
96
|
+
can_do(@gearmand.address, 'function_3')
|
97
|
+
|
98
|
+
functions = client.workers.
|
99
|
+
map { |worker| worker.function_names.sort }.
|
100
|
+
sort_by(&:size)
|
101
|
+
|
102
|
+
expect(functions).to eql([
|
103
|
+
['function_3'],
|
104
|
+
['function_1', 'function_2']
|
105
|
+
])
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'knows all registered functions' do
|
109
|
+
client = GearmanAdminClient.new(@gearmand.address)
|
110
|
+
|
111
|
+
expect(client.status).to be_empty
|
112
|
+
|
113
|
+
can_do(@gearmand.address, 'function_1', 'function_2')
|
114
|
+
can_do(@gearmand.address, 'function_3')
|
115
|
+
submit_job_bg(@gearmand.address, 'function_2', 'function_4')
|
116
|
+
|
117
|
+
expect(client.status.sort_by(&:name)).to eql([
|
118
|
+
{ :name => 'function_1', :jobs_in_queue => 0, :running_jobs => 0,
|
119
|
+
:available_workers => 1 },
|
120
|
+
{ :name => 'function_2', :jobs_in_queue => 1, :running_jobs => 0,
|
121
|
+
:available_workers => 1 },
|
122
|
+
{ :name => 'function_3', :jobs_in_queue => 0, :running_jobs => 0,
|
123
|
+
:available_workers => 1 },
|
124
|
+
{ :name => 'function_4', :jobs_in_queue => 1, :running_jobs => 0,
|
125
|
+
:available_workers => 0 }
|
126
|
+
].map(&GearmanAdminClient::RegisteredFunction.method(:new)))
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'can set the max queue size' do
|
130
|
+
client = GearmanAdminClient.new(@gearmand.address)
|
131
|
+
|
132
|
+
can_do(@gearmand.address, 'function_1')
|
133
|
+
|
134
|
+
client.max_queue_size('function_1', 1)
|
135
|
+
|
136
|
+
2.times do
|
137
|
+
submit_job_bg(@gearmand.address, 'function_1')
|
138
|
+
end
|
139
|
+
|
140
|
+
expect(client.status).to eql([GearmanAdminClient::RegisteredFunction.new(
|
141
|
+
:name => 'function_1',
|
142
|
+
:jobs_in_queue => 1,
|
143
|
+
:running_jobs => 0,
|
144
|
+
:available_workers => 1
|
145
|
+
)])
|
146
|
+
|
147
|
+
client.max_queue_size('function_1', 3)
|
148
|
+
|
149
|
+
2.times do
|
150
|
+
submit_job_bg(@gearmand.address, 'function_1')
|
151
|
+
end
|
152
|
+
|
153
|
+
expect(client.status).to eql([GearmanAdminClient::RegisteredFunction.new(
|
154
|
+
:name => 'function_1',
|
155
|
+
:jobs_in_queue => 3,
|
156
|
+
:running_jobs => 0,
|
157
|
+
:available_workers => 1
|
158
|
+
)])
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gearman_admin_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brian Cobb
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: gearmand_control
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: virtus
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - '='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.0.0.beta0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - '='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.0.0.beta0
|
78
|
+
description: A Ruby wrapper around the Gearman admin protocol
|
79
|
+
email:
|
80
|
+
- bcobb@uwalumni.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- lib/gearman_admin_client/connection.rb
|
86
|
+
- lib/gearman_admin_client/registered_function.rb
|
87
|
+
- lib/gearman_admin_client/version.rb
|
88
|
+
- lib/gearman_admin_client/worker.rb
|
89
|
+
- lib/gearman_admin_client.rb
|
90
|
+
- README.markdown
|
91
|
+
- spec/gearman_admin_client_spec.rb
|
92
|
+
homepage: http://github.com/bcobb/gearman_admin_client
|
93
|
+
licenses: []
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 1.8.25
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: Chat with a gearman server using its admin protocol
|
116
|
+
test_files:
|
117
|
+
- spec/gearman_admin_client_spec.rb
|