zoology 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1 @@
1
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gemspec
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ zoology (0.1)
5
+ state_machine
6
+ zookeeper
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ state_machine (1.2.0)
12
+ zookeeper (1.4.8)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ zoology!
@@ -0,0 +1,2 @@
1
+ require 'rdoc/task'
2
+ require 'bundler/gem_tasks'
@@ -0,0 +1,4 @@
1
+ require 'zoology/logging'
2
+ require 'zoology/callbacks'
3
+ require 'zoology/client'
4
+ require 'zoology/path_cache'
@@ -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
@@ -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: []