zk_service 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in zk_service.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
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,3 @@
1
+ module ZkService
2
+ VERSION = "0.0.1"
3
+ 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
@@ -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