zk_service 0.0.1
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 +4 -0
- data/README.md +58 -0
- data/Rakefile +2 -0
- data/lib/zk_service.rb +131 -0
- data/lib/zk_service/version.rb +3 -0
- data/test/test_zk_service.rb +90 -0
- data/zk_service.gemspec +24 -0
- metadata +115 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
ZK Service
|
2
|
+
=============
|
3
|
+
|
4
|
+
zk_service is a small library which provides a service registry for Zookeeper.
|
5
|
+
|
6
|
+
Servers
|
7
|
+
--------------
|
8
|
+
|
9
|
+
This library provides registration to zookeeper through a mixin.
|
10
|
+
|
11
|
+
This connection can be set by calling,
|
12
|
+
|
13
|
+
For example:
|
14
|
+
|
15
|
+
class SomeServer
|
16
|
+
include ZkService::Publisher
|
17
|
+
|
18
|
+
def intialize
|
19
|
+
zk_service_Details( 'localhost', '2181', 'superservice' )
|
20
|
+
zk_publish("/some/service/path") #Creates an ephemeral node
|
21
|
+
#.....
|
22
|
+
end
|
23
|
+
|
24
|
+
def close
|
25
|
+
#.....
|
26
|
+
zk_unpublish #explicitly removes the ephemeral node
|
27
|
+
end
|
28
|
+
|
29
|
+
Clients
|
30
|
+
-----------
|
31
|
+
|
32
|
+
Upon con requiring a connection to a specific service, a client will consult Zookeeper for the child znodes under '/envname/services/service_name'. These nodes will contain connection details.
|
33
|
+
|
34
|
+
For example:
|
35
|
+
|
36
|
+
class SuperServiceVendor < ZkService::Vendor
|
37
|
+
|
38
|
+
def initialize(host, port)
|
39
|
+
super(host, port)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
Upon initialization, a child connection can be obtained by:
|
45
|
+
|
46
|
+
child_connection = SuperServiceVendorInstance.vend('superservice1')
|
47
|
+
|
48
|
+
Connection details can be obtained by:
|
49
|
+
|
50
|
+
data = SuperServiceVendorInstance.get_data(child_connection)
|
51
|
+
|
52
|
+
|
53
|
+
Issues
|
54
|
+
------
|
55
|
+
|
56
|
+
*Need to check for the existence of a Zookeeper ENV and grab the hostname and port number.
|
57
|
+
|
58
|
+
|
data/Rakefile
ADDED
data/lib/zk_service.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require "zk_service/version"
|
2
|
+
require 'zookeeper'
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module ZkService
|
7
|
+
|
8
|
+
def self.configure( h, p )
|
9
|
+
#config = YAML.env_config( "zookeeper" )
|
10
|
+
#hostkey = config[:host] || "localhost"
|
11
|
+
#port = config[:port] || "2181"
|
12
|
+
hostkey, port = h, p
|
13
|
+
zookeeper_server_addresses = []
|
14
|
+
zookeeper_server_addresses << "#{h}:#{p}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.check_namespaces(zookeeper_namespace)
|
18
|
+
zookeeper_namespace ||= "/"
|
19
|
+
if zookeeper_namespace && !zookeeper_namespace.start_with?("/")
|
20
|
+
raise InvalidZookeeperConfig.new("Zookeeper namespace should start with '/'")
|
21
|
+
end
|
22
|
+
zookeeper_namespace
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.create_connection(zookeeper_server_addresses)
|
26
|
+
begin
|
27
|
+
tryable = zookeeper_server_addresses.choice
|
28
|
+
Zookeeper.new( tryable )
|
29
|
+
# TODO: do not resque Exception or explain, why that is necessary
|
30
|
+
rescue Exception => e
|
31
|
+
zookeeper_server_addresses.shift
|
32
|
+
if zookeeper_server_addresses.empty?
|
33
|
+
raise NoAvailableZookeeper.new # We're out of options.
|
34
|
+
else
|
35
|
+
create_connection(zookeeper_server_addresses) # We recurse to try again.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module Publisher
|
41
|
+
extend ZkService
|
42
|
+
|
43
|
+
attr_reader :zk
|
44
|
+
attr_accessor :zookeeper_server_addresses,:zookeeper_namespace, :provider_path
|
45
|
+
|
46
|
+
def zk_service_details(host, port, service_name)
|
47
|
+
@zookeeper_server_addresses ||= []
|
48
|
+
@provider_path ||= ""
|
49
|
+
@zookeeper_server_addresses = !@zookeeper_server_addresses.empty? || ZkService.configure( host, port )
|
50
|
+
@zk = ZkService.create_connection(@zookeeper_server_addresses)
|
51
|
+
|
52
|
+
@zookeeper_namespace = ZkService.check_namespaces(@zookeeper_namespace)
|
53
|
+
@zookeeper_namespace += service_name
|
54
|
+
end
|
55
|
+
|
56
|
+
def zk_path_exists(path)
|
57
|
+
path.nil?? false : @zk.stat(:path => path)[:stat].exists
|
58
|
+
end
|
59
|
+
|
60
|
+
def zk_publish( data )
|
61
|
+
#register service if not already registered
|
62
|
+
if !zk_path_exists(@zookeeper_namespace)
|
63
|
+
response = @zk.create(:path => @zookeeper_namespace)
|
64
|
+
raise ZkServicePublisherError.new("ZK ERROR: Could not create #{service}'") if response[:rc] != Zookeeper::ZOK
|
65
|
+
end
|
66
|
+
|
67
|
+
path = @zookeeper_namespace + '/provider-'
|
68
|
+
response = (!zk_path_exists(path)) ? @zk.create(:path => path, :data => data.to_json, :sequence => true, :ephemeral => true) : false
|
69
|
+
raise ZkServicePublisherError.new("ZK ERROR: Could not publish path '#{path}'") if response[:rc] != Zookeeper::ZOK
|
70
|
+
|
71
|
+
@provider_path = response[:path]
|
72
|
+
response
|
73
|
+
end
|
74
|
+
|
75
|
+
def zk_unpublish
|
76
|
+
@zk.delete(:path => @provider_path)
|
77
|
+
raise ZkServicePublisherError.new("ZK ERROR: Could not unpublish from path '#{@provider_path}'") if zk_path_exists(@provider_path)
|
78
|
+
end
|
79
|
+
|
80
|
+
def zk_reset_host( host, port )
|
81
|
+
@zookeeper_server_addresses = []
|
82
|
+
@zookeeper_server_addresses << "#{host}:#{port}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Vendor
|
87
|
+
include ZkService
|
88
|
+
|
89
|
+
attr_reader :base_details, :zk
|
90
|
+
attr_accessor :zookeeper_server_addresses,:zookeeper_namespace
|
91
|
+
|
92
|
+
def initialize( host , port )
|
93
|
+
@zookeeper_server_addresses ||= []
|
94
|
+
@zookeeper_namespace ||= "/"
|
95
|
+
@zookeeper_server_addresses << "#{host}:#{port}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def zk_path_exists(path)
|
99
|
+
path.nil?? false : @zk.stat(:path => path)[:stat].exists
|
100
|
+
end
|
101
|
+
|
102
|
+
#return a child ephemeral node
|
103
|
+
def vend(service_name)
|
104
|
+
@zk = ZkService.create_connection(@zookeeper_server_addresses)
|
105
|
+
@service_path = @zookeeper_namespace + service_name
|
106
|
+
|
107
|
+
if zk_path_exists(@service_path)
|
108
|
+
response = @zk.get_children(:path => @service_path)
|
109
|
+
if !response[:children].nil?
|
110
|
+
response[:children].choice
|
111
|
+
else
|
112
|
+
raise ZkServiceVendorError.new("ZK ERROR: No nodes under #{@service_path}'")
|
113
|
+
end
|
114
|
+
else
|
115
|
+
raise ZkServiceVendorError.new("ZK ERROR: Could not find #{@service_path}'")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_data(child_path)
|
120
|
+
#need better error handling
|
121
|
+
return false if @service_path.empty?
|
122
|
+
path = @service_path + "/" + child_path
|
123
|
+
@zk.get(:path => path )[:data]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class NoAvailableZookeeper < StandardError ; end
|
128
|
+
class ZkServicePublisherError < StandardError ; end
|
129
|
+
class ZkServiceVendorError < StandardError ; end
|
130
|
+
class InvalidZookeeperConfig < StandardError ; end
|
131
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
dir = File.dirname(__FILE__)
|
5
|
+
$LOAD_PATH.unshift(File.join(dir, '..', 'lib'))
|
6
|
+
$LOAD_PATH.unshift(dir)
|
7
|
+
require 'zk_service'
|
8
|
+
|
9
|
+
describe "ZK Service Publisher" do
|
10
|
+
before :all do
|
11
|
+
class SomeServer
|
12
|
+
include ZkService::Publisher
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
zk_service_details('localhost', '2181', 'superservice1')
|
16
|
+
end
|
17
|
+
|
18
|
+
def publish(data)
|
19
|
+
zk_publish(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
def close; zk_unpublish; end
|
23
|
+
|
24
|
+
def path_exists(path); zk_path_exists(path); end
|
25
|
+
|
26
|
+
def get_zk; @zk; end
|
27
|
+
|
28
|
+
def reset_host(host,port); zk_reset_host(host,port); end
|
29
|
+
|
30
|
+
def connect_again
|
31
|
+
ZkService.create_connection(@zookeeper_server_addresses)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@zk_server = SomeServer.new
|
35
|
+
end
|
36
|
+
|
37
|
+
it "updates zk service details" do
|
38
|
+
@zk_server.zookeeper_namespace.should == "/superservice1"
|
39
|
+
@zk_server.zookeeper_server_addresses.should == ["localhost:2181"]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can publish its configs to zookeeper" do
|
43
|
+
@zk_server.publish({:hostname => 'awesome', :port => '99', :version => "3"})
|
44
|
+
children = @zk_server.get_zk.get_children(:path => '/superservice1')[:children]
|
45
|
+
children.any? { |w| w.include? 'provider-' }.should be true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "connects and registers the service directory" do
|
49
|
+
@zk_server.path_exists('/superservice1').should == true
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can remove its configs from zookeeper and reconnects" do
|
53
|
+
@zk_server.close
|
54
|
+
@zk_server.path_exists(@zk_server.provider_path).should == false
|
55
|
+
@zk_server.publish({:hostname => 'awesome', :port => '99', :version => "3"})
|
56
|
+
end
|
57
|
+
|
58
|
+
it "with the incorrect host, error gracefully" do
|
59
|
+
@zk_server.reset_host("incorrect_hostname",'84886')
|
60
|
+
lambda { @zk_server.connect_again }.should raise_error(ZkService::NoAvailableZookeeper)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "ZK Service Vendor" do
|
65
|
+
before :all do
|
66
|
+
class SuperServiceVendor < ZkService::Vendor
|
67
|
+
def initialize(host, port)
|
68
|
+
super(host, port)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@zk_client = SuperServiceVendor.new('localhost','2181')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "connects and retrieves connection details from a child" do
|
75
|
+
child_connection = @zk_client.vend('superservice1')
|
76
|
+
(child_connection.include? 'provider-').should be true
|
77
|
+
end
|
78
|
+
|
79
|
+
it "connects and retrieves connection details from a child" do
|
80
|
+
child_connection = @zk_client.vend('superservice1')
|
81
|
+
data = @zk_client.get_data(child_connection)
|
82
|
+
config_details = JSON.parse(data)
|
83
|
+
config_details["port"].should == "99"
|
84
|
+
config_details["hostname"].should == "awesome"
|
85
|
+
end
|
86
|
+
|
87
|
+
it "cant find the service" do
|
88
|
+
lambda { @zk_client.vend('superservice2131231') }.should raise_error(ZkService::Vendor::ZkServiceVendorError)
|
89
|
+
end
|
90
|
+
end
|
data/zk_service.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "zk_service/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "zk_service"
|
7
|
+
s.version = "0.0.1"
|
8
|
+
s.authors = ["Susheel Khamkar"]
|
9
|
+
s.email = ["susheel@crowdflower.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{ZK Service}
|
12
|
+
s.description = %q{This is a small library which provides a service registry for Zookeeper.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "zk_service"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "zookeeper"
|
22
|
+
s.add_dependency "json"
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zk_service
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Susheel Khamkar
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-03-13 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: zookeeper
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: This is a small library which provides a service registry for Zookeeper.
|
64
|
+
email:
|
65
|
+
- susheel@crowdflower.com
|
66
|
+
executables: []
|
67
|
+
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
extra_rdoc_files: []
|
71
|
+
|
72
|
+
files:
|
73
|
+
- Gemfile
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- lib/zk_service.rb
|
77
|
+
- lib/zk_service/version.rb
|
78
|
+
- test/test_zk_service.rb
|
79
|
+
- zk_service.gemspec
|
80
|
+
has_rdoc: true
|
81
|
+
homepage: ""
|
82
|
+
licenses: []
|
83
|
+
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 3
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
requirements: []
|
108
|
+
|
109
|
+
rubyforge_project: zk_service
|
110
|
+
rubygems_version: 1.4.2
|
111
|
+
signing_key:
|
112
|
+
specification_version: 3
|
113
|
+
summary: ZK Service
|
114
|
+
test_files:
|
115
|
+
- test/test_zk_service.rb
|