villein 0.2.1 → 0.3.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/.travis.yml +1 -1
- data/lib/villein/agent.rb +24 -2
- data/lib/villein/client.rb +55 -1
- data/lib/villein/version.rb +1 -1
- data/misc/villein-event-handler +15 -2
- data/spec/agent_spec.rb +15 -0
- data/spec/client_spec.rb +134 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d9b9e6c34204126822c64c925e9a83ef3120b29
|
4
|
+
data.tar.gz: 548c1b7932f71f81db8df8fb23f70a9a3ae10879
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84d91183e28e93240f478ea4ecaeebaafdbe027c3c7506fb77f8bab97ce7fc1a1c4f4b4c45f3f8298992ea6310668d525cfc4054dc2fe0bef161316a33dee9da
|
7
|
+
data.tar.gz: f3632f50eded1238d24d645228871ff6edd3b99f2f637f0534bd0633d5bc6e147d18321ffc0f8348aaae2f62af6fac596d672697f3508ff4469781fa973937b6
|
data/.travis.yml
CHANGED
@@ -17,7 +17,7 @@ notifications:
|
|
17
17
|
- travis-ci@sorah.jp
|
18
18
|
before_script:
|
19
19
|
- mkdir -p vendor/serf
|
20
|
-
- curl -L -o vendor/serf/serf.zip https://dl.bintray.com/mitchellh/serf/0.
|
20
|
+
- curl -L -o vendor/serf/serf.zip https://dl.bintray.com/mitchellh/serf/0.6.0_linux_amd64.zip
|
21
21
|
- unzip -d vendor/serf vendor/serf/serf.zip
|
22
22
|
- export PATH=$PWD/vendor/serf:$PATH
|
23
23
|
script: bundle exec rspec -fd ./spec
|
data/lib/villein/agent.rb
CHANGED
@@ -11,6 +11,7 @@ module Villein
|
|
11
11
|
class Agent < Client
|
12
12
|
class AlreadyStarted < Exception; end
|
13
13
|
class NotRunning < Exception; end
|
14
|
+
class ResponderExists < Exception; end
|
14
15
|
|
15
16
|
EVENT_HANDLER_SH = File.expand_path(File.join(__dir__, '..', '..', 'misc', 'villein-event-handler'))
|
16
17
|
|
@@ -33,7 +34,9 @@ module Villein
|
|
33
34
|
@custom_event_handlers, @replay = event_handlers, replay
|
34
35
|
@initial_tags, @tags_file = tags, tags_file
|
35
36
|
@log_level, @log = log_level, log
|
37
|
+
|
36
38
|
@hooks = {}
|
39
|
+
@responders = {}
|
37
40
|
|
38
41
|
@pid, @exitstatus = nil, nil
|
39
42
|
@pid_lock = Mutex.new
|
@@ -156,6 +159,18 @@ module Villein
|
|
156
159
|
cmd.flatten.map(&:to_s)
|
157
160
|
end
|
158
161
|
|
162
|
+
##
|
163
|
+
# Respond to query events.
|
164
|
+
# Raises error when override is false and responder for given query name already exists.
|
165
|
+
def respond(name, override: false, &block)
|
166
|
+
name = name.to_s
|
167
|
+
if !override && @responders[name]
|
168
|
+
raise ResponderExists, "Responder for #{name} already exists. To force, pass `override: true`"
|
169
|
+
end
|
170
|
+
|
171
|
+
@responders[name] = block
|
172
|
+
end
|
173
|
+
|
159
174
|
private
|
160
175
|
|
161
176
|
def start_process
|
@@ -236,7 +251,7 @@ module Villein
|
|
236
251
|
break if socks[0].eof?
|
237
252
|
end
|
238
253
|
|
239
|
-
handle_event buf
|
254
|
+
handle_event buf, sock
|
240
255
|
ensure
|
241
256
|
sock.close unless sock.closed?
|
242
257
|
end
|
@@ -244,7 +259,7 @@ module Villein
|
|
244
259
|
end
|
245
260
|
end
|
246
261
|
|
247
|
-
def handle_event(json)
|
262
|
+
def handle_event(json, sock)
|
248
263
|
event_payload = JSON.parse(json)
|
249
264
|
event = Event.new(event_payload['env'], payload: event_payload['input'])
|
250
265
|
|
@@ -252,8 +267,15 @@ module Villein
|
|
252
267
|
|
253
268
|
call_hooks event.type.gsub(/-/, '_'), event
|
254
269
|
call_hooks 'event', event
|
270
|
+
|
271
|
+
if event.type == 'query' && @responders[event.query_name]
|
272
|
+
sock.write(@responders[event.query_name].call(event))
|
273
|
+
end
|
255
274
|
rescue JSON::ParserError
|
256
275
|
# do nothing
|
276
|
+
rescue Exception => e
|
277
|
+
$stderr.puts "Exception during handling event: #{event.inspect}"
|
278
|
+
$stderr.puts e.backtrace.map { |_| _.prepend("\t") }
|
257
279
|
end
|
258
280
|
|
259
281
|
def hooks_for(name)
|
data/lib/villein/client.rb
CHANGED
@@ -6,7 +6,6 @@ module Villein
|
|
6
6
|
# Villein::Client allows you to order existing serf agent.
|
7
7
|
# You will need RPC address and agent name to command.
|
8
8
|
class Client
|
9
|
-
|
10
9
|
##
|
11
10
|
# for serf command failures
|
12
11
|
class SerfError < Exception; end
|
@@ -19,11 +18,21 @@ module Villein
|
|
19
18
|
# Error for connection failures
|
20
19
|
class SerfConnectionError < SerfError; end
|
21
20
|
|
21
|
+
##
|
22
|
+
# Error when an called serf command is not found.
|
23
|
+
class SerfCommandNotFound < SerfError; end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Error when an operation is not supported by the current version.
|
27
|
+
class InsufficientVersionError < SerfError; end
|
28
|
+
|
22
29
|
def initialize(rpc_addr, name: nil, serf: 'serf', silence: true)
|
23
30
|
@rpc_addr = rpc_addr
|
24
31
|
@name = name
|
25
32
|
@serf = serf
|
26
33
|
@silence = true
|
34
|
+
|
35
|
+
retrieve_name unless @name
|
27
36
|
end
|
28
37
|
|
29
38
|
def silence?() !!@silence; end
|
@@ -31,6 +40,15 @@ module Villein
|
|
31
40
|
|
32
41
|
attr_reader :name, :rpc_addr, :serf
|
33
42
|
|
43
|
+
##
|
44
|
+
# Returns a result of `serf info`.
|
45
|
+
# This may raise InsufficientVersionError when `serf info` is not supported.
|
46
|
+
def info
|
47
|
+
JSON.parse call_serf('info', '-format', 'json')
|
48
|
+
rescue SerfCommandNotFound
|
49
|
+
raise InsufficientVersionError, 'serf v0.6.0 or later is required to run `serf info`.'
|
50
|
+
end
|
51
|
+
|
34
52
|
def event(name, payload, coalesce: true)
|
35
53
|
options = []
|
36
54
|
|
@@ -41,6 +59,36 @@ module Villein
|
|
41
59
|
call_serf 'event', *options, name, payload
|
42
60
|
end
|
43
61
|
|
62
|
+
def query(name, payload, node: nil, tag: nil, timeout: nil, no_ack: false)
|
63
|
+
# TODO: version check
|
64
|
+
options = ['-format', 'json']
|
65
|
+
|
66
|
+
if node
|
67
|
+
node = [node] unless node.respond_to?(:each)
|
68
|
+
node.each do |n|
|
69
|
+
options << "-node=#{n}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if tag
|
74
|
+
tag = [tag] unless tag.respond_to?(:each)
|
75
|
+
tag.each do |t|
|
76
|
+
options << "-tag=#{t}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
if timeout
|
81
|
+
options << "-timeout=#{timeout}"
|
82
|
+
end
|
83
|
+
|
84
|
+
if no_ack
|
85
|
+
options << "-no-ack"
|
86
|
+
end
|
87
|
+
|
88
|
+
out = call_serf('query', *options, name, payload)
|
89
|
+
JSON.parse(out)
|
90
|
+
end
|
91
|
+
|
44
92
|
def join(addr, replay: false)
|
45
93
|
options = []
|
46
94
|
|
@@ -106,6 +154,10 @@ module Villein
|
|
106
154
|
|
107
155
|
private
|
108
156
|
|
157
|
+
def retrieve_name
|
158
|
+
@name = self.info["agent"]["name"]
|
159
|
+
end
|
160
|
+
|
109
161
|
def call_serf(cmd, *args)
|
110
162
|
status, out = IO.popen([@serf, cmd, "-rpc-addr=#{rpc_addr}", *args, err: [:child, :out]], 'r') do |io|
|
111
163
|
_, s = Process.waitpid2(io.pid)
|
@@ -118,6 +170,8 @@ module Villein
|
|
118
170
|
raise SerfConnectionError, out.chomp
|
119
171
|
when /exceeds limit of \d+ bytes$/
|
120
172
|
raise LengthExceedsLimitError, out.chomp
|
173
|
+
when /^Available commands are:/
|
174
|
+
raise SerfCommandNotFound
|
121
175
|
else
|
122
176
|
raise SerfError, out.chomp
|
123
177
|
end
|
data/lib/villein/version.rb
CHANGED
data/misc/villein-event-handler
CHANGED
@@ -1,2 +1,15 @@
|
|
1
|
-
#!/bin/
|
2
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'json'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
sock = TCPSocket.new(ARGV[0], ARGV[1].to_i)
|
6
|
+
|
7
|
+
serf_env = Hash[ENV.select{|k,v|/SERF/ =~ k}]
|
8
|
+
input = $stdin.read
|
9
|
+
|
10
|
+
sock.puts({env: serf_env, input: input}.to_json)
|
11
|
+
sock.close_write
|
12
|
+
|
13
|
+
if ENV["SERF_EVENT"] == "query"
|
14
|
+
$stdout.write sock.read
|
15
|
+
end
|
data/spec/agent_spec.rb
CHANGED
@@ -76,6 +76,21 @@ describe Villein::Agent do
|
|
76
76
|
expect(received2.type).to eq 'member-join'
|
77
77
|
end
|
78
78
|
|
79
|
+
it "can respond to queries" do
|
80
|
+
agent.respond("hey") do |e|
|
81
|
+
expect(e).to be_a(Villein::Event)
|
82
|
+
"hello"
|
83
|
+
end
|
84
|
+
|
85
|
+
agent.start!
|
86
|
+
Thread.new { agent.wait_for_ready }.join(5)
|
87
|
+
response = agent.query("hey", '')
|
88
|
+
agent.stop!
|
89
|
+
|
90
|
+
expect(response["Responses"].values.first).to eq("hello")
|
91
|
+
|
92
|
+
end
|
93
|
+
|
79
94
|
it "can handle unexpected stop" do
|
80
95
|
received = nil
|
81
96
|
agent.on_stop { |status| received = status }
|
data/spec/client_spec.rb
CHANGED
@@ -50,6 +50,71 @@ describe Villein::Client do
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
describe "#initialize" do
|
54
|
+
context "without name" do
|
55
|
+
subject(:client) { described_class.new('x.x.x.x:nnnn') }
|
56
|
+
|
57
|
+
it "retrieves name using #info" do
|
58
|
+
# we can't use allow(client) here because it calls #initialize!
|
59
|
+
allow_any_instance_of(described_class).to receive(:info).and_return(
|
60
|
+
"agent" => {"name" => "the-name"},
|
61
|
+
)
|
62
|
+
|
63
|
+
expect(client.name).to eq('the-name')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#info" do
|
69
|
+
let(:json) { (<<-EOJ).gsub(/\n|\s+/,'') }
|
70
|
+
{
|
71
|
+
"agent": {
|
72
|
+
"name": "foo"
|
73
|
+
},
|
74
|
+
"runtime": {
|
75
|
+
"arch": "amd64",
|
76
|
+
"cpu_count": "8",
|
77
|
+
"goroutines": "22",
|
78
|
+
"max_procs": "1",
|
79
|
+
"os": "darwin",
|
80
|
+
"version": "go1.2"
|
81
|
+
},
|
82
|
+
"serf": {
|
83
|
+
"event_queue": "0",
|
84
|
+
"event_time": "1",
|
85
|
+
"failed": "0",
|
86
|
+
"intent_queue": "0",
|
87
|
+
"left": "0",
|
88
|
+
"member_time": "2",
|
89
|
+
"members": "2",
|
90
|
+
"query_queue": "0",
|
91
|
+
"query_time": "3"
|
92
|
+
},
|
93
|
+
"tags": {
|
94
|
+
"thisis": "tag"
|
95
|
+
}
|
96
|
+
}
|
97
|
+
EOJ
|
98
|
+
|
99
|
+
subject(:info) { client.info }
|
100
|
+
|
101
|
+
it "returns `serf info`" do
|
102
|
+
expect_serf('info', '-format', 'json', message: json)
|
103
|
+
|
104
|
+
expect(info).to eq(JSON.parse(json))
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when not available" do
|
108
|
+
it "raises error" do
|
109
|
+
expect_serf('info', '-format', 'json', message: 'Available commands are:', success: false)
|
110
|
+
|
111
|
+
expect { info }.to raise_error(Villein::Client::InsufficientVersionError)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
include_examples "failure cases"
|
116
|
+
end
|
117
|
+
|
53
118
|
describe "#event" do
|
54
119
|
subject { client.event('test', 'payload') }
|
55
120
|
|
@@ -84,7 +149,76 @@ describe Villein::Client do
|
|
84
149
|
'Error sending event: user event exceeds limit of 256 bytes')
|
85
150
|
end
|
86
151
|
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "#query" do
|
155
|
+
let(:json) { (<<-EOJ).gsub(/\n|\s+/,'') }
|
156
|
+
{"Acks":["foo","bar"], "Responses":{"foo":"response"}}
|
157
|
+
EOJ
|
87
158
|
|
159
|
+
subject(:query) { client.query('test', 'payload') }
|
160
|
+
|
161
|
+
it "sends query event" do
|
162
|
+
expect_serf('query', '-format', 'json', 'test', 'payload', message: json)
|
163
|
+
|
164
|
+
expect(query).to eq(JSON.parse(json))
|
165
|
+
end
|
166
|
+
|
167
|
+
context "with node filter" do
|
168
|
+
context "in String" do
|
169
|
+
subject(:query) { client.query('test', 'payload', node: 'foo') }
|
170
|
+
|
171
|
+
it "queries with -node" do
|
172
|
+
expect_serf('query', '-format', 'json', '-node=foo', 'test', 'payload', message: json)
|
173
|
+
query
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "in Array" do
|
178
|
+
subject(:query) { client.query('test', 'payload', node: %w(foo bar)) }
|
179
|
+
|
180
|
+
it "queries with -node" do
|
181
|
+
expect_serf('query', '-format', 'json', '-node=foo', '-node=bar', 'test', 'payload', message: json)
|
182
|
+
query
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "with tag filter" do
|
188
|
+
context "in String" do
|
189
|
+
subject(:query) { client.query('test', 'payload', tag: 'foo') }
|
190
|
+
|
191
|
+
it "queries with -tag" do
|
192
|
+
expect_serf('query', '-format', 'json', '-tag=foo', 'test', 'payload', message: json)
|
193
|
+
query
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "in Array" do
|
198
|
+
subject(:query) { client.query('test', 'payload', tag: %w(foo bar)) }
|
199
|
+
|
200
|
+
it "queries with -tag" do
|
201
|
+
expect_serf('query', '-format', 'json', '-tag=foo', '-tag=bar', 'test', 'payload', message: json)
|
202
|
+
query
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
include_examples "failure cases"
|
208
|
+
|
209
|
+
context "when length exceeds limit" do
|
210
|
+
it "raises error" do
|
211
|
+
expect_serf('query', '-format', 'json', 'test', 'payload',
|
212
|
+
success: false,
|
213
|
+
message: 'Error sending event: query exceeds limit of 1024 bytes')
|
214
|
+
|
215
|
+
expect {
|
216
|
+
subject
|
217
|
+
}.to raise_error(
|
218
|
+
Villein::Client::LengthExceedsLimitError,
|
219
|
+
'Error sending event: query exceeds limit of 1024 bytes')
|
220
|
+
end
|
221
|
+
end
|
88
222
|
end
|
89
223
|
|
90
224
|
describe "#join" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: villein
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shota Fukumori (sora_h)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|