sub_zero 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sub_zero/cli/app.rb +2 -1
- data/lib/sub_zero/client.rb +21 -24
- data/lib/sub_zero/configuration.rb +10 -0
- data/lib/sub_zero/environment.rb +10 -0
- data/lib/sub_zero/loggable.rb +58 -0
- data/lib/sub_zero/main/common.rb +3 -4
- data/lib/sub_zero/message/parser.rb +65 -0
- data/lib/sub_zero/message/validations.rb +29 -0
- data/lib/sub_zero/message.rb +49 -0
- data/lib/sub_zero/service/handler.rb +30 -0
- data/lib/sub_zero/service/router.rb +40 -0
- data/lib/sub_zero/service/runner.rb +33 -0
- data/lib/sub_zero/service.rb +31 -0
- data/lib/sub_zero/socket/sender.rb +25 -0
- data/lib/sub_zero/socket/server.rb +59 -0
- data/lib/sub_zero/socket.rb +65 -0
- data/lib/sub_zero/version.rb +1 -1
- data/lib/sub_zero.rb +15 -2
- data/spec/spec_helper.rb +9 -0
- data/spec/sub_zero/client_spec.rb +72 -0
- data/spec/sub_zero/environment_spec.rb +16 -0
- data/spec/sub_zero/service_spec.rb +38 -0
- data/sub_zero.gemspec +4 -1
- metadata +63 -4
- data/lib/sub_zero/client/configuration.rb +0 -12
- data/lib/sub_zero/client/socket.rb +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41d022d98d371aa71c53fbe297f481015d5293f7
|
4
|
+
data.tar.gz: d6f1bdd5be50e6ed39640cafe7e350647e8f38e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 127f4ed3c1f8606bbdfb28203aefb15743cb4a54d5619e4c6033faab2bccc806c1bd9ca9be0f6c71da013c966c7e93bc913aea9deb5e68c8ba98a2e3e9068efa
|
7
|
+
data.tar.gz: af1707609bc526c600b64a60d909f97bce87b7e9731c3b72eaee9824d97161726517c5fd4319673905b4ab3cf4849ecf8cab63631427c6033eba52727cf30b28
|
data/lib/sub_zero/cli/app.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module SubZero
|
2
2
|
module CLI
|
3
3
|
class App < Thor
|
4
|
-
register SubZero::CLI::New, 'new', 'new TYPE NAME',
|
4
|
+
register SubZero::CLI::New, 'new', 'new TYPE NAME',
|
5
|
+
'Creates a new TYPE (service/client) named NAME'
|
5
6
|
end
|
6
7
|
end
|
7
8
|
end
|
data/lib/sub_zero/client.rb
CHANGED
@@ -1,12 +1,5 @@
|
|
1
|
-
require 'active_support/string_inquirer'
|
2
|
-
require 'active_support/hash_with_indifferent_access'
|
3
|
-
|
4
1
|
module SubZero
|
5
2
|
class Client
|
6
|
-
autoload :Configuration, 'sub_zero/client/configuration'
|
7
|
-
autoload :Socket, 'sub_zero/client/socket'
|
8
|
-
|
9
|
-
include Socket
|
10
3
|
|
11
4
|
attr_reader :sid, :config
|
12
5
|
|
@@ -15,11 +8,24 @@ module SubZero
|
|
15
8
|
yield @config if block_given?
|
16
9
|
end
|
17
10
|
|
11
|
+
def socket
|
12
|
+
Socket.new config
|
13
|
+
end
|
14
|
+
|
18
15
|
def call verb, payload = {}, options = {}
|
19
16
|
sz_verb = verb.to_s.upcase.gsub('_', '/')
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
|
18
|
+
request = Message.new(type: 'REQ', subtype: sid, verb: sz_verb,
|
19
|
+
payload: payload, options: options)
|
20
|
+
|
21
|
+
response = socket.call(request)
|
22
|
+
|
23
|
+
if block_given?
|
24
|
+
yield response
|
25
|
+
else
|
26
|
+
fail Error.new(response) if response.error?
|
27
|
+
response.payload
|
28
|
+
end
|
23
29
|
end
|
24
30
|
|
25
31
|
def call! verb, payload = {}, options = {}, &block
|
@@ -32,33 +38,24 @@ module SubZero
|
|
32
38
|
|
33
39
|
def method_missing method, payload = {}, options = {}, &block
|
34
40
|
if method.to_s.end_with? '!'
|
35
|
-
call! method.delete('!'), payload, options, &block
|
41
|
+
call! method.to_s.delete('!'), payload, options, &block
|
36
42
|
else
|
37
43
|
call method, payload, options, &block
|
38
44
|
end
|
39
45
|
end
|
40
46
|
|
41
|
-
def handle_result status, result
|
42
|
-
case status
|
43
|
-
when :ok
|
44
|
-
result
|
45
|
-
when :error
|
46
|
-
fail Error.new(result['error'])
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
47
|
class Error < ::StandardError
|
51
48
|
|
52
49
|
attr_reader :result
|
53
50
|
|
54
|
-
def initialize
|
55
|
-
super
|
51
|
+
def initialize response
|
52
|
+
super response.payload['message']
|
56
53
|
set_backtrace caller
|
57
|
-
@result =
|
54
|
+
@result = response
|
58
55
|
end
|
59
56
|
|
60
57
|
def code
|
61
|
-
result['code']
|
58
|
+
result.payload['code']
|
62
59
|
end
|
63
60
|
|
64
61
|
def status
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
module SubZero
|
4
|
+
module Loggable
|
5
|
+
|
6
|
+
COLORS = [:red, :green, :yellow, :blue, :magenta, :cyan,
|
7
|
+
:light_red, :light_green, :light_yellow, :light_blue, :light_magenta, :light_cyan]
|
8
|
+
|
9
|
+
SEVERITIES = [:info, :warn, :error, :debug]
|
10
|
+
|
11
|
+
SEVERITY_COLORS = { info: :light_blue }
|
12
|
+
|
13
|
+
SEVERITIES.each do |severity|
|
14
|
+
define_method severity do |message, context = {}|
|
15
|
+
log message, { severity: severity }.merge(context)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def error exception, message
|
20
|
+
super message, error: exception
|
21
|
+
end
|
22
|
+
|
23
|
+
def log message, context = {}
|
24
|
+
sender_class = self.kind_of?(Class) ? self : self.class
|
25
|
+
context.merge! sender_class: sender_class.to_s
|
26
|
+
context[:severity] ||= :info
|
27
|
+
|
28
|
+
puts log_colored_message(message, context)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.included(base)
|
32
|
+
base.extend self
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def log_colored_message(message, context)
|
38
|
+
msg = log_colored_severity(context[:severity])
|
39
|
+
msg << " #{Time.now.strftime('%Y%m%d%H%M%S%L')}"
|
40
|
+
|
41
|
+
if sender_class = context.delete(:sender_class)
|
42
|
+
msg = log_colored_term(sender_class)
|
43
|
+
end
|
44
|
+
|
45
|
+
msg << " #{message}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def log_colored_severity(severity)
|
49
|
+
severity.to_s.colorize SEVERITY_COLORS[severity.to_sym]
|
50
|
+
end
|
51
|
+
|
52
|
+
def log_colored_term(term)
|
53
|
+
index = Integer("0x#{Digest::MD5.hexdigest(term)[0, 2]}") % 12
|
54
|
+
term.colorize COLORS[index]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/sub_zero/main/common.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
module SubZero
|
2
|
+
class Message
|
3
|
+
module Parser
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def to_source
|
7
|
+
frames = []
|
8
|
+
|
9
|
+
if subtype.present?
|
10
|
+
frames << "#{type}:#{subtype}"
|
11
|
+
else
|
12
|
+
frames << type
|
13
|
+
end
|
14
|
+
|
15
|
+
frames << rid
|
16
|
+
|
17
|
+
if status.present?
|
18
|
+
frames << "#{verb}:#{status}"
|
19
|
+
else
|
20
|
+
frames << verb
|
21
|
+
end
|
22
|
+
|
23
|
+
frames << payload.to_msgpack
|
24
|
+
frames << options.to_msgpack
|
25
|
+
|
26
|
+
frames
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
|
31
|
+
def from_source source
|
32
|
+
type = source.shift
|
33
|
+
|
34
|
+
if type.index ':'
|
35
|
+
type, subtype = type.split(':')
|
36
|
+
end
|
37
|
+
|
38
|
+
rid = source.shift
|
39
|
+
verb = source.shift
|
40
|
+
|
41
|
+
if verb.index ':'
|
42
|
+
verb, status = verb.split(':')
|
43
|
+
end
|
44
|
+
|
45
|
+
payload = MessagePack.unpack(source.shift).with_indifferent_access
|
46
|
+
|
47
|
+
unless source.empty?
|
48
|
+
routing_info = MessagePack.unpack(source.shift).with_indifferent_access
|
49
|
+
end
|
50
|
+
|
51
|
+
new type: type, rid: rid, verb: verb, payload: payload,
|
52
|
+
subtype: subtype, status: status, routing_info: routing_info
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse source, request = nil
|
56
|
+
from_source(source).tap do |m|
|
57
|
+
m.validate_response!(request) if request
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SubZero
|
2
|
+
class Message
|
3
|
+
module Validations
|
4
|
+
|
5
|
+
def validate_response! request
|
6
|
+
broker_reply = type == 'BRO'
|
7
|
+
service_reply = type == 'REP' && subtype == request.sid
|
8
|
+
|
9
|
+
unless broker_reply || service_reply
|
10
|
+
fail Socket::Error, "unexpected result type: #{type}, " +
|
11
|
+
"subtype: #{subtype}. " + to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
unless rid == request.rid
|
15
|
+
fail Socket::Error, "rid mismatch. sent: #{request.rid}, " +
|
16
|
+
"received: #{rid}. " + to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
unless verb == request.verb && response?
|
20
|
+
fail Socket::Error, "reply verb mismatch: #{verb}, " +
|
21
|
+
"status: #{status}. " + to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'message/parser'
|
2
|
+
require_relative 'message/validations'
|
3
|
+
|
4
|
+
module SubZero
|
5
|
+
class Message
|
6
|
+
include Parser
|
7
|
+
include Validations
|
8
|
+
|
9
|
+
attr_accessor :type,
|
10
|
+
:subtype,
|
11
|
+
:rid,
|
12
|
+
:verb,
|
13
|
+
:status,
|
14
|
+
:payload,
|
15
|
+
:options
|
16
|
+
|
17
|
+
def initialize args = {}
|
18
|
+
@type, @subtype = args.values_at(:type, :subtype)
|
19
|
+
@rid = args[:rid] || SecureRandom.uuid
|
20
|
+
@verb, @status = args.values_at(:verb, :status)
|
21
|
+
@payload = args[:payload] || {}
|
22
|
+
@options = args[:options] || {}
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :sid :subtype
|
26
|
+
alias :routing_info :options
|
27
|
+
|
28
|
+
def heartbeat?
|
29
|
+
verb == 'PONG'
|
30
|
+
end
|
31
|
+
|
32
|
+
def request?
|
33
|
+
status.blank?
|
34
|
+
end
|
35
|
+
|
36
|
+
def response?
|
37
|
+
not request?
|
38
|
+
end
|
39
|
+
|
40
|
+
def success?
|
41
|
+
status == 'OK'
|
42
|
+
end
|
43
|
+
|
44
|
+
def error?
|
45
|
+
status == 'NOK'
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SubZero
|
2
|
+
module Service
|
3
|
+
class Handler
|
4
|
+
|
5
|
+
attr_reader :message
|
6
|
+
|
7
|
+
delegate :rid, :verb, :payload, to: :message
|
8
|
+
|
9
|
+
alias :request_id :rid
|
10
|
+
alias :params :payload
|
11
|
+
|
12
|
+
def initialize message
|
13
|
+
@message = message
|
14
|
+
end
|
15
|
+
|
16
|
+
# verb, payload = nil
|
17
|
+
# payload (same verb with :OK)
|
18
|
+
def reply *args
|
19
|
+
end
|
20
|
+
|
21
|
+
def reply_with payload
|
22
|
+
reply verb, payload
|
23
|
+
end
|
24
|
+
|
25
|
+
def error code, message
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module SubZero
|
4
|
+
module Service
|
5
|
+
module Router
|
6
|
+
|
7
|
+
def routes
|
8
|
+
@routes ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def route &block
|
12
|
+
instance_eval &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def verb args
|
16
|
+
name, route = Hash[args].first
|
17
|
+
routes[name.to_s.upcase] = handler_caller(route)
|
18
|
+
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def handler_caller route
|
25
|
+
handler, action = route.to_s.split('#')
|
26
|
+
|
27
|
+
begin
|
28
|
+
handler_name = "Handlers::#{handler.camelize}"
|
29
|
+
handler_class = const_get(handler_name)
|
30
|
+
rescue
|
31
|
+
fail "Handler not found: #{handler_name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
proc { |message| handler_class.new(message).send action }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SubZero
|
2
|
+
module Runner
|
3
|
+
|
4
|
+
def run!
|
5
|
+
Daemons.run_proc sid, daemon_configuration do
|
6
|
+
start = Time.now
|
7
|
+
ping_at = start + 5
|
8
|
+
|
9
|
+
Signal.trap('INT') { socket.down!; exit }
|
10
|
+
Signal.trap('TERM') { socket.down!; exit }
|
11
|
+
|
12
|
+
socket.up!
|
13
|
+
|
14
|
+
loop do
|
15
|
+
socket.run &method(:resolve)
|
16
|
+
|
17
|
+
now = Time.now
|
18
|
+
|
19
|
+
if now >= ping_at
|
20
|
+
socket.ping
|
21
|
+
ping_at = now + 15
|
22
|
+
end
|
23
|
+
|
24
|
+
break if $stop_requested
|
25
|
+
end
|
26
|
+
|
27
|
+
socket.down!
|
28
|
+
exit 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'service/handler'
|
2
|
+
require_relative 'service/router'
|
3
|
+
require_relative 'service/runner'
|
4
|
+
|
5
|
+
module SubZero
|
6
|
+
module Service
|
7
|
+
include Router
|
8
|
+
include Loggable
|
9
|
+
include Runner
|
10
|
+
|
11
|
+
def sid service_id = nil
|
12
|
+
@sid = service_id.to_sym if service_id
|
13
|
+
@sid
|
14
|
+
end
|
15
|
+
|
16
|
+
def resolve message
|
17
|
+
if handler = routes[message.verb]
|
18
|
+
handler[message]
|
19
|
+
else
|
20
|
+
# TODO verb mismatch error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def socket
|
27
|
+
Socket.new
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SubZero
|
2
|
+
class Socket
|
3
|
+
module Sender
|
4
|
+
|
5
|
+
def call request
|
6
|
+
response = nil
|
7
|
+
t = request.options.fetch(:timeout, 1000) / 1000.0
|
8
|
+
|
9
|
+
::Timeout.timeout t do
|
10
|
+
context do |c|
|
11
|
+
socket c do |s|
|
12
|
+
send_message s, request
|
13
|
+
response = receive_message(s, request)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
response
|
19
|
+
rescue ::Timeout::Error
|
20
|
+
raise SubZero::Socket::TimeoutError
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SubZero
|
2
|
+
class Socket
|
3
|
+
module Server
|
4
|
+
|
5
|
+
def run &handle_message
|
6
|
+
context do |c|
|
7
|
+
socket c do |s|
|
8
|
+
poll s do |m|
|
9
|
+
handle_message[m]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
rescue => e
|
14
|
+
error e, 'failed while running server, going to retry'
|
15
|
+
retry
|
16
|
+
end
|
17
|
+
|
18
|
+
def up!
|
19
|
+
send_server_verb 'UP'
|
20
|
+
end
|
21
|
+
|
22
|
+
def down!
|
23
|
+
send_server_verb 'DOWN'
|
24
|
+
end
|
25
|
+
|
26
|
+
def ping
|
27
|
+
send_server_verb 'PING'
|
28
|
+
end
|
29
|
+
|
30
|
+
def send_server_verb verb
|
31
|
+
context do |c|
|
32
|
+
socket c do |s|
|
33
|
+
send_message s, Message.new(type: 'SRV', verb: verb)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def poll socket
|
39
|
+
ZMQ::Poller.new.tap do |p|
|
40
|
+
p.register socket, ZMQ::POLLIN
|
41
|
+
p.poll 1
|
42
|
+
|
43
|
+
p.readables.each do |s|
|
44
|
+
s.recv_strings source = []
|
45
|
+
|
46
|
+
begin
|
47
|
+
message = Message.parse(source)
|
48
|
+
rescue => e
|
49
|
+
error e, 'error parsing message'
|
50
|
+
end
|
51
|
+
|
52
|
+
yield message if message
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'ffi-rzmq'
|
2
|
+
require 'msgpack'
|
3
|
+
|
4
|
+
require_relative 'socket/sender'
|
5
|
+
require_relative 'socket/server'
|
6
|
+
|
7
|
+
module SubZero
|
8
|
+
class Socket
|
9
|
+
include Loggable
|
10
|
+
include Sender
|
11
|
+
include Server
|
12
|
+
|
13
|
+
class Error < StandardError; end
|
14
|
+
class TimeoutError < Socket::Error; end
|
15
|
+
|
16
|
+
attr_reader :config
|
17
|
+
|
18
|
+
def initialize config = Configuration.default
|
19
|
+
@config = config
|
20
|
+
end
|
21
|
+
|
22
|
+
def context
|
23
|
+
context = ZMQ::Context.create(1)
|
24
|
+
fail Socket::Error, 'failed to create context' unless context
|
25
|
+
yield context
|
26
|
+
check! context.terminate
|
27
|
+
rescue Socket::Error
|
28
|
+
check! context.terminate
|
29
|
+
raise
|
30
|
+
end
|
31
|
+
|
32
|
+
def socket context
|
33
|
+
socket = context.socket ZMQ::DEALER
|
34
|
+
socket.identity = SecureRandom.hex(10)
|
35
|
+
check! socket.connect("tcp://#{config[:ip]}:#{config[:port]}")
|
36
|
+
yield socket
|
37
|
+
check! socket.close
|
38
|
+
rescue Socket::Error
|
39
|
+
check! socket.close
|
40
|
+
raise
|
41
|
+
end
|
42
|
+
|
43
|
+
def send_message socket, message
|
44
|
+
frames = message.to_source
|
45
|
+
last = frames.pop
|
46
|
+
frames.each { |f| check! socket.send_string f.to_s, ZMQ::SNDMORE }
|
47
|
+
check! socket.send_string last
|
48
|
+
end
|
49
|
+
|
50
|
+
def receive_message socket, request
|
51
|
+
check! socket.recv_strings(frames = [])
|
52
|
+
Message.parse frames, request
|
53
|
+
end
|
54
|
+
|
55
|
+
def check! result_code
|
56
|
+
return if ZMQ::Util.resultcode_ok? result_code
|
57
|
+
|
58
|
+
fail Socket::Error, "operation failed, errno [#{ZMQ::Util.errno}], " +
|
59
|
+
"description [#{ZMQ::Util.error_string}]"
|
60
|
+
rescue
|
61
|
+
raise Socket::Error "operation failed, cannot have errno"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/sub_zero/version.rb
CHANGED
data/lib/sub_zero.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'securerandom'
|
3
3
|
require 'timeout'
|
4
|
+
require 'digest/md5'
|
4
5
|
|
5
6
|
require 'thor'
|
6
7
|
require 'thor/group'
|
7
8
|
|
9
|
+
require 'active_support/string_inquirer'
|
10
|
+
require 'active_support/hash_with_indifferent_access'
|
11
|
+
require 'active_support/concern'
|
12
|
+
require 'active_support/core_ext/module/delegation'
|
13
|
+
|
8
14
|
require_relative 'sub_zero/version'
|
15
|
+
require_relative 'sub_zero/environment'
|
9
16
|
require_relative 'sub_zero/main'
|
10
17
|
|
11
18
|
module SubZero
|
12
|
-
autoload :CLI,
|
13
|
-
autoload :Client,
|
19
|
+
autoload :CLI, 'sub_zero/cli'
|
20
|
+
autoload :Client, 'sub_zero/client'
|
21
|
+
autoload :Configuration, 'sub_zero/configuration'
|
22
|
+
autoload :Loggable, 'sub_zero/loggable'
|
23
|
+
autoload :Message, 'sub_zero/message'
|
24
|
+
autoload :Service, 'sub_zero/service'
|
25
|
+
autoload :Socket, 'sub_zero/socket'
|
14
26
|
extend self
|
27
|
+
extend Environment
|
15
28
|
|
16
29
|
def client service_id
|
17
30
|
block_given? ? Client.new(service_id, &block) : Client.new(service_id)
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SubZero::Client do
|
4
|
+
|
5
|
+
subject { described_class.new :sid }
|
6
|
+
|
7
|
+
let(:config) { { ip: '127.0.0.1', port: 7777 } }
|
8
|
+
|
9
|
+
its(:sid) { should eq('SID') }
|
10
|
+
its(:config) { should eq(config) }
|
11
|
+
|
12
|
+
describe '#socket' do
|
13
|
+
let(:socket) { double :socket }
|
14
|
+
|
15
|
+
before do
|
16
|
+
SubZero::Socket.stub(:new).with(config) { socket }
|
17
|
+
end
|
18
|
+
|
19
|
+
its(:socket) { should eq(socket) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#call' do
|
23
|
+
let(:socket) { double :socket }
|
24
|
+
let(:response) { double :response, error?: false, payload: { found: true } }
|
25
|
+
|
26
|
+
before do
|
27
|
+
subject.stub(:socket) { socket }
|
28
|
+
|
29
|
+
socket.should_receive :call do |request|
|
30
|
+
request.type.should eq('REQ')
|
31
|
+
request.sid.should eq('SID')
|
32
|
+
request.verb.should eq('GET/USER')
|
33
|
+
request.payload.should eq(id: 2)
|
34
|
+
request.options.should eq(timeout: 30)
|
35
|
+
response
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns response payload' do
|
40
|
+
subject.call(:get_user, { id: 2 }, { timeout: 30 }).should eq(found: true)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'injects full response in block' do
|
44
|
+
subject.call :get_user, { id: 2 }, { timeout: 30 } do |result|
|
45
|
+
result.should eq(response)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#[]' do
|
51
|
+
let(:response) { double :response }
|
52
|
+
|
53
|
+
it 'is a different way to do call' do
|
54
|
+
subject.should_receive(:call).with(:get_user) { response }
|
55
|
+
subject[:get_user].should eq(response)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'calling verbs via method missing' do
|
60
|
+
let(:response) { double :response }
|
61
|
+
let(:args) { [{ id: 2 }, { timeout: 3000 }] }
|
62
|
+
|
63
|
+
it 'calls verb with method missing name' do
|
64
|
+
subject.should_receive(:call).with(:get_user, *args) { response }
|
65
|
+
subject.get_user(*args).should eq(response)
|
66
|
+
|
67
|
+
subject.should_receive(:call!).with('get_user', *args) { response }
|
68
|
+
subject.get_user!(*args).should eq(response)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SubZero::Environment do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
Class.new { extend SubZero::Environment }
|
7
|
+
end
|
8
|
+
|
9
|
+
its(:env) { should eq('test') }
|
10
|
+
|
11
|
+
specify '#env?' do
|
12
|
+
subject.env.test?.should be_true
|
13
|
+
subject.env.development?.should be_false
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SubZero::Service do
|
4
|
+
|
5
|
+
class Auditing
|
6
|
+
extend SubZero::Service
|
7
|
+
|
8
|
+
sid :auditing
|
9
|
+
end
|
10
|
+
|
11
|
+
class Auditing
|
12
|
+
module Handlers
|
13
|
+
class Logs < SubZero::Service::Handler
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
subject { Auditing }
|
19
|
+
|
20
|
+
describe '.sid' do
|
21
|
+
it 'retrieves sid' do
|
22
|
+
subject.sid.should eq(:auditing)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.route' do
|
27
|
+
before do
|
28
|
+
subject.route do
|
29
|
+
verb list: 'logs#index'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'defines routes' do
|
34
|
+
subject.routes.key?('LIST').should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/sub_zero.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'sub_zero/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'sub_zero'
|
8
8
|
spec.version = SubZero::VERSION
|
9
9
|
spec.authors = ["Victor Rodrigues", "Bruno Antunes"]
|
10
10
|
spec.email = ["victorc.rodrigues@gmail.com", "sardaukar.siet@gmail.com"]
|
@@ -21,9 +21,12 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
22
|
spec.add_development_dependency 'rake'
|
23
23
|
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'pry'
|
24
25
|
|
25
26
|
spec.add_dependency 'thor'
|
26
27
|
spec.add_dependency 'msgpack'
|
27
28
|
spec.add_dependency 'ffi-rzmq'
|
28
29
|
spec.add_dependency 'activesupport'
|
30
|
+
spec.add_dependency 'colorize'
|
31
|
+
spec.add_dependency 'daemons'
|
29
32
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sub_zero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Rodrigues
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -53,6 +53,20 @@ dependencies:
|
|
53
53
|
- - '>='
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: pry
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: thor
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,6 +123,34 @@ dependencies:
|
|
109
123
|
- - '>='
|
110
124
|
- !ruby/object:Gem::Version
|
111
125
|
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: colorize
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :runtime
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: daemons
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :runtime
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
112
154
|
description: ZeroMQ SOA solution
|
113
155
|
email:
|
114
156
|
- victorc.rodrigues@gmail.com
|
@@ -148,12 +190,26 @@ files:
|
|
148
190
|
- lib/sub_zero/cli/templates/ruby/service/verbs.tt
|
149
191
|
- lib/sub_zero/cli/templates/ruby/service/version.tt
|
150
192
|
- lib/sub_zero/client.rb
|
151
|
-
- lib/sub_zero/
|
152
|
-
- lib/sub_zero/
|
193
|
+
- lib/sub_zero/configuration.rb
|
194
|
+
- lib/sub_zero/environment.rb
|
195
|
+
- lib/sub_zero/loggable.rb
|
153
196
|
- lib/sub_zero/main.rb
|
154
197
|
- lib/sub_zero/main/common.rb
|
198
|
+
- lib/sub_zero/message.rb
|
199
|
+
- lib/sub_zero/message/parser.rb
|
200
|
+
- lib/sub_zero/message/validations.rb
|
201
|
+
- lib/sub_zero/service.rb
|
202
|
+
- lib/sub_zero/service/handler.rb
|
203
|
+
- lib/sub_zero/service/router.rb
|
204
|
+
- lib/sub_zero/service/runner.rb
|
205
|
+
- lib/sub_zero/socket.rb
|
206
|
+
- lib/sub_zero/socket/sender.rb
|
207
|
+
- lib/sub_zero/socket/server.rb
|
155
208
|
- lib/sub_zero/version.rb
|
156
209
|
- spec/spec_helper.rb
|
210
|
+
- spec/sub_zero/client_spec.rb
|
211
|
+
- spec/sub_zero/environment_spec.rb
|
212
|
+
- spec/sub_zero/service_spec.rb
|
157
213
|
- sub_zero.gemspec
|
158
214
|
- thoughts.md
|
159
215
|
homepage: ''
|
@@ -182,3 +238,6 @@ specification_version: 4
|
|
182
238
|
summary: ZeroMQ GREAT SOA solution
|
183
239
|
test_files:
|
184
240
|
- spec/spec_helper.rb
|
241
|
+
- spec/sub_zero/client_spec.rb
|
242
|
+
- spec/sub_zero/environment_spec.rb
|
243
|
+
- spec/sub_zero/service_spec.rb
|
@@ -1,98 +0,0 @@
|
|
1
|
-
require 'ffi-rzmq'
|
2
|
-
require 'msgpack'
|
3
|
-
|
4
|
-
module SubZero
|
5
|
-
class Client
|
6
|
-
module Socket
|
7
|
-
|
8
|
-
class SocketError < StandardError; end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def zeromq_fetch verb, payload = {}, options = {}
|
13
|
-
result = []
|
14
|
-
|
15
|
-
timeout = options.fetch(:timeout, 1000) / 1000.0
|
16
|
-
|
17
|
-
Timeout.timeout timeout do
|
18
|
-
zeromq_context do |context|
|
19
|
-
zeromq_socket context do |socket|
|
20
|
-
rid = SecureRandom.uuid
|
21
|
-
zeromq_send socket, "REQ:#{sid}", rid, verb, payload.to_msgpack, options.to_msgpack
|
22
|
-
result = zeromq_result(socket, rid, verb)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
result
|
28
|
-
rescue Timeout::Error
|
29
|
-
raise SubZero::Client::Error.new(code: 'timeout', message: "call timeout: #{timeout}s")
|
30
|
-
end
|
31
|
-
|
32
|
-
def zeromq_context
|
33
|
-
context = ZMQ::Context.create(1)
|
34
|
-
fail SocketError, 'failed to create context' unless context
|
35
|
-
yield context
|
36
|
-
ensure
|
37
|
-
zcc!(context.terminate) if context
|
38
|
-
end
|
39
|
-
|
40
|
-
def zeromq_socket context
|
41
|
-
socket = context.socket ZMQ::DEALER
|
42
|
-
socket.identity = SecureRandom.hex(10)
|
43
|
-
zcc! socket.connect("tcp://#{config[:ip]}:#{config[:port]}")
|
44
|
-
yield socket
|
45
|
-
ensure
|
46
|
-
zcc! socket.close
|
47
|
-
end
|
48
|
-
|
49
|
-
def zeromq_send socket, *msgs
|
50
|
-
last = msgs.pop
|
51
|
-
msgs.each { |m| zcc! socket.send_string m, ZMQ::SNDMORE }
|
52
|
-
zcc! socket.send_string last
|
53
|
-
end
|
54
|
-
|
55
|
-
def zeromq_check_code! result_code
|
56
|
-
return if ZMQ::Util.resultcode_ok? result_code
|
57
|
-
|
58
|
-
fail SocketError, "operation failed, errno [#{ZMQ::Util.errno}], " +
|
59
|
-
"description [#{ZMQ::Util.error_string}]"
|
60
|
-
end
|
61
|
-
|
62
|
-
alias :zcc! :zeromq_check_code!
|
63
|
-
|
64
|
-
def zeromq_result socket, rid, verb
|
65
|
-
result = []
|
66
|
-
zcc! socket.recv_strings(result)
|
67
|
-
|
68
|
-
zeromq_validate_result! rid, verb, result
|
69
|
-
|
70
|
-
routing_info = MessagePack.unpack(result.pop)
|
71
|
-
payload = MessagePack.unpack(result.pop)
|
72
|
-
rep_verb = result.pop
|
73
|
-
status = rep_verb.end_with?('NOK') ? :error : :ok
|
74
|
-
|
75
|
-
[status, payload]
|
76
|
-
end
|
77
|
-
|
78
|
-
def zeromq_validate_result! rid, verb, result
|
79
|
-
type, rep_rid, rep_verb, payload = result
|
80
|
-
|
81
|
-
common = "result: #{result}"
|
82
|
-
|
83
|
-
unless type == "REP:#{sid}" || type == 'BRO'
|
84
|
-
fail SocketError, "unexpected result type: #{type}. " + common
|
85
|
-
end
|
86
|
-
|
87
|
-
unless rid == rep_rid
|
88
|
-
fail SocketError, "rid mismatch. sent: #{rid}, received: #{rep_id}. " + common
|
89
|
-
end
|
90
|
-
|
91
|
-
unless rep_verb == "#{verb}:OK" || rep_verb == "#{verb}:NOK"
|
92
|
-
fail SocketError, "reply verb mismatch: #{rep_verb}. " + common
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|