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 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