zss 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MjNkNWE0YzBjMWQyYzg4NDRmMmQ0MDJhNDZlOWY1OWY2OTY2NWVhNw==
5
- data.tar.gz: !binary |-
6
- NmFiOWM0N2UwNjU2Y2RhODY2OWE1ZDQyN2FhZjk2YTk1OWRmOTI2MA==
2
+ SHA1:
3
+ metadata.gz: f3fe063c07e36ab09e3f66202e1ec9b936d4124a
4
+ data.tar.gz: 8a03c4d06efc0dd89a84b5bdf311712ae6edb205
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MDU4Y2IzMTc5NDcyOWMxN2UzZTYzMmY2YWU4ZjJiNzBmYzNjMzA3MGViNDRk
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
- provider: rubygems
25
- api_key:
26
- secure: OsOTnuz+qiZe4RY2hOhYsv46LJE7eQf3jZAX5oBBxsSaC6V91SgyHgl1mexMM6r1CPvgOlG4b33AU2Um9NIVrO11fwl/psSeh6zrMf46l5tmn+QCtjOuoM1jtKpomYAPXioH0KkZWoRcqPyNZemZx14YJQW0/fLtrc5xUS20Re0=
27
- gemspec: zss.gemspec
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.2)
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.1.0)
31
- i18n (0.6.9)
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.3.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 minor
94
+ $ bump patch
86
95
 
87
96
  Bump Minor version
88
97
 
data/lib/zss/client.rb CHANGED
@@ -1,19 +1,19 @@
1
- require 'zss/socket'
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 = nil
9
- @frontend = config.try(:frontend) || Configuration.default.frontend
8
+ def initialize sid, config = {}
9
+ @frontend = config[:frontend] || Configuration.default.frontend
10
10
  @sid = sid.to_s.upcase
11
- @identity = config.try(:identity) || "client"
12
- @timeout = config.try(:timeout) || 1000
11
+ @identity = config[:identity] || "client"
12
+ @timeout = config[:timeout] || 1000
13
13
  end
14
14
 
15
- def call verb, payload = nil, headers: {}, timeout: nil
16
- action = verb.to_s.upcase#.gsub('_', '/')
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, payload = nil, headers: {}
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
- call verb, payload, headers: headers
35
+ payload = args[0]
36
+ options = args[1] || {}
37
+ call verb, payload, options
36
38
  end
37
39
 
38
40
  def socket
@@ -2,7 +2,10 @@ module ZSS
2
2
  class Configuration
3
3
 
4
4
  def self.default
5
- Hashie::Mash.new frontend: 'tcp://127.0.0.1:5560'
5
+ Hashie::Mash.new(
6
+ frontend: 'tcp://127.0.0.1:5560',
7
+ backend: 'tcp://127.0.0.1:5559'
8
+ )
6
9
  end
7
10
 
8
11
  end
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, :developer_message, :user_message
6
+ attr_reader :code, :user_message
7
+ attr_accessor :developer_message
5
8
 
6
- def initialize code, payload
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
@@ -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
@@ -31,6 +31,10 @@ module ZSS
31
31
 
32
32
  end
33
33
 
34
+ def req?
35
+ type == Type::REQ
36
+ end
37
+
34
38
  def self.parse(frames)
35
39
 
36
40
  frames.unshift(nil) if frames.length == 7
@@ -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
@@ -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