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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 312c4f44e76ab04dfc5432713777cab81fe3b841
4
- data.tar.gz: de6c4fcf92f268aff30976f55a10efdd53631f89
3
+ metadata.gz: af36f01ce7087aedfc374cb6541e40bbb91a8321
4
+ data.tar.gz: 83612d9193d206eed0770bcee0fbba3ce773e258
5
5
  SHA512:
6
- metadata.gz: be8f871517ddb03e03db209d76351546068805aa6d7e4fae44903f0444a944aece28d630551c14333c0b7ec72039280d2047776da780251882e96c9438ad325b
7
- data.tar.gz: d1944855fe12a066af5a9bc54a91be22ca47bb09aee40f809e1f09d47fe3a415e65a20c78bf21e70c7f3f48eaeffadd5819e7a1803e901f7efb5855a182d429f
6
+ metadata.gz: 40b1b349f00083a0e2a662c03f4e82a13d1d204f0a95e480197c58879fdf7f79a6d5ba8384b64072b7c84907f053623a021a4aaa371043630329868c19f3ff45
7
+ data.tar.gz: 43e8c0e97b37961345414d56b9544d3e93818ed2215ef0b39bebb904072f3c5e04fabb921e1b7793056df45bbdc955b6aac1784034b1247924c64c32e6983871
data/.gitignore CHANGED
@@ -20,3 +20,5 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ *.dSYM
24
+ misc/villein-event-handler
data/.travis.yml CHANGED
@@ -16,6 +16,7 @@ notifications:
16
16
  email:
17
17
  - travis-ci@sorah.jp
18
18
  before_script:
19
+ - ruby extconf.rb
19
20
  - mkdir -p vendor/serf
20
21
  - curl -L -o vendor/serf/serf.zip https://dl.bintray.com/mitchellh/serf/0.6.0_linux_amd64.zip
21
22
  - unzip -d vendor/serf vendor/serf/serf.zip
data/Makefile ADDED
@@ -0,0 +1,4 @@
1
+ all:
2
+ @echo ok
3
+ install:
4
+ @echo ok
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
- EVENT_HANDLER_SH = File.expand_path(File.join(__dir__, '..', '..', 'misc', 'villein-event-handler'))
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
- [EVENT_HANDLER_SH, *event_listener_addr].join(' ')
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
- while sock = @event_listener_server.accept
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(1024, buf)
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(json, sock)
263
- event_payload = JSON.parse(json)
264
- event = Event.new(event_payload['env'], payload: event_payload['input'])
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
 
@@ -1,3 +1,3 @@
1
1
  module Villein
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -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.3.2
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-07-31 00:00:00.000000000 Z
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
@@ -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