zoology 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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +18 -0
- data/Rakefile +2 -0
- data/lib/zoology.rb +4 -0
- data/lib/zoology/callbacks.rb +17 -0
- data/lib/zoology/client.rb +128 -0
- data/lib/zoology/logging.rb +26 -0
- data/lib/zoology/path_cache.rb +57 -0
- data/lib/zoology/path_cache/watcher.rb +67 -0
- data/zoology.gemspec +17 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7fb8d471eda9b3d0294c62df3da33a0475be0971
|
4
|
+
data.tar.gz: 976e309a76a19d24f178663de8e1b6fb421652a5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 943704e42385bdd91a5ec07f32fda23f2e81fc2815e48255b09c5e31e2d3312b0ecd302770871b94797f306244b4fef8cd1a0f6666ee7d5bceda09db535b3289
|
7
|
+
data.tar.gz: 43e63331b7a033963531bd64d3581b4dc0fd32187d252dedade6f11dbbdb360fbd86af6c00c9cdf7523e1e83d2dc4eea30148848d01d557b52d21e3b8b1a9aaa
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.swp
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/Rakefile
ADDED
data/lib/zoology.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Zoology
|
2
|
+
module Callbacks
|
3
|
+
def call(callback, *args)
|
4
|
+
cb = @callbacks[callback]
|
5
|
+
cb.call(*args) if cb
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def callbacks(*callbacks)
|
11
|
+
@callbacks ||= {}
|
12
|
+
callbacks.each do |cb|
|
13
|
+
self.class.send(:define_method, cb) { |&blk| @callbacks[cb] = blk }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Zoology
|
2
|
+
class Client
|
3
|
+
require 'zookeeper'
|
4
|
+
|
5
|
+
include Logging
|
6
|
+
include Callbacks
|
7
|
+
|
8
|
+
def initialize(servers = 'localhost:2181', opts = {}, klass = Zookeeper)
|
9
|
+
@servers = servers
|
10
|
+
@heartbeat = opts[:heartbeat] || 2000
|
11
|
+
@klass = klass
|
12
|
+
|
13
|
+
callbacks :on_connected, :on_expired, :on_disconnected
|
14
|
+
end
|
15
|
+
|
16
|
+
def connect
|
17
|
+
logger.debug 'Connect to ZK'
|
18
|
+
@zookeeper = @klass.new(@servers, @heartbeat, method(:watcher))
|
19
|
+
end
|
20
|
+
|
21
|
+
def reopen
|
22
|
+
logger.debug 'reopen it'
|
23
|
+
@zookeeper.reopen
|
24
|
+
end
|
25
|
+
|
26
|
+
def connected?
|
27
|
+
if @zookeeper
|
28
|
+
@zookeeper.connected?
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def when_path(path, &blk)
|
35
|
+
req = @zookeeper.get(:path => path)
|
36
|
+
if req[:rc] == ::Zookeeper::ZOK
|
37
|
+
blk.call
|
38
|
+
else
|
39
|
+
wait_for_path(path, path, [], &blk)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_missing(sym, *args, &blk)
|
44
|
+
logger.debug "Call to zookeeper #{sym}: #{args.map(&:to_s).join(',')}"
|
45
|
+
@zookeeper.send(sym, *args, &blk)
|
46
|
+
end
|
47
|
+
|
48
|
+
def watcher_callback(&blk)
|
49
|
+
Zookeeper::Callbacks::WatcherCallback.create(&blk)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def watcher(event)
|
55
|
+
case event[:state]
|
56
|
+
when ::Zookeeper::ZOO_CONNECTED_STATE
|
57
|
+
logger.debug 'Received connected state'
|
58
|
+
call(:on_connected)
|
59
|
+
when ::Zookeeper::ZOO_CONNECTING_STATE
|
60
|
+
logger.debug 'Received connecting state'
|
61
|
+
call(:on_disconnected)
|
62
|
+
when ::Zookeeper::ZOO_EXPIRED_SESSION_STATE
|
63
|
+
logger.debug 'Received expired state'
|
64
|
+
call(:on_expired)
|
65
|
+
reopen
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def wait_for_path(complete_path, wait_path, rest = [], &blk)
|
70
|
+
cb = watcher_callback do |event|
|
71
|
+
if event.type != ::Zookeeper::ZOO_SESSION_EVENT
|
72
|
+
next_path = join(wait_path, rest.first)
|
73
|
+
if @zookeeper.get(:path => next_path)[:rc] == ::Zookeeper::ZOK
|
74
|
+
if next_path == complete_path
|
75
|
+
logger.debug "#{complete_path} now exists, watching it"
|
76
|
+
blk.call
|
77
|
+
else
|
78
|
+
logger.debug "#{next_path} exists, moving on to next"
|
79
|
+
rest.shift
|
80
|
+
wait_for_path(complete_path, next_path, rest, &blk)
|
81
|
+
end
|
82
|
+
else
|
83
|
+
wait_for_path(complete_path, wait_path, rest, &blk)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
req = @zookeeper.get_children(:path => wait_path, :watcher => cb)
|
89
|
+
case req[:rc]
|
90
|
+
when ::Zookeeper::ZOK
|
91
|
+
logger.debug "Now watching #{wait_path}"
|
92
|
+
when ::Zookeeper::ZNONODE
|
93
|
+
logger.debug 'Re-resolving paths'
|
94
|
+
wait_path, rest = find_wait_path(complete_path)
|
95
|
+
wait_for_path(complete_path, wait_path, rest, &blk)
|
96
|
+
else
|
97
|
+
logger.warn "wait_for_path #{wait_path} failed: #{req[:rc]}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_wait_path(complete_path)
|
102
|
+
parts = split(complete_path)
|
103
|
+
rest = [parts.pop]
|
104
|
+
until parts.empty?
|
105
|
+
path = join(*parts)
|
106
|
+
if @zookeeper.get(:path => path)[:rc] == ::Zookeeper::ZOK
|
107
|
+
break
|
108
|
+
end
|
109
|
+
rest.unshift parts.pop
|
110
|
+
end
|
111
|
+
[path, rest]
|
112
|
+
end
|
113
|
+
|
114
|
+
def join(*path)
|
115
|
+
p = ::File.join(*path)
|
116
|
+
if p == ''
|
117
|
+
'/'
|
118
|
+
else
|
119
|
+
p
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def split(path)
|
124
|
+
head, *tail = path.split('/')
|
125
|
+
[head] + tail.reject(&:empty?)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Zoology
|
2
|
+
module Logging
|
3
|
+
require 'logger'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
def logger
|
7
|
+
@logger ||= Logging.logger(self.class)
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :level
|
12
|
+
|
13
|
+
def disable!
|
14
|
+
@disable = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def logger(name)
|
18
|
+
sink = @disable ? StringIO.new : STDOUT
|
19
|
+
logger = Logger.new(sink)
|
20
|
+
logger.level = @level || Logger::INFO
|
21
|
+
logger.progname = name
|
22
|
+
logger
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Zoology
|
2
|
+
class PathCache
|
3
|
+
require 'zoology/path_cache/watcher'
|
4
|
+
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
def initialize(client, path, &callback)
|
8
|
+
@client = client
|
9
|
+
@watches = {}
|
10
|
+
@watcher = nil
|
11
|
+
@path = path
|
12
|
+
@callback = callback
|
13
|
+
@client.on_connected do |_event|
|
14
|
+
if @sleep
|
15
|
+
logger.debug "Sleeping for #{@sleep}s to avoid thundering herd"
|
16
|
+
sleep(@sleep) if @sleep
|
17
|
+
end
|
18
|
+
watch_paths
|
19
|
+
end
|
20
|
+
@client.on_expired do |_event|
|
21
|
+
@watches.each { |_path, watcher| watcher.shutdown }
|
22
|
+
@watches = {}
|
23
|
+
@sleep = rand(10)
|
24
|
+
end
|
25
|
+
if @client.connected?
|
26
|
+
watch_paths
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def watch_paths
|
33
|
+
watcher = lambda do |_wpath, type, req|
|
34
|
+
if type == :update
|
35
|
+
update_watches(req[:children]) if req[:rc] == Zookeeper::ZOK
|
36
|
+
else
|
37
|
+
@client.when_path(@path) { watch_paths }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@watcher = Watcher.new(@client, @path, :get_children, &watcher)
|
42
|
+
@watcher.watch
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_watches(children)
|
46
|
+
paths = children.map { |child| File.join(@path, child) }
|
47
|
+
(paths - @watches.keys).each do |path|
|
48
|
+
@watches[path] = Watcher.new(@client, path, &@callback)
|
49
|
+
end
|
50
|
+
(@watches.keys - paths).each do |path|
|
51
|
+
@watches.delete(path).shutdown
|
52
|
+
end
|
53
|
+
@watches.each { |_name, watcher| watcher.watch }
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Zoology; class PathCache
|
2
|
+
class Watcher
|
3
|
+
require 'state_machine'
|
4
|
+
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
state_machine :state, :initial => :not_watching do
|
8
|
+
after_transition :not_watching => :watching, :do => :set_watch
|
9
|
+
before_transition any => :shutdown, :do => :deleted
|
10
|
+
|
11
|
+
event :watch do
|
12
|
+
transition :not_watching => :watching
|
13
|
+
end
|
14
|
+
|
15
|
+
event :not_watching do
|
16
|
+
transition :watching => :not_watching
|
17
|
+
end
|
18
|
+
|
19
|
+
event :shutdown do
|
20
|
+
transition all => :shutdown
|
21
|
+
end
|
22
|
+
|
23
|
+
state :shutdown do
|
24
|
+
def call(type, data = nil)
|
25
|
+
logger.debug "Received call request, but in shutdown so not doing anything: #{@path}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
state all - [:shutdown] do
|
30
|
+
def call(type, data = nil)
|
31
|
+
@callback.call @path, type, data
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(client, path, zk_call = :get, &callback)
|
37
|
+
@client = client
|
38
|
+
@path = path
|
39
|
+
@zk_call = zk_call
|
40
|
+
@callback = callback
|
41
|
+
|
42
|
+
super()
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_watch
|
46
|
+
watcher = @client.watcher_callback do |event|
|
47
|
+
set_watch if event.type != Zookeeper::ZOO_SESSION_EVENT
|
48
|
+
end
|
49
|
+
begin
|
50
|
+
req = @client.send(@zk_call, :path => @path, :watcher => watcher)
|
51
|
+
if req[:rc] == Zookeeper::ZOK
|
52
|
+
call(:update, req)
|
53
|
+
elsif req[:rc] == Zookeeper::ZNONODE
|
54
|
+
shutdown
|
55
|
+
else
|
56
|
+
not_watching
|
57
|
+
end
|
58
|
+
rescue Zookeeper::Exceptions::ContinuationTimeoutError
|
59
|
+
not_watching
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def deleted
|
64
|
+
call(:deleted)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end; end
|
data/zoology.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = 'zoology'
|
3
|
+
gem.version = '0.1'
|
4
|
+
gem.authors = ['Torbjörn Norinder']
|
5
|
+
gem.email = ['torbjorn@genunix.se']
|
6
|
+
gem.description = "Zookeeper utilities and recipes"
|
7
|
+
gem.summary = gem.description
|
8
|
+
gem.homepage = 'https://github.com/campanja/zoology'
|
9
|
+
gem.platform = Gem::Platform::RUBY
|
10
|
+
|
11
|
+
gem.add_dependency 'zookeeper'
|
12
|
+
gem.add_dependency 'state_machine'
|
13
|
+
gem.files = `git ls-files`.split($/)
|
14
|
+
gem.executables = []
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.require_paths = ['lib']
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zoology
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Torbjörn Norinder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: zookeeper
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: state_machine
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Zookeeper utilities and recipes
|
42
|
+
email:
|
43
|
+
- torbjorn@genunix.se
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- Gemfile.lock
|
51
|
+
- Rakefile
|
52
|
+
- lib/zoology.rb
|
53
|
+
- lib/zoology/callbacks.rb
|
54
|
+
- lib/zoology/client.rb
|
55
|
+
- lib/zoology/logging.rb
|
56
|
+
- lib/zoology/path_cache.rb
|
57
|
+
- lib/zoology/path_cache/watcher.rb
|
58
|
+
- zoology.gemspec
|
59
|
+
homepage: https://github.com/campanja/zoology
|
60
|
+
licenses: []
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.2.2
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: Zookeeper utilities and recipes
|
82
|
+
test_files: []
|