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.
@@ -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: []