kazoo-ruby 0.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: ec91cf4af491cd9c00ad11b459c64b92884d9289
4
+ data.tar.gz: 585ba40ab883b2bc70014ae38ece4fb7f4240bcf
5
+ SHA512:
6
+ metadata.gz: 3f7021a9e7f42e3315d9c7386db8113619c555881535c1ddc44e2c4aec07983acd02d3f49442b3654090376f2d33f3b95a463cc375c5b2e1eab9a9778a054928
7
+ data.tar.gz: 10bc6041afe7942eeaaa214ef9609ff391e1b8eef6daa723b626f6994bf3cb9dd875c5bf5bf3e68a80c66c7e489c8999ec293c03b5f2f7279cc9935280a0f3c8
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ sudo: false
3
+
4
+ rvm:
5
+ - "2.0"
6
+ - "2.1"
7
+ - "2.2"
8
+
9
+ cache:
10
+ - bundler
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Willem van Bergen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # Kazoo
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'kazoo'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install kazoo
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/kazoo/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs = ["lib", "test"]
6
+ t.test_files = FileList['test/*_test.rb']
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
3
+ require 'kazoo/cli'
4
+
5
+ begin
6
+ ENV["THOR_DEBUG"] = "1"
7
+ Kazoo::CLI.start(ARGV)
8
+ rescue Thor::UndefinedCommandError, Thor::UnknownArgumentError, Thor::AmbiguousCommandError, Thor::InvocationError => e
9
+ $stderr.puts(e.message)
10
+ exit(64)
11
+ rescue Thor::Error => e
12
+ $stderr.puts(e.message)
13
+ exit(1)
14
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kazoo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kazoo-ruby"
8
+ spec.version = Kazoo::VERSION
9
+ spec.authors = ["Willem van Bergen"]
10
+ spec.email = ["willem@vanbergen.org"]
11
+ spec.summary = %q{Library to access Kafka metadata in Zookeeper}
12
+ spec.description = %q{Library to access Kafka metadata in Zookeeper}
13
+ spec.homepage = "https://github.com/wvanbergen/kazoo"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "thor", "~> 0.19.1"
22
+ spec.add_runtime_dependency "zookeeper", "~> 1.4"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "mocha", "~> 1.1"
27
+ spec.add_development_dependency "minitest", "~> 5"
28
+ end
@@ -0,0 +1,12 @@
1
+ require 'zookeeper'
2
+ require 'json'
3
+ require 'thread'
4
+
5
+ module Kazoo
6
+ end
7
+
8
+ require 'kazoo/cluster'
9
+ require 'kazoo/broker'
10
+ require 'kazoo/topic'
11
+ require 'kazoo/partition'
12
+ require 'kazoo/version'
@@ -0,0 +1,68 @@
1
+ module Kazoo
2
+ class Broker
3
+ attr_reader :cluster, :id, :host, :port, :jmx_port
4
+
5
+ def initialize(cluster, id, host, port, jmx_port: nil)
6
+ @cluster = cluster
7
+ @id, @host, @port = id, host, port
8
+ @jmx_port = jmx_port
9
+ end
10
+
11
+ def led_partitions
12
+ result, threads, mutex = [], ThreadGroup.new, Mutex.new
13
+ cluster.partitions.each do |partition|
14
+ t = Thread.new do
15
+ select = partition.leader == self
16
+ mutex.synchronize { result << partition } if select
17
+ end
18
+ threads.add(t)
19
+ end
20
+ threads.list.each(&:join)
21
+ result
22
+ end
23
+
24
+ def replicated_partitions
25
+ result, threads, mutex = [], ThreadGroup.new, Mutex.new
26
+ cluster.partitions.each do |partition|
27
+ t = Thread.new do
28
+ select = partition.replicas.include?(self)
29
+ mutex.synchronize { result << partition } if select
30
+ end
31
+ threads.add(t)
32
+ end
33
+ threads.list.each(&:join)
34
+ result
35
+ end
36
+
37
+ def critical?(replicas: 1)
38
+ result, threads, mutex = false, ThreadGroup.new, Mutex.new
39
+ replicated_partitions.each do |partition|
40
+ t = Thread.new do
41
+ isr = partition.isr.reject { |r| r == self }
42
+ mutex.synchronize { result = true if isr.length < replicas }
43
+ end
44
+ threads.add(t)
45
+ end
46
+ threads.list.each(&:join)
47
+ result
48
+ end
49
+
50
+ def addr
51
+ "#{host}:#{port}"
52
+ end
53
+
54
+ def eql?(other)
55
+ other.is_a?(Kazoo::Broker) && other.cluster == self.cluster && other.id == self.id
56
+ end
57
+
58
+ alias_method :==, :eql?
59
+
60
+ def hash
61
+ [self.cluster, self.id].hash
62
+ end
63
+
64
+ def self.from_json(cluster, id, json)
65
+ new(cluster, id.to_i, json.fetch('host'), json.fetch('port'), jmx_port: json.fetch('jmx_port', nil))
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,79 @@
1
+ require 'kazoo'
2
+ require 'thor'
3
+
4
+ module Kazoo
5
+ class CLI < Thor
6
+ class_option :zookeeper, :type => :string, :default => ENV['ZOOKEEPER_PEERS']
7
+ class_option :chroot, :type => :string, :default => ""
8
+
9
+ desc "cluster", "Describes the Kafka cluster as registered in Zookeeper"
10
+ def cluster
11
+ validate_class_options!
12
+
13
+ kafka_cluster.brokers.values.sort_by(&:id).each do |broker|
14
+ $stdout.puts "#{broker.id}:\t#{broker.addr}\t(hosts #{broker.replicated_partitions.length} partitions, leads #{broker.led_partitions.length})"
15
+ end
16
+ end
17
+
18
+ desc "topics", "Lists all topics in the cluster"
19
+ def topics
20
+ validate_class_options!
21
+
22
+ kafka_cluster.topics.values.sort_by(&:name).each do |topic|
23
+ $stdout.puts topic.name
24
+ end
25
+ end
26
+
27
+ option :topic, :type => :string
28
+ desc "partitions", "Lists partitions"
29
+ def partitions
30
+ validate_class_options!
31
+
32
+ topics = kafka_cluster.topics.values
33
+ topics.select! { |t| t.name == options[:topic] } if options[:topic]
34
+ topics.sort_by!(&:name)
35
+
36
+ topics.each do |topic|
37
+ topic.partitions.each do |partition|
38
+ $stdout.puts "#{partition.topic.name}/#{partition.id}\tReplicas: #{partition.replicas.map(&:id).join(",")}"
39
+ end
40
+ end
41
+ end
42
+
43
+ option :replicas, :type => :numeric, :default => 1
44
+ desc "critical <broker>", "Determine whether a broker is critical"
45
+ def critical(broker_name)
46
+ validate_class_options!
47
+
48
+ if broker(broker_name).critical?(replicas: options[:replicas])
49
+ raise Thor::Error, "WARNING: broker #{broker_name} is critical and cannot be stopped safely!"
50
+ else
51
+ $stdout.puts "Broker #{broker_name} is non-critical and can be stopped safely."
52
+ end
53
+ end
54
+
55
+
56
+ private
57
+
58
+ def validate_class_options!
59
+ if options[:zookeeper].nil? || options[:zookeeper] == ''
60
+ raise Thor::InvocationError, "Please supply --zookeeper argument, or set the ZOOKEEPER_PEERS environment variable"
61
+ end
62
+ end
63
+
64
+ def broker(name_or_id)
65
+ broker = if name_or_id =~ /\A\d+\z/
66
+ kafka_cluster.brokers[name_or_id.to_i]
67
+ else
68
+ kafka_cluster.brokers.values.detect { |b| b.addr == name_or_id } || cluster.brokers.values.detect { |b| b.host == name_or_id }
69
+ end
70
+
71
+ raise Thor::InvocationError, "Broker #{name_or_id.inspect} not found!" if broker.nil?
72
+ broker
73
+ end
74
+
75
+ def kafka_cluster
76
+ @kafka_cluster ||= Kazoo::Cluster.new(options[:zookeeper], chroot: options[:chroot])
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,71 @@
1
+ module Kazoo
2
+ class Cluster
3
+
4
+ attr_reader :zookeeper, :chroot
5
+
6
+ def initialize(zookeeper, chroot: "")
7
+ @zookeeper, @chroot = zookeeper, chroot
8
+ @zk_mutex, @brokers_mutex, @topics_mutex = Mutex.new, Mutex.new, Mutex.new
9
+ end
10
+
11
+ def zk
12
+ @zk_mutex.synchronize do
13
+ @zk ||= Zookeeper.new(zookeeper)
14
+ end
15
+ end
16
+
17
+ def brokers
18
+ @brokers_mutex.synchronize do
19
+ @brokers ||= begin
20
+ brokers = zk.get_children(path: node_with_chroot("/brokers/ids"))
21
+ result, threads, mutex = {}, ThreadGroup.new, Mutex.new
22
+ brokers.fetch(:children).map do |id|
23
+ t = Thread.new do
24
+ broker_info = zk.get(path: node_with_chroot("/brokers/ids/#{id}"))
25
+ broker = Kazoo::Broker.from_json(self, id, JSON.parse(broker_info.fetch(:data)))
26
+ mutex.synchronize { result[id.to_i] = broker }
27
+ end
28
+ threads.add(t)
29
+ end
30
+ threads.list.each(&:join)
31
+ result
32
+ end
33
+ end
34
+ end
35
+
36
+ def topics
37
+ @topics_mutex.synchronize do
38
+ @topics ||= begin
39
+ topics = zk.get_children(path: node_with_chroot("/brokers/topics"))
40
+ result, threads, mutex = {}, ThreadGroup.new, Mutex.new
41
+ topics.fetch(:children).each do |name|
42
+ t = Thread.new do
43
+ topic_info = zk.get(path: node_with_chroot("/brokers/topics/#{name}"))
44
+ topic = Kazoo::Topic.from_json(self, name, JSON.parse(topic_info.fetch(:data)))
45
+ mutex.synchronize { result[name] = topic }
46
+ end
47
+ threads.add(t)
48
+ end
49
+ threads.list.each(&:join)
50
+ result
51
+ end
52
+ end
53
+ end
54
+
55
+ def partitions
56
+ topics.values.flat_map(&:partitions)
57
+ end
58
+
59
+ def reset_metadata
60
+ @topics, @brokers = nil, nil
61
+ end
62
+
63
+ def under_replicated?
64
+ partitions.any?(&:under_replicated?)
65
+ end
66
+
67
+ def node_with_chroot(path)
68
+ "#{@chroot}#{path}"
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,48 @@
1
+ module Kazoo
2
+ class Partition
3
+ attr_reader :topic, :id, :replicas
4
+
5
+ def initialize(topic, id, replicas: nil)
6
+ @topic, @id, @replicas = topic, id, replicas
7
+ @mutex = Mutex.new
8
+ end
9
+
10
+ def cluster
11
+ topic.cluster
12
+ end
13
+
14
+ def replication_factor
15
+ replicas.length
16
+ end
17
+
18
+ def leader
19
+ @mutex.synchronize do
20
+ refresh_state if @leader.nil?
21
+ @leader
22
+ end
23
+ end
24
+
25
+ def isr
26
+ @mutex.synchronize do
27
+ refresh_state if @isr.nil?
28
+ @isr
29
+ end
30
+ end
31
+
32
+ def under_replicated?
33
+ isr.length < replication_factor
34
+ end
35
+
36
+ protected
37
+
38
+ def refresh_state
39
+ state_json = cluster.zk.get(path: cluster.node_with_chroot("/brokers/topics/#{topic.name}/partitions/#{id}/state"))
40
+ set_state(JSON.parse(state_json.fetch(:data)))
41
+ end
42
+
43
+ def set_state(json)
44
+ @leader = cluster.brokers.fetch(json.fetch('leader'))
45
+ @isr = json.fetch('isr').map { |r| cluster.brokers.fetch(r) }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ module Kazoo
2
+ class Topic
3
+
4
+ attr_reader :cluster, :name
5
+ attr_accessor :partitions
6
+
7
+ def initialize(cluster, name, partitions: nil)
8
+ @cluster, @name, @partitions = cluster, name, partitions
9
+ end
10
+
11
+ def self.from_json(cluster, name, json)
12
+ topic = new(cluster, name)
13
+ topic.partitions = json.fetch('partitions').map do |(id, replicas)|
14
+ topic.partition(id.to_i, replicas: replicas.map { |b| cluster.brokers[b] })
15
+ end.sort_by(&:id)
16
+
17
+ return topic
18
+ end
19
+
20
+ def partition(*args)
21
+ Kazoo::Partition.new(self, *args)
22
+ end
23
+
24
+ def replication_factor
25
+ partitions.map(&:replication_factor).min
26
+ end
27
+
28
+ def under_replicated?
29
+ partitions.any?(:under_replicated?)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Kazoo
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+
3
+ class BrokerTest < Minitest::Test
4
+ include MockCluster
5
+
6
+ def setup
7
+ @cluster = mock_cluster
8
+ end
9
+
10
+ def test_broker_critical?
11
+ refute @cluster.brokers[1].critical?(replicas: 1), "We have 2 in-sync replicas for everything so we can lose 1."
12
+ assert @cluster.brokers[2].critical?(replicas: 2), "We only have 2 replicas so we can never lose 2."
13
+
14
+ # Simulate losing a broker from the ISR for a partition.
15
+ # This partition lives on broker 1 and 3
16
+ @cluster.topics['test.4'].partitions[2].expects(:isr).returns([@cluster.brokers[1]])
17
+
18
+ assert @cluster.brokers[1].critical?(replicas: 1), "Final remaining broker for this partition's ISR set, cannot lose"
19
+ refute @cluster.brokers[2].critical?(replicas: 1), "Not related to the under-replicated partitions"
20
+ refute @cluster.brokers[3].critical?(replicas: 1), "Already down, so not critical"
21
+ end
22
+
23
+ def test_from_json
24
+ json_payload = '{"jmx_port":9999,"timestamp":"1431719964125","host":"kafka03.example.com","version":1,"port":9092}'
25
+ broker = Kazoo::Broker.from_json(mock('cluster'), 3, JSON.parse(json_payload))
26
+
27
+ assert_equal 3, broker.id
28
+ assert_equal 'kafka03.example.com', broker.host
29
+ assert_equal 9092, broker.port
30
+ assert_equal 9999, broker.jmx_port
31
+ assert_equal "kafka03.example.com:9092", broker.addr
32
+ end
33
+
34
+ def test_replicated_partitions
35
+ assert_equal 3, @cluster.brokers[1].replicated_partitions.length
36
+ assert_equal 4, @cluster.brokers[2].replicated_partitions.length
37
+ assert_equal 3, @cluster.brokers[3].replicated_partitions.length
38
+ end
39
+
40
+ def test_led_partitions
41
+ assert_equal 2, @cluster.brokers[1].led_partitions.length
42
+ assert_equal 2, @cluster.brokers[2].led_partitions.length
43
+ assert_equal 1, @cluster.brokers[3].led_partitions.length
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class ClusterTest < Minitest::Test
4
+ include MockCluster
5
+
6
+ def setup
7
+ @cluster = mock_cluster
8
+ end
9
+
10
+ def test_cluster_under_replicated?
11
+ refute @cluster.under_replicated?
12
+
13
+ @cluster.topics['test.4'].partitions[2].expects(:isr).returns([@cluster.brokers[1]])
14
+ assert @cluster.under_replicated?
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class PartitionTest < Minitest::Test
4
+ include MockCluster
5
+
6
+ def setup
7
+ @cluster = mock_cluster
8
+ end
9
+
10
+ def test_replication_factor
11
+ assert_equal 2, @cluster.topics['test.1'].partitions[0].replication_factor
12
+ end
13
+
14
+ def test_state
15
+ partition = @cluster.topics['test.1'].partitions[0]
16
+ partition.unstub(:leader)
17
+ partition.unstub(:isr)
18
+
19
+ json_payload = '{"controller_epoch":157,"leader":1,"version":1,"leader_epoch":8,"isr":[3,2,1]}'
20
+ @cluster.zk.expects(:get).with(path: "/brokers/topics/test.1/partitions/0/state").returns(data: json_payload)
21
+
22
+ assert_equal 1, partition.leader.id
23
+ assert_equal [3,2,1], partition.isr.map(&:id)
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
2
+
3
+ require 'kazoo'
4
+ require 'minitest/autorun'
5
+ require 'mocha/mini_test'
6
+ require 'pp'
7
+
8
+ module MockCluster
9
+ def mock_cluster
10
+ cluster = Kazoo::Cluster.new('example.com:2181')
11
+ cluster.stubs(:zk).returns(mock)
12
+
13
+ cluster.stubs(:brokers).returns(
14
+ 1 => Kazoo::Broker.new(cluster, 1, 'example.com', 9091),
15
+ 2 => Kazoo::Broker.new(cluster, 2, 'example.com', 9092),
16
+ 3 => Kazoo::Broker.new(cluster, 3, 'example.com', 9093),
17
+ )
18
+
19
+ cluster.stubs(:topics).returns(
20
+ 'test.1' => topic_1 = Kazoo::Topic.new(cluster, 'test.1'),
21
+ 'test.4' => topic_4 = Kazoo::Topic.new(cluster, 'test.4')
22
+ )
23
+
24
+ topic_1.stubs(:partitions).returns([
25
+ Kazoo::Partition.new(topic_1, 0, replicas: [cluster.brokers[1], cluster.brokers[2]]),
26
+ ])
27
+
28
+ topic_4.stubs(:partitions).returns([
29
+ Kazoo::Partition.new(topic_4, 0, replicas: [cluster.brokers[2], cluster.brokers[1]]),
30
+ Kazoo::Partition.new(topic_4, 1, replicas: [cluster.brokers[2], cluster.brokers[3]]),
31
+ Kazoo::Partition.new(topic_4, 2, replicas: [cluster.brokers[1], cluster.brokers[3]]),
32
+ Kazoo::Partition.new(topic_4, 3, replicas: [cluster.brokers[3], cluster.brokers[2]]),
33
+ ])
34
+
35
+ topic_1.partitions[0].stubs(:isr).returns([cluster.brokers[1], cluster.brokers[2]])
36
+ topic_4.partitions[0].stubs(:isr).returns([cluster.brokers[2], cluster.brokers[1]])
37
+ topic_4.partitions[1].stubs(:isr).returns([cluster.brokers[2], cluster.brokers[3]])
38
+ topic_4.partitions[2].stubs(:isr).returns([cluster.brokers[1], cluster.brokers[3]])
39
+ topic_4.partitions[3].stubs(:isr).returns([cluster.brokers[3], cluster.brokers[2]])
40
+
41
+ topic_1.partitions[0].stubs(:leader).returns(cluster.brokers[1])
42
+ topic_4.partitions[0].stubs(:leader).returns(cluster.brokers[2])
43
+ topic_4.partitions[1].stubs(:leader).returns(cluster.brokers[2])
44
+ topic_4.partitions[2].stubs(:leader).returns(cluster.brokers[1])
45
+ topic_4.partitions[3].stubs(:leader).returns(cluster.brokers[3])
46
+
47
+ return cluster
48
+ end
49
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class TopicTest < Minitest::Test
4
+ include MockCluster
5
+
6
+ def setup
7
+ @cluster = mock_cluster
8
+ end
9
+
10
+ def test_from_json
11
+ json_payload = '{"version":1,"partitions":{"2":[1,2,3],"1":[3,1,2],"3":[2,3,1],"0":[3,2,1]}}'
12
+ topic = Kazoo::Topic.from_json(@cluster, 'test.4', JSON.parse(json_payload))
13
+
14
+ assert_equal 4, topic.partitions.length
15
+ assert_equal [3,2,1], topic.partitions[0].replicas.map(&:id)
16
+ assert_equal [3,1,2], topic.partitions[1].replicas.map(&:id)
17
+ assert_equal [1,2,3], topic.partitions[2].replicas.map(&:id)
18
+ assert_equal [2,3,1], topic.partitions[3].replicas.map(&:id)
19
+ end
20
+
21
+ def test_replication_factor
22
+ json_payload = '{"version":1,"partitions":{"2":[1,2,3],"1":[3,1,2],"3":[2,3,1],"0":[3,2,1]}}'
23
+ topic = Kazoo::Topic.from_json(@cluster, 'test.4', JSON.parse(json_payload))
24
+ assert_equal 3, topic.replication_factor
25
+
26
+ json_payload = '{"version":1,"partitions":{"2":[2,3],"1":[2],"3":[2,3,1],"0":[3,2,1]}}'
27
+ topic = Kazoo::Topic.from_json(@cluster, 'test.4', JSON.parse(json_payload))
28
+ assert_equal 1, topic.replication_factor
29
+ end
30
+
31
+ def tets_topic_under_replicated?
32
+ refute @cluster.topics['test.1'].under_replicated?
33
+ refute @cluster.topics['test.1'].partitions[0].under_replicated?
34
+
35
+ @cluster.topics['test.1'].partitions[0].expects(:isr).returns([@cluster.brokers[1]])
36
+
37
+ assert @cluster.topics['test.1'].partitions[0].under_replicated?
38
+ assert @cluster.topics['test.1'].under_replicated?
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kazoo-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Willem van Bergen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.19.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.19.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: zookeeper
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mocha
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '5'
97
+ description: Library to access Kafka metadata in Zookeeper
98
+ email:
99
+ - willem@vanbergen.org
100
+ executables:
101
+ - kazoo
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - .gitignore
106
+ - .travis.yml
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - bin/kazoo
112
+ - kazoo-ruby.gemspec
113
+ - lib/kazoo.rb
114
+ - lib/kazoo/broker.rb
115
+ - lib/kazoo/cli.rb
116
+ - lib/kazoo/cluster.rb
117
+ - lib/kazoo/partition.rb
118
+ - lib/kazoo/topic.rb
119
+ - lib/kazoo/version.rb
120
+ - test/broker_test.rb
121
+ - test/cluster_test.rb
122
+ - test/partition_test.rb
123
+ - test/test_helper.rb
124
+ - test/topic_test.rb
125
+ homepage: https://github.com/wvanbergen/kazoo
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.0.14
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Library to access Kafka metadata in Zookeeper
149
+ test_files:
150
+ - test/broker_test.rb
151
+ - test/cluster_test.rb
152
+ - test/partition_test.rb
153
+ - test/test_helper.rb
154
+ - test/topic_test.rb