sub_zero 0.0.4 → 0.0.5
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/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
|