zss 0.0.2 → 0.0.3
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 +5 -13
- data/.travis.yml +5 -5
- data/Gemfile.lock +12 -4
- data/README.md +10 -1
- data/lib/zss/client.rb +12 -10
- data/lib/zss/configuration.rb +4 -1
- data/lib/zss/error.rb +23 -2
- data/lib/zss/errors.json +59 -0
- data/lib/zss/message.rb +4 -0
- data/lib/zss/message/smi.rb +43 -0
- data/lib/zss/router.rb +42 -0
- data/lib/zss/runner.rb +37 -0
- data/lib/zss/service.rb +147 -0
- data/lib/zss/socket.rb +12 -9
- data/lib/zss/version.rb +1 -1
- data/spec/integration/client_spec.rb +3 -2
- data/spec/integration/service_spec.rb +48 -0
- data/spec/integration/socket_spec.rb +3 -2
- data/spec/spec_broker_helper.rb +55 -7
- data/spec/spec_helper.rb +9 -0
- data/spec/unit/client_spec.rb +1 -1
- data/spec/unit/error_spec.rb +25 -12
- data/spec/unit/message_spec.rb +14 -0
- data/spec/unit/router_spec.rb +97 -0
- data/spec/unit/service_spec.rb +314 -0
- data/spec/unit/socket_spec.rb +40 -30
- data/zss.gemspec +2 -0
- metadata +69 -30
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NmFiOWM0N2UwNjU2Y2RhODY2OWE1ZDQyN2FhZjk2YTk1OWRmOTI2MA==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f3fe063c07e36ab09e3f66202e1ec9b936d4124a
|
4
|
+
data.tar.gz: 8a03c4d06efc0dd89a84b5bdf311712ae6edb205
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
NTAzYTU1NzI4NTVmZmU3NDk2NjQwZGY0NDIyZTJkMmNlYmUzMGE3Yzk5YTAz
|
11
|
-
YTcxYzI2MzBlMzZlNzdkMGUzYTZkYzYyY2ZmZDc0M2U3NTQwNTU=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZTJkNTk5NDg2NDE3OWU2MWVlMjY0ZWY5OGFiOTNmYmExY2NiYzFlZGY5N2Mz
|
14
|
-
YjQwMmFlZGM2MDJiZGM4NGEwOWZhNGQxNDE2N2U2YTA0NmZiOWRlNDA1ZDE1
|
15
|
-
YWRjOTA1NWFiODUxNWU0YTFhMmU5YTExY2Q0OTcyZGY2ZDc4Zjc=
|
6
|
+
metadata.gz: f50426fa1b1d3cc52f8c65798ff8943e48c9294e55d00f2e4b4d63def33a1a19a0fc55573873e877420f1c8bdd87e95f884e7586a8c4a7334aa2f12d464a1170
|
7
|
+
data.tar.gz: ac111e8fd24c86206b1e874d9f6f01ced6c16b4bcc32cf88fdbad62f1e01a70c14333842cf15cf63d54892d9bc1d2928a52bf1333063109b328d72bd15c80b52
|
data/.travis.yml
CHANGED
@@ -20,8 +20,8 @@ addons:
|
|
20
20
|
code_climate:
|
21
21
|
repo_token:
|
22
22
|
secure: U6d5emmLhWdFAqrVgtHtXDs1lR2f40is89mZfOp1HbnTQKnClGbmCuEGfeHL8HbaMZjpxFb7g9Ery26E3g1gCsE82sP8SkD0qY46LbWlufRctZWsD4d+TZZZttqo2eNNObKkW5JbzW2pGiRqQb7RCfsxsOnqqhXl3uXVOHK24x4=
|
23
|
-
deploy:
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
# deploy:
|
24
|
+
# provider: rubygems
|
25
|
+
# api_key:
|
26
|
+
# secure: OsOTnuz+qiZe4RY2hOhYsv46LJE7eQf3jZAX5oBBxsSaC6V91SgyHgl1mexMM6r1CPvgOlG4b33AU2Um9NIVrO11fwl/psSeh6zrMf46l5tmn+QCtjOuoM1jtKpomYAPXioH0KkZWoRcqPyNZemZx14YJQW0/fLtrc5xUS20Re0=
|
27
|
+
# gemspec: zss.gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
zss (0.0.
|
4
|
+
zss (0.0.3)
|
5
5
|
activesupport
|
6
|
+
daemons
|
7
|
+
em-zeromq
|
6
8
|
ffi-rzmq
|
7
9
|
hashie
|
8
10
|
msgpack
|
@@ -20,18 +22,24 @@ GEM
|
|
20
22
|
codeclimate-test-reporter (0.3.0)
|
21
23
|
simplecov (>= 0.7.1, < 1.0.0)
|
22
24
|
coderay (1.1.0)
|
25
|
+
daemons (1.1.9)
|
23
26
|
diff-lcs (1.2.5)
|
24
27
|
docile (1.1.5)
|
28
|
+
em-zeromq (0.5.0)
|
29
|
+
eventmachine (>= 1.0.0)
|
30
|
+
ffi (>= 1.0.0)
|
31
|
+
ffi-rzmq (~> 2.0.1)
|
32
|
+
eventmachine (1.0.3)
|
25
33
|
ffi (1.9.3)
|
26
34
|
ffi-rzmq (2.0.1)
|
27
35
|
ffi-rzmq-core (>= 1.0.1)
|
28
36
|
ffi-rzmq-core (1.0.3)
|
29
37
|
ffi (~> 1.9)
|
30
|
-
hashie (3.
|
31
|
-
i18n (0.6.
|
38
|
+
hashie (3.2.0)
|
39
|
+
i18n (0.6.11)
|
32
40
|
json (1.8.1)
|
33
41
|
method_source (0.8.2)
|
34
|
-
minitest (5.
|
42
|
+
minitest (5.4.0)
|
35
43
|
msgpack (0.5.8)
|
36
44
|
multi_json (1.10.1)
|
37
45
|
pry (0.10.0)
|
data/README.md
CHANGED
@@ -76,13 +76,22 @@ PongClient.call("ping/pong", "payload")
|
|
76
76
|
4. Push to the branch (`git push origin my-new-feature`)
|
77
77
|
5. Create new Pull Request
|
78
78
|
|
79
|
+
## Deployment
|
80
|
+
|
81
|
+
Continuous Integration build on master branch is configured to:
|
82
|
+
1. bump gem patch version
|
83
|
+
2. tag version
|
84
|
+
3. deploy to ruby gems
|
85
|
+
|
86
|
+
Major and Minor version are controlled manually, check next chapter.
|
87
|
+
|
79
88
|
## Bump versioning
|
80
89
|
|
81
90
|
We use [bump gem](https://github.com/gregorym/bump) to control gem versioning.
|
82
91
|
|
83
92
|
Bump Patch version
|
84
93
|
|
85
|
-
$ bump
|
94
|
+
$ bump patch
|
86
95
|
|
87
96
|
Bump Minor version
|
88
97
|
|
data/lib/zss/client.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
|
1
|
+
require_relative 'socket'
|
2
2
|
|
3
3
|
module ZSS
|
4
4
|
class Client
|
5
5
|
|
6
6
|
attr_reader :sid, :frontend, :identity, :timeout
|
7
7
|
|
8
|
-
def initialize sid, config =
|
9
|
-
@frontend = config
|
8
|
+
def initialize sid, config = {}
|
9
|
+
@frontend = config[:frontend] || Configuration.default.frontend
|
10
10
|
@sid = sid.to_s.upcase
|
11
|
-
@identity = config
|
12
|
-
@timeout = config
|
11
|
+
@identity = config[:identity] || "client"
|
12
|
+
@timeout = config[:timeout] || 1000
|
13
13
|
end
|
14
14
|
|
15
|
-
def call verb, payload
|
16
|
-
action = verb.to_s.upcase
|
15
|
+
def call verb, payload, headers: {}, timeout: nil
|
16
|
+
action = verb.to_s.upcase
|
17
17
|
address = Message::Address.new(sid: sid, verb: action)
|
18
18
|
|
19
19
|
request = Message.new(
|
@@ -21,7 +21,7 @@ module ZSS
|
|
21
21
|
headers: headers,
|
22
22
|
payload: payload)
|
23
23
|
|
24
|
-
response = socket.call(request)
|
24
|
+
response = socket.call(request, timeout)
|
25
25
|
fail ZSS::Error.new(response.status, response.payload) if response.is_error?
|
26
26
|
|
27
27
|
response.payload
|
@@ -29,10 +29,12 @@ module ZSS
|
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
|
-
def method_missing method,
|
32
|
+
def method_missing method, *args
|
33
33
|
# since we cannot use / on method names we replace _ with /
|
34
34
|
verb = method.to_s.gsub('_', '/')
|
35
|
-
|
35
|
+
payload = args[0]
|
36
|
+
options = args[1] || {}
|
37
|
+
call verb, payload, options
|
36
38
|
end
|
37
39
|
|
38
40
|
def socket
|
data/lib/zss/configuration.rb
CHANGED
data/lib/zss/error.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module ZSS
|
2
4
|
class Error < ::StandardError
|
3
5
|
|
4
|
-
attr_reader :code, :
|
6
|
+
attr_reader :code, :user_message
|
7
|
+
attr_accessor :developer_message
|
5
8
|
|
6
|
-
def initialize
|
9
|
+
def initialize(code, payload)
|
7
10
|
@code = code.to_i
|
8
11
|
@developer_message = payload.developerMessage
|
9
12
|
@user_message = payload.userMessage
|
@@ -11,5 +14,23 @@ module ZSS
|
|
11
14
|
set_backtrace caller
|
12
15
|
end
|
13
16
|
|
17
|
+
def self.[](code)
|
18
|
+
data = get_errors[code.to_s]
|
19
|
+
Error.new(code.to_i, data.body)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def self.get_errors
|
25
|
+
@errors ||= begin
|
26
|
+
path = File.join(
|
27
|
+
File.dirname(File.absolute_path(__FILE__)),
|
28
|
+
'errors.json'
|
29
|
+
)
|
30
|
+
file = File.read(path)
|
31
|
+
Hashie::Mash.new(JSON.parse(file))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
14
35
|
end
|
15
36
|
end
|
data/lib/zss/errors.json
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
{
|
2
|
+
"400": {
|
3
|
+
"code": 400,
|
4
|
+
"body": {
|
5
|
+
"developerMessage": "The request cannot be fulfilled due to bad syntax.",
|
6
|
+
"userMessage": "An error occured",
|
7
|
+
"errorCode": 400
|
8
|
+
}
|
9
|
+
},
|
10
|
+
"401": {
|
11
|
+
"code": 401,
|
12
|
+
"body": {
|
13
|
+
"developerMessage": "User authentication token has expired or is missing",
|
14
|
+
"userMessage": "This resource is only available after logging in.",
|
15
|
+
"errorCode": 401
|
16
|
+
}
|
17
|
+
},
|
18
|
+
"403": {
|
19
|
+
"code": 403,
|
20
|
+
"body": {
|
21
|
+
"developerMessage": "User does not have enough privileges to access this resource.",
|
22
|
+
"userMessage": "You do not have access to this resource.",
|
23
|
+
"errorCode": 403
|
24
|
+
},
|
25
|
+
"headers": {}
|
26
|
+
},
|
27
|
+
"404": {
|
28
|
+
"code": 404,
|
29
|
+
"body": {
|
30
|
+
"developerMessage": "The resource could not be found.",
|
31
|
+
"userMessage": "The content you requested was not found.",
|
32
|
+
"errorCode": 404
|
33
|
+
}
|
34
|
+
},
|
35
|
+
"429": {
|
36
|
+
"code": 429,
|
37
|
+
"body": {
|
38
|
+
"developerMessage": "You have sent too many requests in a given amount of time.",
|
39
|
+
"userMessage": "Please wait a while before trying to access this content again",
|
40
|
+
"errorCode": 429
|
41
|
+
}
|
42
|
+
},
|
43
|
+
"500": {
|
44
|
+
"code": 500,
|
45
|
+
"body": {
|
46
|
+
"developerMessage": "There was an error while processing this request. There is probably something wrong with the API server.",
|
47
|
+
"userMessage": "There was an error while processing this request.",
|
48
|
+
"errorCode": 500
|
49
|
+
}
|
50
|
+
},
|
51
|
+
"599": {
|
52
|
+
"code": 599,
|
53
|
+
"body": {
|
54
|
+
"developerMessage": "Connection timeout while processing this request.",
|
55
|
+
"userMessage": "Connection timeout while processing this request.",
|
56
|
+
"errorCode": 599
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
data/lib/zss/message.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
module ZSS
|
2
|
+
|
3
|
+
class Message
|
4
|
+
|
5
|
+
class SMI
|
6
|
+
|
7
|
+
SMI = 'SMI'
|
8
|
+
|
9
|
+
def self.down sid
|
10
|
+
Message.new(
|
11
|
+
address: Message::Address.new(
|
12
|
+
sid: SMI,
|
13
|
+
verb: 'DOWN'
|
14
|
+
),
|
15
|
+
payload: sid
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.up sid
|
20
|
+
Message.new(
|
21
|
+
address: Message::Address.new(
|
22
|
+
sid: SMI,
|
23
|
+
verb: 'UP'
|
24
|
+
),
|
25
|
+
payload: sid
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.heartbeat sid
|
30
|
+
Message.new(
|
31
|
+
address: Message::Address.new(
|
32
|
+
sid: SMI,
|
33
|
+
verb: 'HEARTBEAT'
|
34
|
+
),
|
35
|
+
payload: sid
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/lib/zss/router.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module ZSS
|
2
|
+
class Router
|
3
|
+
|
4
|
+
def add(context, route, handler = nil)
|
5
|
+
|
6
|
+
fail "Invalid context!" unless context
|
7
|
+
fail "Invalid route: #{route}" unless route
|
8
|
+
|
9
|
+
handler ||= route.to_sym
|
10
|
+
|
11
|
+
fail "Invalid handler: #{handler}" unless context.respond_to? handler
|
12
|
+
|
13
|
+
routes[route.to_s.upcase] = get_proc(context, handler)
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(route)
|
17
|
+
handler = routes[route.to_s.upcase]
|
18
|
+
return handler if handler
|
19
|
+
|
20
|
+
error = Error[404]
|
21
|
+
error.developer_message = "Invalid route #{route}!"
|
22
|
+
fail error
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def routes
|
28
|
+
@routes ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_proc(context, handler)
|
32
|
+
receive_headers = context.method(handler).parameters.size == 2
|
33
|
+
|
34
|
+
if receive_headers
|
35
|
+
Proc.new { |p,h| context.send(handler, p, h) }
|
36
|
+
else
|
37
|
+
Proc.new { |p,h| context.send(handler, p) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/zss/runner.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'daemons'
|
2
|
+
|
3
|
+
module ZSS
|
4
|
+
class Runner
|
5
|
+
|
6
|
+
def self.run(proc_name)
|
7
|
+
proc_name = proc_name.to_s
|
8
|
+
pid_path = log_path = './log'
|
9
|
+
|
10
|
+
FileUtils.mkdir_p pid_path
|
11
|
+
FileUtils.mkdir_p log_path
|
12
|
+
|
13
|
+
daemon_opts = {
|
14
|
+
multiple: true,
|
15
|
+
dir_mode: :normal,
|
16
|
+
dir: pid_path,
|
17
|
+
log_output: true,
|
18
|
+
stop_proc: lambda do
|
19
|
+
puts "stop #{proc_name} daemon..."
|
20
|
+
$stop_requested = true
|
21
|
+
end
|
22
|
+
}
|
23
|
+
|
24
|
+
puts "Starting #{proc_name}:\n\tPID: #{pid_path}\n\tLOGS: #{log_path}"
|
25
|
+
|
26
|
+
Daemons.run_proc proc_name, daemon_opts do
|
27
|
+
daemon = ZSS::ServiceRegister.get_service
|
28
|
+
|
29
|
+
puts "Started #{proc_name} daemon..."
|
30
|
+
daemon.run
|
31
|
+
|
32
|
+
puts "Stoping #{proc_name} daemon"
|
33
|
+
exit 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/zss/service.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'em-zeromq'
|
2
|
+
require_relative 'router'
|
3
|
+
require_relative 'message/smi'
|
4
|
+
|
5
|
+
module ZSS
|
6
|
+
class Service
|
7
|
+
|
8
|
+
attr_reader :sid, :heartbeat, :backend, :identity
|
9
|
+
|
10
|
+
def initialize(sid, config = {})
|
11
|
+
|
12
|
+
fail Error[500] if sid.blank?
|
13
|
+
|
14
|
+
@sid = sid.to_s.upcase
|
15
|
+
@heartbeat = config.try(:heartbeat) || 1000
|
16
|
+
@backend = config.try(:backend) || Configuration.default.backend
|
17
|
+
@router = ZSS::Router.new
|
18
|
+
@identity = "#{sid}##{SecureRandom.uuid}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
Thread.abort_on_exception = true
|
23
|
+
|
24
|
+
context = EM::ZeroMQ::Context.new(1)
|
25
|
+
fail RuntimeError, 'failed to create create_context' unless context
|
26
|
+
|
27
|
+
# puts "Starting SID: '#{sid}' ID: '#{identity}'"
|
28
|
+
# puts "Env: #{ZSS::Environment.env}"
|
29
|
+
# puts "Broker: #{backend}"
|
30
|
+
|
31
|
+
EM.run do
|
32
|
+
# handle interrupts
|
33
|
+
Signal.trap("INT") { stop }
|
34
|
+
Signal.trap("TERM") { stop }
|
35
|
+
|
36
|
+
connect_socket context
|
37
|
+
|
38
|
+
start_heartbeat_worker
|
39
|
+
|
40
|
+
# send up message
|
41
|
+
send Message::SMI.up(sid)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_route(context, route, handler = nil)
|
46
|
+
router.add(context, route, handler)
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
timer.cancel if timer
|
51
|
+
|
52
|
+
# puts "Stoping SID: '#{sid}' ID: '#{socket.identity}'"
|
53
|
+
EM.add_timer do
|
54
|
+
send Message::SMI.down(sid)
|
55
|
+
socket.disconnect backend
|
56
|
+
EM::stop
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_accessor :socket, :router, :timer
|
63
|
+
|
64
|
+
def connect_socket(context)
|
65
|
+
|
66
|
+
@socket = context.socket ZMQ::DEALER
|
67
|
+
fail RuntimeError, 'failed to create socket' unless socket
|
68
|
+
|
69
|
+
socket.identity = identity
|
70
|
+
socket.setsockopt(ZMQ::LINGER, 0)
|
71
|
+
socket.on(:message, &method(:handle_frames))
|
72
|
+
|
73
|
+
socket.connect(backend)
|
74
|
+
end
|
75
|
+
|
76
|
+
def start_heartbeat_worker
|
77
|
+
@timer = EventMachine::PeriodicTimer.new(heartbeat / 1000) do
|
78
|
+
send Message::SMI.heartbeat(sid)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_frames(*frames)
|
83
|
+
# we need to close frame to avoid memory leaks
|
84
|
+
frames = frames.map do |frame|
|
85
|
+
out_frame = frame.copy_out_string
|
86
|
+
frame.close
|
87
|
+
out_frame
|
88
|
+
end
|
89
|
+
|
90
|
+
handle Message.parse(frames)
|
91
|
+
end
|
92
|
+
|
93
|
+
def handle(message)
|
94
|
+
if message.req?
|
95
|
+
handle_request(message)
|
96
|
+
#else
|
97
|
+
# puts "heartbeat response received!"
|
98
|
+
end
|
99
|
+
rescue ZSS::Error => error
|
100
|
+
#puts "Erorr: ZSS::Error raised while processing request: #{e}"
|
101
|
+
reply_error error, message
|
102
|
+
rescue => e
|
103
|
+
#puts "Error while processing request: #{e}"
|
104
|
+
reply_error Error[500], message
|
105
|
+
end
|
106
|
+
|
107
|
+
def handle_request(message)
|
108
|
+
if message.address.sid != sid
|
109
|
+
error = Error[404]
|
110
|
+
error.developer_message = "Invalid SID: #{message.address.sid}!"
|
111
|
+
fail error
|
112
|
+
end
|
113
|
+
|
114
|
+
# the router returns an handler that receives payload and headers
|
115
|
+
handler = router.get(message.address.verb)
|
116
|
+
message.payload = handler.call(message.payload, message.headers)
|
117
|
+
reply message
|
118
|
+
end
|
119
|
+
|
120
|
+
def reply_error(error, message)
|
121
|
+
message.status = error.code
|
122
|
+
message.payload = {
|
123
|
+
errorCode: error.code,
|
124
|
+
userMessage: error.user_message,
|
125
|
+
developerMessage: error.developer_message
|
126
|
+
}
|
127
|
+
message.type = Message::Type::REP
|
128
|
+
send message
|
129
|
+
end
|
130
|
+
|
131
|
+
def reply(message)
|
132
|
+
#puts "reply #{message}"
|
133
|
+
message.status = 200
|
134
|
+
message.type = Message::Type::REP
|
135
|
+
send message
|
136
|
+
end
|
137
|
+
|
138
|
+
def send(msg)
|
139
|
+
frames = msg.to_frames
|
140
|
+
#remove identity frame on request
|
141
|
+
frames.shift if msg.req?
|
142
|
+
success = socket.send_msg(*frames)
|
143
|
+
puts "An Error ocurred while sending message" unless success
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|