ruby_skynet 0.5.0 → 0.6.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.
@@ -0,0 +1,122 @@
1
+ # Allow test to be run in-place without requiring a gem install
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'shoulda'
7
+ require 'ruby_skynet'
8
+
9
+ # Register an appender if one is not already registered
10
+ if SemanticLogger::Logger.appenders.size == 0
11
+ SemanticLogger::Logger.default_level = :trace
12
+ SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
13
+ end
14
+
15
+ # Unit Test
16
+ class ServiceRegistryTest < Test::Unit::TestCase
17
+ context RubySkynet::ServiceRegistry do
18
+
19
+ setup do
20
+ @service_name = 'MyRegistryService'
21
+ @version = 5
22
+ @region = 'RegistryTest'
23
+ @hostname = '127.0.0.1'
24
+ @port = 2100
25
+ @service_key = "/services/#{@service_name}/#{@version}/#{@region}/#{@hostname}/#{@port}"
26
+ end
27
+
28
+ context "without a registered service" do
29
+ should "not be in doozer" do
30
+ RubySkynet.services.send(:doozer_pool).with_connection do |doozer|
31
+ assert_equal '', doozer[@service_key]
32
+ end
33
+ end
34
+ end
35
+
36
+ context "with a registered service" do
37
+ setup do
38
+ RubySkynet.services.register_service(@service_name, @version, @region, @hostname, @port)
39
+ # Allow time for doozer callback that service was registered
40
+ sleep 0.1
41
+ end
42
+
43
+ teardown do
44
+ RubySkynet.services.deregister_service(@service_name, @version, @region, @hostname, @port)
45
+ # Allow time for doozer callback that service was deregistered
46
+ sleep 0.1
47
+ # No servers should be in the local registry
48
+ assert_equal nil, RubySkynet.services.servers_for(@service_name, @version, @region)
49
+ end
50
+
51
+ should "find server using exact match" do
52
+ assert servers = RubySkynet.services.servers_for(@service_name, @version, @region)
53
+ assert_equal 1, servers.size
54
+ assert_equal "#{@hostname}:#{@port}", servers.first
55
+ end
56
+
57
+ should "find server using * version match" do
58
+ assert servers = RubySkynet.services.servers_for(@service_name, '*', @region)
59
+ assert_equal 1, servers.size
60
+ assert_equal "#{@hostname}:#{@port}", servers.first
61
+ end
62
+
63
+ context "with multiple servers" do
64
+ setup do
65
+ @second_hostname = '127.0.10.1'
66
+ RubySkynet.services.register_service(@service_name, @version, @region, @hostname, @port+1)
67
+ RubySkynet.services.register_service(@service_name, @version, @region, @hostname, @port+3)
68
+ RubySkynet.services.register_service(@service_name, @version-1, @region, @hostname, @port+2)
69
+ RubySkynet.services.register_service(@service_name, @version, @region, @second_hostname, @port)
70
+ end
71
+
72
+ teardown do
73
+ RubySkynet.services.deregister_service(@service_name, @version, @region, @hostname, @port+1)
74
+ RubySkynet.services.deregister_service(@service_name, @version, @region, @hostname, @port+3)
75
+ RubySkynet.services.deregister_service(@service_name, @version-1, @region, @hostname, @port+2)
76
+ RubySkynet.services.deregister_service(@service_name, @version, @region, @second_hostname, @port)
77
+ end
78
+
79
+ should "using * version match" do
80
+ assert servers = RubySkynet.services.servers_for(@service_name, '*', @region)
81
+ assert_equal 3, servers.size, RubySkynet.services.to_h.to_s
82
+ assert_equal "#{@hostname}:#{@port}", servers.first
83
+ assert_equal "#{@hostname}:#{@port+3}", servers.last
84
+ end
85
+ end
86
+
87
+ should "return nil when service not found" do
88
+ assert_equal nil, RubySkynet.services.servers_for('MissingService', @version, @region)
89
+ end
90
+
91
+ should "return nil when version not found" do
92
+ assert_equal nil, RubySkynet.services.servers_for(@service_name, @version+1, @region)
93
+ end
94
+
95
+ should "return nil when region not found" do
96
+ assert_equal nil, RubySkynet.services.servers_for(@service_name, @version, 'OtherRegion')
97
+ end
98
+
99
+ should "be in doozer" do
100
+ RubySkynet.services.send(:doozer_pool).with_connection do |doozer|
101
+ assert_equal true, doozer[@service_key].length > 20
102
+ end
103
+ end
104
+ end
105
+
106
+ context "scoring" do
107
+ [
108
+ ['192.168.11.0', 4 ],
109
+ ['192.168.11.10', 3 ],
110
+ ['192.168.10.0', 2 ],
111
+ ['192.5.10.0', 1 ],
112
+ ['10.0.11.0', 0 ],
113
+ ].each do |test|
114
+ should "handle score #{test[1]}" do
115
+ local_ip_address = "192.168.11.0"
116
+ assert_equal test[1], RubySkynet::ServiceRegistry.score_for_server(test[0], local_ip_address), "Local: #{local_ip_address} Server: #{test[0]} Score: #{test[1]}"
117
+ end
118
+ end
119
+ end
120
+
121
+ end
122
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_skynet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-03-27 00:00:00.000000000 Z
11
+ date: 2013-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: semantic_logger
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '>='
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 2.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 2.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: resilient_socket
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '>='
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.4.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 0.4.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: multi_json
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,42 +58,42 @@ dependencies:
58
58
  requirements:
