ruby_skynet 0.1.0
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.
- data/Gemfile +13 -0
- data/Gemfile.lock +49 -0
- data/LICENSE.txt +201 -0
- data/README.md +119 -0
- data/Rakefile +40 -0
- data/lib/ruby_skynet/client.rb +293 -0
- data/lib/ruby_skynet/doozer/client.rb +202 -0
- data/lib/ruby_skynet/doozer/exceptions.rb +5 -0
- data/lib/ruby_skynet/doozer/msg.pb.rb +118 -0
- data/lib/ruby_skynet/exceptions.rb +6 -0
- data/lib/ruby_skynet/version.rb +3 -0
- data/lib/ruby_skynet.rb +11 -0
- data/nbproject/private/config.properties +0 -0
- data/nbproject/private/private.properties +1 -0
- data/nbproject/private/rake-d.txt +4 -0
- data/nbproject/project.properties +6 -0
- data/nbproject/project.xml +15 -0
- data/skynet-0.1.0.gem +0 -0
- data/test/doozer_client_test.rb +73 -0
- data/test/ruby_skynet_client_test.rb +77 -0
- data/test/simple_server.rb +134 -0
- data/test.log +5887 -0
- metadata +131 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
require 'bson'
|
2
|
+
require 'sync_attr'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
#
|
6
|
+
# RubySkynet Client
|
7
|
+
#
|
8
|
+
# Supports
|
9
|
+
# RPC calls to Skynet
|
10
|
+
# Skynet Service autodiscovery
|
11
|
+
#
|
12
|
+
module RubySkynet
|
13
|
+
class Client
|
14
|
+
include SyncAttr
|
15
|
+
|
16
|
+
# Default doozer configuration
|
17
|
+
# To replace this default, set the config as follows:
|
18
|
+
# RubySkynet::Client.doozer_config = { .... }
|
19
|
+
sync_attr_accessor :doozer_config do
|
20
|
+
{
|
21
|
+
:server => '127.0.0.1:8046',
|
22
|
+
:read_timeout => 5,
|
23
|
+
:connect_timeout => 3,
|
24
|
+
:connect_retry_interval => 0.1,
|
25
|
+
:connect_retry_count => 3
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Lazy initialize Doozer Client
|
30
|
+
sync_cattr_reader :doozer do
|
31
|
+
Doozer::Client.new
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create a client connection, call the supplied block and close the connection on
|
35
|
+
# completion of the block
|
36
|
+
#
|
37
|
+
# Example
|
38
|
+
#
|
39
|
+
# require 'ruby_skynet'
|
40
|
+
# SemanticLogger.default_level = :trace
|
41
|
+
# SemanticLogger.appenders << SemanticLogger::Appender::File(STDOUT)
|
42
|
+
# RubySkynet::Client.connect('TutorialService') do |tutorial_service|
|
43
|
+
# p tutorial_service.call(:value => 5)
|
44
|
+
# end
|
45
|
+
def self.connect(service_name, params={})
|
46
|
+
begin
|
47
|
+
client = self.new(service_name, params)
|
48
|
+
yield(client)
|
49
|
+
ensure
|
50
|
+
client.close if client
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns a new RubySkynet Client for the named service
|
55
|
+
#
|
56
|
+
# Parameters:
|
57
|
+
# :service_name
|
58
|
+
# Name of the service to look for and connect to on Skynet
|
59
|
+
#
|
60
|
+
# :doozer_servers [Array of String]
|
61
|
+
# Array of URL's of doozer servers to connect to with port numbers
|
62
|
+
# ['server1:2000', 'server2:2000']
|
63
|
+
#
|
64
|
+
# The second server will only be attempted once the first server
|
65
|
+
# cannot be connected to or has timed out on connect
|
66
|
+
# A read failure or timeout will not result in switching to the second
|
67
|
+
# server, only a connection failure or during an automatic reconnect
|
68
|
+
#
|
69
|
+
# :read_timeout [Float]
|
70
|
+
# Time in seconds to timeout on read
|
71
|
+
# Can be overridden by supplying a timeout in the read call
|
72
|
+
# Default: 60
|
73
|
+
#
|
74
|
+
# :connect_timeout [Float]
|
75
|
+
# Time in seconds to timeout when trying to connect to the server
|
76
|
+
# Default: Half of the :read_timeout ( 30 seconds )
|
77
|
+
#
|
78
|
+
# :connect_retry_count [Fixnum]
|
79
|
+
# Number of times to retry connecting when a connection fails
|
80
|
+
# Default: 10
|
81
|
+
#
|
82
|
+
# :connect_retry_interval [Float]
|
83
|
+
# Number of seconds between connection retry attempts after the first failed attempt
|
84
|
+
# Default: 0.5
|
85
|
+
def initialize(service_name, params = {})
|
86
|
+
@service_name = service_name
|
87
|
+
@logger = SemanticLogger::Logger.new("#{self.class.name}: #{service_name}")
|
88
|
+
|
89
|
+
# User configurable options
|
90
|
+
params[:read_timeout] ||= 60
|
91
|
+
params[:connect_timeout] ||= 30
|
92
|
+
params[:connect_retry_interval] ||= 0.1
|
93
|
+
params[:connect_retry_count] ||= 5
|
94
|
+
|
95
|
+
# If Server name and port of where Skynet Service is running
|
96
|
+
# is not supplied look for it in Doozer
|
97
|
+
unless params[:server] || params[:servers]
|
98
|
+
params[:server] = self.class.server_for(service_name)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Disable buffering the send since it is a RPC call
|
102
|
+
params[:buffered] = false
|
103
|
+
|
104
|
+
@logger.trace "Socket Connection parameters", params
|
105
|
+
|
106
|
+
# For each new connection perform the Skynet handshake
|
107
|
+
params[:on_connect] = Proc.new do |socket|
|
108
|
+
# Reset user_data on each connection
|
109
|
+
socket.user_data = 0
|
110
|
+
|
111
|
+
# Receive Service Handshake
|
112
|
+
# Registered bool
|
113
|
+
# Registered indicates the state of this service. If it is false, the connection will
|
114
|
+
# close immediately and the client should look elsewhere for this service.
|
115
|
+
#
|
116
|
+
# ClientID string
|
117
|
+
# ClientID is a UUID that is used by the client to identify itself in RPC requests.
|
118
|
+
@logger.debug "Waiting for Service Handshake"
|
119
|
+
service_handshake = self.class.read_bson_document(socket)
|
120
|
+
@logger.trace 'Service Handshake', service_handshake
|
121
|
+
|
122
|
+
# #TODO When a reconnect returns registered == false we need to go back to doozer
|
123
|
+
@registered = service_handshake['registered']
|
124
|
+
@client_id = service_handshake['clientid']
|
125
|
+
|
126
|
+
# Send blank ClientHandshake
|
127
|
+
client_handshake = { 'clientid' => @client_id }
|
128
|
+
@logger.debug "Sending Client Handshake"
|
129
|
+
@logger.trace 'Client Handshake', client_handshake
|
130
|
+
socket.send(BSON.serialize(client_handshake))
|
131
|
+
end
|
132
|
+
|
133
|
+
@socket = ResilientSocket::TCPClient.new(params)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Performs a synchronous call to the Skynet Service
|
137
|
+
#
|
138
|
+
# Parameters:
|
139
|
+
# method_name [String|Symbol]:
|
140
|
+
# Name of the method to call at the service
|
141
|
+
# parameters [Hash]:
|
142
|
+
# Parameters to pass into the service
|
143
|
+
#
|
144
|
+
# Returns the Hash result returned from the Skynet Service
|
145
|
+
#
|
146
|
+
# Raises RubySkynet::ProtocolError
|
147
|
+
# Raises RubySkynet::SkynetException
|
148
|
+
def call(method_name, parameters)
|
149
|
+
# Skynet requires BSON RPC Calls to have the following format:
|
150
|
+
# https://github.com/bketelsen/skynet/blob/protocol/protocol.md
|
151
|
+
request_id = BSON::ObjectId.new.to_s
|
152
|
+
@logger.tagged request_id do
|
153
|
+
@logger.benchmark_info "Called Skynet Service: #{@service_name}.#{method_name}" do
|
154
|
+
|
155
|
+
# Resilient Send
|
156
|
+
retry_count = 0
|
157
|
+
@socket.retry_on_connection_failure do |socket|
|
158
|
+
# user_data is maintained per session and a different session could
|
159
|
+
# be supplied with each retry
|
160
|
+
socket.user_data ||= 0
|
161
|
+
header = {
|
162
|
+
'servicemethod' => "#{@service_name}.Forward",
|
163
|
+
'seq' => socket.user_data,
|
164
|
+
}
|
165
|
+
@logger.debug "Sending Header"
|
166
|
+
@logger.trace 'Header', header
|
167
|
+
socket.send(BSON.serialize(header))
|
168
|
+
|
169
|
+
@logger.trace 'Parameters:', parameters
|
170
|
+
|
171
|
+
# The parameters are placed in the request object in BSON serialized
|
172
|
+
# form
|
173
|
+
request = {
|
174
|
+
'clientid' => @client_id,
|
175
|
+
'in' => BSON.serialize(parameters).to_s,
|
176
|
+
'method' => method_name.to_s,
|
177
|
+
'requestinfo' => {
|
178
|
+
'requestid' => request_id,
|
179
|
+
# Increment retry count to indicate that the request may have been tried previously
|
180
|
+
# TODO: this should be incremented if request is retried,
|
181
|
+
'retrycount' => retry_count,
|
182
|
+
# TODO: this should be forwarded along in case of services also
|
183
|
+
# being a client and calling additional services. If empty it will
|
184
|
+
# be stuffed with connecting address
|
185
|
+
'originaddress' => ''
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
@logger.debug "Sending Request"
|
190
|
+
@logger.trace 'Request', request
|
191
|
+
socket.send(BSON.serialize(request))
|
192
|
+
end
|
193
|
+
|
194
|
+
# Once send is successful it could have been processed, so we can no
|
195
|
+
# longer retry now otherwise we could create a duplicate
|
196
|
+
# retry_count += 1
|
197
|
+
|
198
|
+
# Read header first as a separate BSON document
|
199
|
+
@logger.debug "Reading header from server"
|
200
|
+
header = self.class.read_bson_document(@socket)
|
201
|
+
@logger.debug 'Header', header
|
202
|
+
|
203
|
+
# Read the BSON response document
|
204
|
+
@logger.debug "Reading response from server"
|
205
|
+
response = self.class.read_bson_document(@socket)
|
206
|
+
@logger.trace 'Response', response
|
207
|
+
|
208
|
+
# Ensure the sequence number in the response header matches the
|
209
|
+
# sequence number sent in the request
|
210
|
+
if seq_no = header['seq']
|
211
|
+
raise ProtocolError.new("Incorrect Response received, expected seq=#{@socket.user_data}, received: #{header.inspect}") if seq_no != @socket.user_data
|
212
|
+
else
|
213
|
+
raise ProtocolError.new("Invalid Response header, missing 'seq': #{header.inspect}")
|
214
|
+
end
|
215
|
+
|
216
|
+
# Increment Sequence number only on successful response
|
217
|
+
@socket.user_data += 1
|
218
|
+
|
219
|
+
# If an error is returned from Skynet raise a Skynet exception
|
220
|
+
if error = header['error']
|
221
|
+
raise SkynetException.new(error) if error.to_s.length > 0
|
222
|
+
end
|
223
|
+
|
224
|
+
# If an error is returned from the service raise a Service exception
|
225
|
+
if error = response['error']
|
226
|
+
raise ServiceException.new(error) if error.to_s.length > 0
|
227
|
+
end
|
228
|
+
|
229
|
+
# Return Value
|
230
|
+
# The return value is inside the response object, it's a byte array of it's own and needs to be deserialized
|
231
|
+
result = BSON.deserialize(response['out'])
|
232
|
+
@logger.trace 'Return Value', result
|
233
|
+
result
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns a BSON document read from the socket.
|
239
|
+
# Returns nil if the operation times out or if a network
|
240
|
+
# connection failure occurs
|
241
|
+
def self.read_bson_document(socket)
|
242
|
+
bytebuf = BSON::ByteBuffer.new
|
243
|
+
# Read 4 byte size of following BSON document
|
244
|
+
bytes = ''
|
245
|
+
socket.read(4, bytes)
|
246
|
+
|
247
|
+
# Read BSON document
|
248
|
+
sz = bytes.unpack("V")[0]
|
249
|
+
raise "Invalid Data received from server:#{bytes.inspect}" unless sz
|
250
|
+
|
251
|
+
bytebuf.append!(bytes)
|
252
|
+
bytes = ''
|
253
|
+
sz -= 4
|
254
|
+
until bytes.size >= sz
|
255
|
+
buf = ''
|
256
|
+
socket.read(sz, buf)
|
257
|
+
bytes << buf
|
258
|
+
end
|
259
|
+
bytebuf.append!(bytes)
|
260
|
+
return BSON.deserialize(bytebuf)
|
261
|
+
end
|
262
|
+
|
263
|
+
def close()
|
264
|
+
@socket.close
|
265
|
+
end
|
266
|
+
|
267
|
+
##############################
|
268
|
+
#protected
|
269
|
+
|
270
|
+
# Returns [Array] of the hostname and port pair [String] that implements a particular service
|
271
|
+
# Performs a doozer lookup to find the servers
|
272
|
+
#
|
273
|
+
# service_name:
|
274
|
+
# version: Version of service to locate
|
275
|
+
# Default: Find latest version
|
276
|
+
def self.registered_implementers(service_name, version = '*', region = 'Development')
|
277
|
+
hosts = []
|
278
|
+
doozer.walk("/services/#{service_name}/#{version}/#{region}/*/*").each do |node|
|
279
|
+
entry = MultiJson.load(node.value)
|
280
|
+
hosts << entry if entry['Registered']
|
281
|
+
end
|
282
|
+
hosts
|
283
|
+
end
|
284
|
+
|
285
|
+
# Randomly returns a server that implements the requested service
|
286
|
+
def self.server_for(service_name, version = '*', region = 'Development')
|
287
|
+
hosts = registered_implementers(service_name, version, region)
|
288
|
+
service = hosts[rand(hosts.size)]['Config']['ServiceAddr']
|
289
|
+
"#{service['IPAddress']}:#{service['Port']}"
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'ruby_skynet/doozer/msg.pb'
|
2
|
+
require 'semantic_logger'
|
3
|
+
require 'resilient_socket'
|
4
|
+
require 'ruby_skynet/doozer/exceptions'
|
5
|
+
require 'ruby_skynet/doozer/msg.pb'
|
6
|
+
|
7
|
+
module RubySkynet
|
8
|
+
module Doozer
|
9
|
+
class Client
|
10
|
+
|
11
|
+
# Create a resilient client connection to a Doozer server
|
12
|
+
def initialize(params={})
|
13
|
+
@logger = SemanticLogger::Logger.new(self.class)
|
14
|
+
|
15
|
+
# User configurable options
|
16
|
+
params[:read_timeout] ||= 5
|
17
|
+
params[:connect_timeout] ||= 3
|
18
|
+
params[:connect_retry_interval] ||= 0.1
|
19
|
+
params[:connect_retry_count] ||= 3
|
20
|
+
|
21
|
+
# Server name and port where Doozer is running
|
22
|
+
# Defaults to 127.0.0.1:8046
|
23
|
+
params[:server] ||= '127.0.0.1:8046' unless params[:servers]
|
24
|
+
|
25
|
+
# Disable buffering the send since it is a RPC call
|
26
|
+
params[:buffered] = false
|
27
|
+
|
28
|
+
@logger.trace "Socket Connection parameters", params
|
29
|
+
|
30
|
+
# For each new connection
|
31
|
+
params[:on_connect] = Proc.new do |socket|
|
32
|
+
# Reset user_data on each connection
|
33
|
+
socket.user_data = 0
|
34
|
+
end
|
35
|
+
|
36
|
+
@socket = ResilientSocket::TCPClient.new(params)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Close this client connection to doozer
|
40
|
+
def close
|
41
|
+
@socket.close if @socket
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the current Doozer revision
|
45
|
+
def current_revision
|
46
|
+
invoke(Request.new(:verb => Request::Verb::REV)).rev
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set a value in Doozer
|
50
|
+
# path: Path to the value to be set
|
51
|
+
# value: Value to set
|
52
|
+
# rev: Revision at which to set the value
|
53
|
+
# If not supplied it will replace the latest version on the server
|
54
|
+
#
|
55
|
+
# Returns the new revision of the updated value
|
56
|
+
#
|
57
|
+
# It is recommended to set the revision so that multiple clients do not
|
58
|
+
# attempt to update the value at the same time.
|
59
|
+
# Setting the revision also allows the call to be retried automatically
|
60
|
+
# in the event of a network failure
|
61
|
+
def set(path, value, rev=-1)
|
62
|
+
invoke(Request.new(:path => path, :value => value, :rev => rev, :verb => Request::Verb::SET), false).rev
|
63
|
+
end
|
64
|
+
|
65
|
+
# Sets the current value at the supplied path
|
66
|
+
def []=(path,value)
|
67
|
+
set(path, value)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the value at the supplied path and revision
|
71
|
+
def get(path, rev = nil)
|
72
|
+
invoke(Request.new(:path => path, :rev => rev, :verb => Request::Verb::GET))
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns just the value at the supplied path, not the revision
|
76
|
+
def [](path)
|
77
|
+
get(path).value
|
78
|
+
end
|
79
|
+
|
80
|
+
# Deletes the file at path if rev is greater than or equal to the file's revision.
|
81
|
+
# Returns nil when the file was removed
|
82
|
+
# Raises an exception if an attempt to remove the file and its revision
|
83
|
+
# is greater than that supplied
|
84
|
+
def delete(path, rev=-1)
|
85
|
+
invoke(Request.new(:path => path, :rev => rev, :verb => Request::Verb::DEL))
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the directory in the supplied path
|
90
|
+
# Use offset to get the next
|
91
|
+
# returns nil if no further paths are available
|
92
|
+
def directory(path, offset = 0, rev = nil)
|
93
|
+
begin
|
94
|
+
invoke(Request.new(:path => path, :rev => rev, :offset => offset, :verb => Request::Verb::GETDIR))
|
95
|
+
rescue RubySkynet::Doozer::ResponseError => exc
|
96
|
+
raise exc unless exc.message.include?('RANGE')
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def stat(path, rev = nil)
|
102
|
+
invoke(Request.new(:path => path, :rev => rev, :verb => Request::Verb::STAT))
|
103
|
+
end
|
104
|
+
|
105
|
+
def access(secret)
|
106
|
+
invoke(Request.new(:path => secret, :verb => Request::Verb::ACCESS))
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns every entry in the supplied path
|
110
|
+
# path can also contain wildcard characters such as '*'
|
111
|
+
# Example:
|
112
|
+
# hosts = []
|
113
|
+
# walk('/ctl/node/*/addr', current_revision).each do |node|
|
114
|
+
# hosts << node.value unless hosts.include? node.value
|
115
|
+
# end
|
116
|
+
def walk(path, rev = nil, offset = 0)
|
117
|
+
paths = []
|
118
|
+
revision = rev || current_revision
|
119
|
+
# Resume walk on network connection failure
|
120
|
+
@socket.retry_on_connection_failure do
|
121
|
+
while true
|
122
|
+
send(Request.new(:path => path, :rev => revision , :offset => offset, :verb => Request::Verb::WALK))
|
123
|
+
response = read
|
124
|
+
if response.err_code
|
125
|
+
break if response.err_code == Response::Err::RANGE
|
126
|
+
else
|
127
|
+
raise ResponseError.new("#{Response::Err.name_by_value(response.err_code)}: #{response.err_detail}") if response.err_code != 0
|
128
|
+
end
|
129
|
+
paths << response
|
130
|
+
offset += 1
|
131
|
+
end
|
132
|
+
end
|
133
|
+
paths
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns [Array] of hostname [String] with each string
|
137
|
+
# representing another Doozer server that can be connected to
|
138
|
+
def doozer_hosts
|
139
|
+
hosts = []
|
140
|
+
walk('/ctl/node/*/addr', current_revision).each do |node|
|
141
|
+
hosts << node.value unless hosts.include? node.value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# TODO Implement watching for changes in a separate thread with it's own
|
146
|
+
# client connection
|
147
|
+
#def watch(path, rev = nil)
|
148
|
+
# invoke(Request.new(:path => secret, :verb => Request::Verb::WAIT))
|
149
|
+
#end
|
150
|
+
|
151
|
+
#####################
|
152
|
+
# protected
|
153
|
+
|
154
|
+
# Call the Doozer server
|
155
|
+
#
|
156
|
+
# When readonly ==> true the request is always retried on network failure
|
157
|
+
# When readonly ==> false the request is retried on network failure
|
158
|
+
# _only_ if a rev has been supplied
|
159
|
+
#
|
160
|
+
# When modifier is true
|
161
|
+
def invoke(request, readonly=true)
|
162
|
+
retry_read = readonly || !request.rev.nil?
|
163
|
+
response = nil
|
164
|
+
@socket.retry_on_connection_failure do
|
165
|
+
send(request)
|
166
|
+
response = read if retry_read
|
167
|
+
end
|
168
|
+
# Network error on read must be sent back to caller since we do not
|
169
|
+
# know if the modification was made
|
170
|
+
response = read unless retry_read
|
171
|
+
raise ResponseError.new("#{Response::Err.name_by_value(response.err_code)}: #{response.err_detail}") if response.err_code != 0
|
172
|
+
response
|
173
|
+
end
|
174
|
+
|
175
|
+
# Send the protobuf Request to Doozer
|
176
|
+
def send(request)
|
177
|
+
request.tag = 0
|
178
|
+
data = request.serialize_to_string
|
179
|
+
# An additional header is added to the request indicating the size of the request
|
180
|
+
head = [data.length].pack("N")
|
181
|
+
@socket.send(head+data)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Read the protobuf Response from Doozer
|
185
|
+
def read
|
186
|
+
# First strip the additional header indicating the size of the subsequent response
|
187
|
+
head = @socket.read(4)
|
188
|
+
length = head.unpack("N")[0]
|
189
|
+
|
190
|
+
# Since can returns upto 'length' bytes we need to make sure it returns
|
191
|
+
# at least 'length' bytes
|
192
|
+
# TODO: Make this a binary buffer
|
193
|
+
data = ''
|
194
|
+
until data.size >= length
|
195
|
+
data << @socket.read(length)
|
196
|
+
end
|
197
|
+
Response.new.parse_from_string(data)
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
### Generated by rprotoc. DO NOT EDIT!
|
2
|
+
### <proto file: doozerd/server/msg.proto>
|
3
|
+
# package server;
|
4
|
+
#
|
5
|
+
# // see doc/proto.md
|
6
|
+
# message Request {
|
7
|
+
# optional int32 tag = 1;
|
8
|
+
#
|
9
|
+
# enum Verb {
|
10
|
+
# GET = 1;
|
11
|
+
# SET = 2;
|
12
|
+
# DEL = 3;
|
13
|
+
# REV = 5;
|
14
|
+
# WAIT = 6;
|
15
|
+
# NOP = 7;
|
16
|
+
# WALK = 9;
|
17
|
+
# GETDIR = 14;
|
18
|
+
# STAT = 16;
|
19
|
+
# ACCESS = 99;
|
20
|
+
# }
|
21
|
+
# optional Verb verb = 2;
|
22
|
+
#
|
23
|
+
# optional string path = 4;
|
24
|
+
# optional bytes value = 5;
|
25
|
+
# optional int32 other_tag = 6;
|
26
|
+
#
|
27
|
+
# optional int32 offset = 7;
|
28
|
+
#
|
29
|
+
# optional int64 rev = 9;
|
30
|
+
# }
|
31
|
+
#
|
32
|
+
# // see doc/proto.md
|
33
|
+
# message Response {
|
34
|
+
# optional int32 tag = 1;
|
35
|
+
# optional int32 flags = 2;
|
36
|
+
#
|
37
|
+
# optional int64 rev = 3;
|
38
|
+
# optional string path = 5;
|
39
|
+
# optional bytes value = 6;
|
40
|
+
# optional int32 len = 8;
|
41
|
+
#
|
42
|
+
# enum Err {
|
43
|
+
# // don't use value 0
|
44
|
+
# OTHER = 127;
|
45
|
+
# TAG_IN_USE = 1;
|
46
|
+
# UNKNOWN_VERB = 2;
|
47
|
+
# READONLY = 3;
|
48
|
+
# TOO_LATE = 4;
|
49
|
+
# REV_MISMATCH = 5;
|
50
|
+
# BAD_PATH = 6;
|
51
|
+
# MISSING_ARG = 7;
|
52
|
+
# RANGE = 8;
|
53
|
+
# NOTDIR = 20;
|
54
|
+
# ISDIR = 21;
|
55
|
+
# NOENT = 22;
|
56
|
+
# }
|
57
|
+
# optional Err err_code = 100;
|
58
|
+
# optional string err_detail = 101;
|
59
|
+
# }
|
60
|
+
|
61
|
+
require 'protobuf/message/message'
|
62
|
+
require 'protobuf/message/enum'
|
63
|
+
require 'protobuf/message/service'
|
64
|
+
require 'protobuf/message/extend'
|
65
|
+
|
66
|
+
module RubySkynet
|
67
|
+
module Doozer
|
68
|
+
class Request < ::Protobuf::Message
|
69
|
+
defined_in __FILE__
|
70
|
+
optional :int32, :tag, 1
|
71
|
+
class Verb < ::Protobuf::Enum
|
72
|
+
defined_in __FILE__
|
73
|
+
GET = value(:GET, 1)
|
74
|
+
SET = value(:SET, 2)
|
75
|
+
DEL = value(:DEL, 3)
|
76
|
+
REV = value(:REV, 5)
|
77
|
+
WAIT = value(:WAIT, 6)
|
78
|
+
NOP = value(:NOP, 7)
|
79
|
+
WALK = value(:WALK, 9)
|
80
|
+
GETDIR = value(:GETDIR, 14)
|
81
|
+
STAT = value(:STAT, 16)
|
82
|
+
ACCESS = value(:ACCESS, 99)
|
83
|
+
end
|
84
|
+
optional :Verb, :verb, 2
|
85
|
+
optional :string, :path, 4
|
86
|
+
optional :bytes, :value, 5
|
87
|
+
optional :int32, :other_tag, 6
|
88
|
+
optional :int32, :offset, 7
|
89
|
+
optional :int64, :rev, 9
|
90
|
+
end
|
91
|
+
class Response < ::Protobuf::Message
|
92
|
+
defined_in __FILE__
|
93
|
+
optional :int32, :tag, 1
|
94
|
+
optional :int32, :flags, 2
|
95
|
+
optional :int64, :rev, 3
|
96
|
+
optional :string, :path, 5
|
97
|
+
optional :bytes, :value, 6
|
98
|
+
optional :int32, :len, 8
|
99
|
+
class Err < ::Protobuf::Enum
|
100
|
+
defined_in __FILE__
|
101
|
+
OTHER = value(:OTHER, 127)
|
102
|
+
TAG_IN_USE = value(:TAG_IN_USE, 1)
|
103
|
+
UNKNOWN_VERB = value(:UNKNOWN_VERB, 2)
|
104
|
+
READONLY = value(:READONLY, 3)
|
105
|
+
TOO_LATE = value(:TOO_LATE, 4)
|
106
|
+
REV_MISMATCH = value(:REV_MISMATCH, 5)
|
107
|
+
BAD_PATH = value(:BAD_PATH, 6)
|
108
|
+
MISSING_ARG = value(:MISSING_ARG, 7)
|
109
|
+
RANGE = value(:RANGE, 8)
|
110
|
+
NOTDIR = value(:NOTDIR, 20)
|
111
|
+
ISDIR = value(:ISDIR, 21)
|
112
|
+
NOENT = value(:NOENT, 22)
|
113
|
+
end
|
114
|
+
optional :Err, :err_code, 100
|
115
|
+
optional :string, :err_detail, 101
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/ruby_skynet.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'semantic_logger'
|
2
|
+
require 'resilient_socket'
|
3
|
+
|
4
|
+
require 'ruby_skynet/exceptions'
|
5
|
+
require 'ruby_skynet/version'
|
6
|
+
module RubySkynet
|
7
|
+
module Doozer
|
8
|
+
autoload :Client, 'ruby_skynet/doozer/client'
|
9
|
+
end
|
10
|
+
autoload :Client, 'ruby_skynet/client'
|
11
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
platform.active=Ruby
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project xmlns="http://www.netbeans.org/ns/project/1">
|
3
|
+
<type>org.netbeans.modules.ruby.rubyproject</type>
|
4
|
+
<configuration>
|
5
|
+
<data xmlns="http://www.netbeans.org/ns/ruby-project/1">
|
6
|
+
<name>ruby_skynet</name>
|
7
|
+
<source-roots>
|
8
|
+
<root id="src.lib.dir" name="Source Files"/>
|
9
|
+
</source-roots>
|
10
|
+
<test-roots>
|
11
|
+
<root id="test.test.dir"/>
|
12
|
+
</test-roots>
|
13
|
+
</data>
|
14
|
+
</configuration>
|
15
|
+
</project>
|
data/skynet-0.1.0.gem
ADDED
Binary file
|