villein 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +1 -0
- data/Makefile +4 -0
- data/extconf.rb +15 -0
- data/lib/villein/agent.rb +25 -11
- data/lib/villein/version.rb +1 -1
- data/misc/villein-event-handler.rb +21 -0
- data/spec/event_handler_spec.rb +62 -0
- data/src/villein-event-handler.c +187 -0
- data/villein.gemspec +1 -0
- metadata +10 -4
- data/misc/villein-event-handler +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af36f01ce7087aedfc374cb6541e40bbb91a8321
|
4
|
+
data.tar.gz: 83612d9193d206eed0770bcee0fbba3ce773e258
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40b1b349f00083a0e2a662c03f4e82a13d1d204f0a95e480197c58879fdf7f79a6d5ba8384b64072b7c84907f053623a021a4aaa371043630329868c19f3ff45
|
7
|
+
data.tar.gz: 43e8c0e97b37961345414d56b9544d3e93818ed2215ef0b39bebb904072f3c5e04fabb921e1b7793056df45bbdc955b6aac1784034b1247924c64c32e6983871
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Makefile
ADDED
data/extconf.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# This extconf doesn't configure Ruby C extension, just builds
|
2
|
+
# misc/villein-event-handler which is out of ruby environment.
|
3
|
+
#
|
4
|
+
require 'rbconfig'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
exit if /mswin/ === RUBY_PLATFORM
|
8
|
+
|
9
|
+
misc = File.join(File.dirname(__FILE__), 'misc')
|
10
|
+
src = File.join(File.dirname(__FILE__), 'src')
|
11
|
+
|
12
|
+
Kernel.exec RbConfig::CONFIG["CC"],
|
13
|
+
"-Wall",
|
14
|
+
"-o", File.join(misc, 'villein-event-handler'),
|
15
|
+
File.join(src, 'villein-event-handler.c')
|
data/lib/villein/agent.rb
CHANGED
@@ -13,8 +13,14 @@ module Villein
|
|
13
13
|
class NotRunning < Exception; end
|
14
14
|
class ResponderExists < Exception; end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
EVENT_HANDLER_NATIVE = File.expand_path(File.join(__dir__, '..', '..', 'misc', 'villein-event-handler'))
|
17
|
+
EVENT_HANDLER_RB = File.expand_path(File.join(__dir__, '..', '..', 'misc', 'villein-event-handler.rb'))
|
18
|
+
|
19
|
+
EVENT_HANDLER = if File.exist?(EVENT_HANDLER_NATIVE)
|
20
|
+
EVENT_HANDLER_NATIVE
|
21
|
+
else
|
22
|
+
EVENT_HANDLER_RB
|
23
|
+
end
|
18
24
|
def initialize(serf: 'serf',
|
19
25
|
node: Socket.gethostname,
|
20
26
|
rpc_addr: '127.0.0.1:7373', bind: nil, iface: nil, advertise: nil,
|
@@ -23,7 +29,8 @@ module Villein
|
|
23
29
|
encrypt: nil, profile: nil, protocol: nil,
|
24
30
|
event_handlers: [], replay: nil,
|
25
31
|
tags: {}, tags_file: nil,
|
26
|
-
log_level: :info, log: File::NULL
|
32
|
+
log_level: :info, log: File::NULL,
|
33
|
+
villein_handler: EVENT_HANDLER)
|
27
34
|
@serf = serf
|
28
35
|
@name = node
|
29
36
|
@rpc_addr = rpc_addr
|
@@ -34,6 +41,7 @@ module Villein
|
|
34
41
|
@custom_event_handlers, @replay = event_handlers, replay
|
35
42
|
@initial_tags, @tags_file = tags, tags_file
|
36
43
|
@log_level, @log = log_level, log
|
44
|
+
@villein_handler = villein_handler
|
37
45
|
|
38
46
|
@hooks = {}
|
39
47
|
@responders = {}
|
@@ -141,7 +149,7 @@ module Villein
|
|
141
149
|
|
142
150
|
cmd << [
|
143
151
|
'-event-handler',
|
144
|
-
[
|
152
|
+
[@villein_handler, *event_listener_addr].join(' ')
|
145
153
|
]
|
146
154
|
|
147
155
|
@custom_event_handlers.each do |handler|
|
@@ -239,15 +247,16 @@ module Villein
|
|
239
247
|
end
|
240
248
|
|
241
249
|
def event_listener_loop
|
242
|
-
|
243
|
-
Thread.new do
|
250
|
+
loop do
|
251
|
+
Thread.new(@event_listener_server.accept) do |sock|
|
244
252
|
begin
|
245
|
-
buf = ""
|
253
|
+
buf, obuf = "", ""
|
246
254
|
loop do
|
247
255
|
socks, _, _ = IO.select([sock], nil, nil, 5)
|
248
256
|
break unless socks
|
249
257
|
|
250
|
-
socks[0].read_nonblock(
|
258
|
+
socks[0].read_nonblock(2048, obuf)
|
259
|
+
buf << obuf
|
251
260
|
break if socks[0].eof?
|
252
261
|
end
|
253
262
|
|
@@ -259,9 +268,14 @@ module Villein
|
|
259
268
|
end
|
260
269
|
end
|
261
270
|
|
262
|
-
def handle_event(
|
263
|
-
|
264
|
-
|
271
|
+
def handle_event(payload, sock)
|
272
|
+
env_payload, input = payload.split(/\0\0/,2) # ['name=val', 'name=val', '', 'input']
|
273
|
+
|
274
|
+
env = Hash[env_payload.split(/\0/).map do |line|
|
275
|
+
line.split(/=/,2)
|
276
|
+
end]
|
277
|
+
|
278
|
+
event = Event.new(env, payload: input)
|
265
279
|
|
266
280
|
@event_received = true
|
267
281
|
|
data/lib/villein/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#require 'json'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
sock = TCPSocket.new(ARGV[0], ARGV[1].to_i)
|
6
|
+
sock.set_encoding(Encoding::ASCII_8BIT)
|
7
|
+
|
8
|
+
|
9
|
+
ENV.each do |name, value|
|
10
|
+
next unless name.start_with?('SERF'.freeze)
|
11
|
+
|
12
|
+
sock.write "#{name}=#{value}\0"
|
13
|
+
end
|
14
|
+
|
15
|
+
sock.write "\0"
|
16
|
+
sock.write $stdin.read
|
17
|
+
sock.close_write
|
18
|
+
|
19
|
+
if ENV["SERF_EVENT"] == "query"
|
20
|
+
$stdout.write sock.read
|
21
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
describe 'event handler' do
|
5
|
+
%w(villein-event-handler villein-event-handler.rb).each do |handler_name|
|
6
|
+
_handler = File.join(__dir__, '..', 'misc', handler_name)
|
7
|
+
opts = File.exist?(_handler) ? {} : {pending: "#{handler_name} not exists"}
|
8
|
+
describe handler_name, opts do
|
9
|
+
let(:handler) { _handler }
|
10
|
+
|
11
|
+
def run_handler(env: {'SERF_EVENT' => 'user', 'SERF_USER_EVENT' => 'test'}, input: nil, response: nil)
|
12
|
+
serv = TCPServer.new('localhost', 0)
|
13
|
+
sockout = nil
|
14
|
+
serv_thread = Thread.new do
|
15
|
+
begin
|
16
|
+
sock = serv.accept
|
17
|
+
|
18
|
+
sockout = sock.read
|
19
|
+
sock.write response if response
|
20
|
+
sock.close_write
|
21
|
+
ensure
|
22
|
+
sock.close if sock && !sock.closed?
|
23
|
+
serv.close if serv && !serv.closed?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
serv_thread.abort_on_exception = true
|
27
|
+
|
28
|
+
stdout = nil
|
29
|
+
IO.popen([env, handler, serv.addr[-1].to_s, serv.addr[1].to_s], 'r+') do |io|
|
30
|
+
io.write(input) if input
|
31
|
+
io.close_write
|
32
|
+
stdout = io.read
|
33
|
+
Process.waitpid2 io.pid
|
34
|
+
end
|
35
|
+
|
36
|
+
serv_thread.join
|
37
|
+
return stdout, sockout
|
38
|
+
end
|
39
|
+
|
40
|
+
it "reports environment variables" do
|
41
|
+
stdout, sockout = run_handler
|
42
|
+
expect(sockout).to include("SERF_EVENT=user\0")
|
43
|
+
expect(sockout).to include("SERF_USER_EVENT=test\0")
|
44
|
+
expect(sockout).to match(/(user|test)\0\0\z/)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "pass stdin to socket" do
|
48
|
+
stdout, sockout = run_handler(input: "foo\n")
|
49
|
+
expect(sockout).to include("SERF_EVENT=user\0")
|
50
|
+
expect(sockout).to match(/(user|test)\0\0foo\n\z/m)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "if SERF_EVENT=query" do
|
54
|
+
it "pass socket input to stdout" do
|
55
|
+
stdout, sockout = run_handler(env: {'SERF_EVENT' => 'query'}, input: "foo\n", response: "bar\n")
|
56
|
+
expect(sockout).to match(/SERF_EVENT=query\0\0foo\n\z/m)
|
57
|
+
expect(stdout).to eq("bar\n")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <unistd.h>
|
4
|
+
|
5
|
+
#include <errno.h>
|
6
|
+
|
7
|
+
#include <string.h>
|
8
|
+
|
9
|
+
#include <sys/types.h>
|
10
|
+
#include <sys/stat.h>
|
11
|
+
|
12
|
+
#include <fcntl.h>
|
13
|
+
#include <sys/select.h>
|
14
|
+
|
15
|
+
#include <netdb.h>
|
16
|
+
#include <sys/socket.h>
|
17
|
+
#include <arpa/inet.h>
|
18
|
+
#include <netinet/in.h>
|
19
|
+
|
20
|
+
#define BUFSIZE 2048
|
21
|
+
|
22
|
+
typedef struct {
|
23
|
+
const char *hostname;
|
24
|
+
const char *port;
|
25
|
+
struct addrinfo *host_head;
|
26
|
+
int sock;
|
27
|
+
} Mission;
|
28
|
+
|
29
|
+
extern char **environ;
|
30
|
+
|
31
|
+
void
|
32
|
+
cleanup(Mission *mission)
|
33
|
+
{
|
34
|
+
if (mission->host_head != NULL)
|
35
|
+
freeaddrinfo(mission->host_head);
|
36
|
+
if (mission->sock != -1)
|
37
|
+
close(mission->sock);
|
38
|
+
}
|
39
|
+
|
40
|
+
int
|
41
|
+
is_query_p()
|
42
|
+
{
|
43
|
+
return getenv("SERF_EVENT") != NULL && strcmp(getenv("SERF_EVENT"), "query") == 0;
|
44
|
+
}
|
45
|
+
|
46
|
+
void
|
47
|
+
enable_nonblock(Mission *mission, int fd)
|
48
|
+
{
|
49
|
+
errno = 0;
|
50
|
+
unsigned int flags = fcntl(fd, F_GETFL);
|
51
|
+
|
52
|
+
if (errno != 0) {
|
53
|
+
perror("oops: F_GETCL");
|
54
|
+
exit(1);
|
55
|
+
}
|
56
|
+
|
57
|
+
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
58
|
+
perror("oops: F_SETCL");
|
59
|
+
exit(1);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
void
|
64
|
+
connect_villein(Mission *mission)
|
65
|
+
{
|
66
|
+
int err;
|
67
|
+
struct addrinfo hints, *host;
|
68
|
+
|
69
|
+
memset(&hints, 0, sizeof(hints));
|
70
|
+
hints.ai_socktype = SOCK_STREAM;
|
71
|
+
hints.ai_family = PF_UNSPEC;
|
72
|
+
|
73
|
+
if ((err = getaddrinfo(mission->hostname, mission->port, &hints, &mission->host_head)) != 0) {
|
74
|
+
fprintf(stderr, "oops: getaddrinfo(%s, %s) error: %s\n", mission->hostname, mission->port, gai_strerror(err));
|
75
|
+
exit(1);
|
76
|
+
}
|
77
|
+
|
78
|
+
for(host = mission->host_head; host != NULL; host = host->ai_next) {
|
79
|
+
if ((mission->sock = socket(host->ai_family, host->ai_socktype, host->ai_protocol)) < 0) {
|
80
|
+
continue;
|
81
|
+
}
|
82
|
+
|
83
|
+
if (connect(mission->sock, host->ai_addr, host->ai_addrlen) != 0) {
|
84
|
+
close(mission->sock);
|
85
|
+
continue;
|
86
|
+
}
|
87
|
+
|
88
|
+
break;
|
89
|
+
}
|
90
|
+
|
91
|
+
if (host == NULL) {
|
92
|
+
fprintf(stderr, "failed to connect host");
|
93
|
+
cleanup(mission);
|
94
|
+
exit(1);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
void
|
99
|
+
report_environ(Mission *mission)
|
100
|
+
{
|
101
|
+
char **env = environ;
|
102
|
+
|
103
|
+
while (*env) {
|
104
|
+
write(mission->sock, *env, strlen(*env) + 1); /* \0 */
|
105
|
+
env++;
|
106
|
+
}
|
107
|
+
write(mission->sock, "\0", 1);
|
108
|
+
}
|
109
|
+
|
110
|
+
void
|
111
|
+
copy_stream(Mission *mission, int from_fd, int to_fd)
|
112
|
+
{
|
113
|
+
int maxfd = from_fd < to_fd ? to_fd : from_fd;
|
114
|
+
int retval;
|
115
|
+
fd_set rfds;
|
116
|
+
char *buf;
|
117
|
+
buf = malloc(BUFSIZE);
|
118
|
+
|
119
|
+
FD_ZERO(&rfds);
|
120
|
+
FD_SET(from_fd, &rfds);
|
121
|
+
|
122
|
+
struct stat from_fdstat;
|
123
|
+
fstat(from_fd, &from_fdstat);
|
124
|
+
|
125
|
+
|
126
|
+
while (1) {
|
127
|
+
ssize_t count = 0;
|
128
|
+
retval = select(maxfd + 1, &rfds, NULL, NULL, NULL);
|
129
|
+
|
130
|
+
if (retval == -1) {
|
131
|
+
perror("oops: copy select");
|
132
|
+
free(buf);
|
133
|
+
cleanup(mission); exit(1);
|
134
|
+
}
|
135
|
+
|
136
|
+
errno = 0;
|
137
|
+
|
138
|
+
if (S_ISSOCK(from_fdstat.st_mode)) {
|
139
|
+
count = recv(from_fd, buf, BUFSIZE, 0);
|
140
|
+
}
|
141
|
+
else {
|
142
|
+
count = read(from_fd, buf, BUFSIZE);
|
143
|
+
}
|
144
|
+
|
145
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
146
|
+
if (errno != 0) {
|
147
|
+
perror("oops: copy read");
|
148
|
+
free(buf);
|
149
|
+
cleanup(mission); exit(1);
|
150
|
+
}
|
151
|
+
if (count == 0) break;
|
152
|
+
|
153
|
+
write(to_fd, buf, count);
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
int
|
158
|
+
main(int argc, const char *argv[])
|
159
|
+
{
|
160
|
+
if (argc < 3) {
|
161
|
+
fprintf(stderr, "usage: $0 host port\n");
|
162
|
+
return 2;
|
163
|
+
}
|
164
|
+
|
165
|
+
Mission mission;
|
166
|
+
memset(&mission, 0, sizeof(mission));
|
167
|
+
|
168
|
+
mission.hostname = argv[1];
|
169
|
+
mission.port = argv[2];
|
170
|
+
mission.sock = -1;
|
171
|
+
|
172
|
+
connect_villein(&mission);
|
173
|
+
report_environ(&mission);
|
174
|
+
|
175
|
+
enable_nonblock(&mission, 0);
|
176
|
+
copy_stream(&mission, 0, mission.sock);
|
177
|
+
shutdown(mission.sock, 1); /* close_write */
|
178
|
+
|
179
|
+
if (is_query_p()) {
|
180
|
+
enable_nonblock(&mission, mission.sock);
|
181
|
+
copy_stream(&mission, mission.sock, 1);
|
182
|
+
}
|
183
|
+
|
184
|
+
cleanup(&mission);
|
185
|
+
|
186
|
+
return 0;
|
187
|
+
}
|
data/villein.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
|
+
spec.extensions = ["extconf.rb"]
|
20
21
|
|
21
22
|
spec.add_development_dependency "rspec", "2.14.1"
|
22
23
|
spec.add_development_dependency "bundler", "~> 1.5"
|
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.4.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-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -71,7 +71,8 @@ email:
|
|
71
71
|
- sorah@cookpad.com
|
72
72
|
- her@sorah.jp
|
73
73
|
executables: []
|
74
|
-
extensions:
|
74
|
+
extensions:
|
75
|
+
- extconf.rb
|
75
76
|
extra_rdoc_files: []
|
76
77
|
files:
|
77
78
|
- ".gitignore"
|
@@ -79,20 +80,24 @@ files:
|
|
79
80
|
- ".travis.yml"
|
80
81
|
- Gemfile
|
81
82
|
- LICENSE.txt
|
83
|
+
- Makefile
|
82
84
|
- README.md
|
83
85
|
- Rakefile
|
86
|
+
- extconf.rb
|
84
87
|
- lib/villein.rb
|
85
88
|
- lib/villein/agent.rb
|
86
89
|
- lib/villein/client.rb
|
87
90
|
- lib/villein/event.rb
|
88
91
|
- lib/villein/tags.rb
|
89
92
|
- lib/villein/version.rb
|
90
|
-
- misc/villein-event-handler
|
93
|
+
- misc/villein-event-handler.rb
|
91
94
|
- spec/agent_spec.rb
|
92
95
|
- spec/client_spec.rb
|
96
|
+
- spec/event_handler_spec.rb
|
93
97
|
- spec/event_spec.rb
|
94
98
|
- spec/spec_helper.rb
|
95
99
|
- spec/tags_spec.rb
|
100
|
+
- src/villein-event-handler.c
|
96
101
|
- villein.gemspec
|
97
102
|
homepage: ''
|
98
103
|
licenses:
|
@@ -121,6 +126,7 @@ summary: Use `serf` (serfdom.io) from Ruby.
|
|
121
126
|
test_files:
|
122
127
|
- spec/agent_spec.rb
|
123
128
|
- spec/client_spec.rb
|
129
|
+
- spec/event_handler_spec.rb
|
124
130
|
- spec/event_spec.rb
|
125
131
|
- spec/spec_helper.rb
|
126
132
|
- spec/tags_spec.rb
|
data/misc/villein-event-handler
DELETED
@@ -1,15 +0,0 @@
|
|
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
|