59
59
  - - '>='
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 1.5.2
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - '>='
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 1.5.2
69
69
  - !ruby/object:Gem::Dependency
70
- name: ruby_protobuf
70
+ name: ruby_doozer
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - '>='
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 0.5.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '>='
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 0.5.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: gene_pool
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - '>='
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: 1.3.0
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - '>='
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: 1.3.0
97
97
  description: Ruby Client for invoking Skynet services
98
98
  email:
99
99
  - reidmo@gmail.com
@@ -106,7 +106,6 @@ files:
106
106
  - LICENSE.txt
107
107
  - README.md
108
108
  - Rakefile
109
- - examples/e.rb
110
109
  - examples/echo_client.rb
111
110
  - examples/echo_server.rb
112
111
  - lib/rails/generators/ruby_skynet/config/config_generator.rb
@@ -116,22 +115,18 @@ files:
116
115
  - lib/ruby_skynet/client.rb
117
116
  - lib/ruby_skynet/common.rb
118
117
  - lib/ruby_skynet/connection.rb
119
- - lib/ruby_skynet/doozer/client.rb
120
- - lib/ruby_skynet/doozer/exceptions.rb
121
- - lib/ruby_skynet/doozer/msg.pb.rb
122
118
  - lib/ruby_skynet/exceptions.rb
123
119
  - lib/ruby_skynet/railtie.rb
124
120
  - lib/ruby_skynet/railties/ruby_skynet.rake
125
- - lib/ruby_skynet/registry.rb
126
121
  - lib/ruby_skynet/ruby_skynet.rb
127
122
  - lib/ruby_skynet/server.rb
128
123
  - lib/ruby_skynet/service.rb
124
+ - lib/ruby_skynet/service_registry.rb
129
125
  - lib/ruby_skynet/version.rb
130
126
  - test.sh
131
127
  - test/base_test.rb
132
128
  - test/client_test.rb
133
- - test/doozer_client_test.rb
134
- - test/registry_test.rb
129
+ - test/service_registry_test.rb
135
130
  - test/service_test.rb
136
131
  homepage: https://github.com/ClarityServices/ruby_skynet
137
132
  licenses:
data/examples/e.rb DELETED
File without changes
@@ -1,209 +0,0 @@
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
- include SemanticLogger::Loggable
12
-
13
- # Create a resilient client connection to a Doozer server
14
- def initialize(params={})
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.dup
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
- # Wait for changes to the supplied path
146
- # Returns the next change to the supplied path
147
- def wait(path, rev=current_revision, timeout=-1)
148
- invoke(Request.new(:path => path, :rev => rev, :verb => Request::Verb::WAIT), true, timeout)
149
- end
150
-
151
- # Watch for any changes to the supplied path, calling the supplied block
152
- # for every change
153
- # Runs until an exception is thrown
154
- #
155
- # If a connection error occurs it will create a new connection to doozer
156
- # and resubmit the wait. I.e. Will continue from where it left off
157
- # without any noticeable effect to the supplied block
158
- def watch(path, rev=current_revision)
159
- loop do
160
- result = wait(path, rev, -1)
161
- yield result
162
- rev = result.rev + 1
163
- end
164
- end
165
-
166
- #####################
167
- #protected
168
-
169
- # Call the Doozer server
170
- #
171
- # When readonly ==> true the request is always retried on network failure
172
- # When readonly ==> false the request is retried on network failure
173
- # _only_ if a rev has been supplied
174
- #
175
- # When modifier is true
176
- def invoke(request, readonly=true, timeout=nil)
177
- retry_read = readonly || !request.rev.nil?
178
- response = nil
179
- @socket.retry_on_connection_failure do
180
- send(request)
181
- response = read(timeout) if retry_read
182
- end
183
- # Network error on read must be sent back to caller since we do not
184
- # know if the modification was made
185
- response = read(timeout) unless retry_read
186
- raise ResponseError.new("#{Response::Err.name_by_value(response.err_code)} (#{response.err_code}): #{response.err_detail}") if response.err_code != 0
187
- response
188
- end
189
-
190
- # Send the protobuf Request to Doozer
191
- def send(request)
192
- request.tag = 0
193
- data = request.serialize_to_string
194
- # An additional header is added to the request indicating the size of the request
195
- head = [data.length].pack("N")
196
- @socket.write(head+data)
197
- end
198
-
199
- # Read the protobuf Response from Doozer
200
- def read(timeout=nil)
201
- # First strip the additional header indicating the size of the subsequent response
202
- head = @socket.read(4,nil,timeout)
203
- length = head.unpack("N")[0]
204
- Response.new.parse_from_string(@socket.read(length))
205
- end
206
-
207
- end
208
- end
209
- end
@@ -1,5 +0,0 @@
1
- module RubySkynet
2
- module Doozer
3
- class ResponseError < ::RuntimeError; end
4
- end
5
- end
@@ -1,118 +0,0 @@
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