druid_config 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/lib/druid_config.rb +6 -0
- data/lib/druid_config/cluster.rb +19 -1
- data/lib/druid_config/entities/data_source.rb +63 -1
- data/lib/druid_config/entities/node.rb +2 -1
- data/lib/druid_config/entities/rule.rb +164 -0
- data/lib/druid_config/entities/rule_collection.rb +65 -0
- data/lib/druid_config/entities/tier.rb +2 -1
- data/lib/druid_config/entities/worker.rb +2 -1
- data/lib/druid_config/validators/rule_validator.rb +10 -0
- data/lib/druid_config/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34faeb23be9dfd54181862f172bb5db8b794f489
|
4
|
+
data.tar.gz: 58c09b7c65c33be02e1b2cf0f9ca8eaa3754666f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64062d2ad8ec0abc2487a003feb746d175aaf6cc8b47b703fbc1544e1a59bd1089209c6d61d164678cbe469884ebefff97f0114cec7884bc63daea73b44df45f
|
7
|
+
data.tar.gz: 7149d8613c814c63ae21ccae898a0205dcbe479a5b0973f757d462a4d8b4d47a5ebefff5fd1cda6527b73e61222c68ff221dc893523735ae33e579617422b129
|
data/README.md
CHANGED
@@ -44,6 +44,8 @@ Call methods defined in `DruidConfig::Cluster` to access to the data. To get mor
|
|
44
44
|
* `realtimes`: realtime nodes
|
45
45
|
* `workers`: worker nodes
|
46
46
|
* `physical_workers`: array of URIs of worker nodes
|
47
|
+
* `running_tasks`, `pending_tasks`, `waiting_tasks`, `complete_tasks`: tasks in the cluster based in their status
|
48
|
+
* `task`: load a task based on an identifier
|
47
49
|
* `services`: Hash with physical nodes and the services they are running
|
48
50
|
|
49
51
|
## Entities
|
@@ -55,6 +57,7 @@ Some methods return an instance of an `Entity` class. These entities provide mul
|
|
55
57
|
* [Tier](https://github.com/redBorder/druid_config/blob/master/lib/druid_config/entities/tier.rb)
|
56
58
|
* [Node](https://github.com/redBorder/druid_config/blob/master/lib/druid_config/entities/node.rb)
|
57
59
|
* [Worker](https://github.com/redBorder/druid_config/blob/master/lib/druid_config/entities/worker.rb)
|
60
|
+
* [Task](https://github.com/redBorder/druid_config/blob/master/lib/druid_config/entities/task.rb)
|
58
61
|
|
59
62
|
## Exceptions
|
60
63
|
|
data/lib/druid_config.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# Global library
|
2
2
|
require 'httparty'
|
3
|
+
require 'iso8601'
|
4
|
+
require 'delegate'
|
5
|
+
require 'json'
|
6
|
+
require 'net/http'
|
3
7
|
|
4
8
|
# Classes
|
5
9
|
require 'druid_config/zk'
|
6
10
|
require 'druid_config/version'
|
7
11
|
require 'druid_config/util'
|
8
12
|
require 'druid_config/entities/segment'
|
13
|
+
require 'druid_config/entities/rule'
|
14
|
+
require 'druid_config/entities/rule_collection'
|
9
15
|
require 'druid_config/entities/task'
|
10
16
|
require 'druid_config/entities/worker'
|
11
17
|
require 'druid_config/entities/node'
|
data/lib/druid_config/cluster.rb
CHANGED
@@ -158,6 +158,17 @@ module DruidConfig
|
|
158
158
|
datasources.select { |el| el.name == datasource }
|
159
159
|
end
|
160
160
|
|
161
|
+
#
|
162
|
+
# Return default datasource. This datasource hasn't got metadata
|
163
|
+
# associated. It's only used to read and apply default rules.
|
164
|
+
#
|
165
|
+
def default_datasource
|
166
|
+
DruidConfig::Entities::DataSource.new(
|
167
|
+
{ 'name' => DruidConfig::Entities::DataSource::DEFAULT_DATASOURCE,
|
168
|
+
'properties' => {} },
|
169
|
+
{})
|
170
|
+
end
|
171
|
+
|
161
172
|
# Rules
|
162
173
|
# -----------------
|
163
174
|
|
@@ -165,9 +176,16 @@ module DruidConfig
|
|
165
176
|
# Return the rules applied to a cluster
|
166
177
|
#
|
167
178
|
def rules
|
179
|
+
rules = DruidConfig::Entities::RuleCollection.new
|
168
180
|
secure_query do
|
169
|
-
self.class.get('/rules')
|
181
|
+
self.class.get('/rules').each do |datasource, ds_rules|
|
182
|
+
ds_rules.each do |rule|
|
183
|
+
rules << DruidConfig::Entities::Rule.parse(rule, datasource)
|
184
|
+
end
|
185
|
+
end
|
170
186
|
end
|
187
|
+
# Return initialized rules
|
188
|
+
rules
|
171
189
|
end
|
172
190
|
|
173
191
|
# Tiers
|
@@ -12,6 +12,9 @@ module DruidConfig
|
|
12
12
|
|
13
13
|
attr_reader :name, :properties, :load_status
|
14
14
|
|
15
|
+
# Name of default datasource
|
16
|
+
DEFAULT_DATASOURCE = '_default'.freeze
|
17
|
+
|
15
18
|
#
|
16
19
|
# Initialize a DataSource
|
17
20
|
#
|
@@ -75,17 +78,76 @@ module DruidConfig
|
|
75
78
|
# Rules
|
76
79
|
# -----------------
|
77
80
|
def rules(params = '')
|
81
|
+
return @rules if @rules
|
82
|
+
@rules = DruidConfig::Entities::RuleCollection.new
|
78
83
|
secure_query do
|
79
|
-
self.class.get("/rules/#{@name}?#{params}")
|
84
|
+
self.class.get("/rules/#{@name}?#{params}").each do |rule|
|
85
|
+
@rules << DruidConfig::Entities::Rule.parse(rule)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
@rules
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Apply given rules to the datasource
|
93
|
+
#
|
94
|
+
# == Paremeters:
|
95
|
+
# rules::
|
96
|
+
# RuleCollection of rules
|
97
|
+
#
|
98
|
+
# == Returns:
|
99
|
+
# Boolean indicating the status of the request
|
100
|
+
#
|
101
|
+
def update_rules(new_rules)
|
102
|
+
if post_rules(new_rules)
|
103
|
+
@rules = new_rules
|
104
|
+
true
|
105
|
+
else
|
106
|
+
false
|
80
107
|
end
|
81
108
|
end
|
82
109
|
|
110
|
+
#
|
111
|
+
# Save current rules
|
112
|
+
#
|
113
|
+
# == Returns:
|
114
|
+
# Boolean indicating the status of the request
|
115
|
+
#
|
116
|
+
def save_rules
|
117
|
+
post_rules(rules)
|
118
|
+
end
|
119
|
+
|
83
120
|
def history_rules(interval)
|
84
121
|
secure_query do
|
85
122
|
self.class.get("/rules/#{@name}/history"\
|
86
123
|
"?interval=#{interval}")
|
87
124
|
end
|
88
125
|
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
#
|
130
|
+
# Save rules of this data source
|
131
|
+
#
|
132
|
+
# == Paremeters:
|
133
|
+
# rules::
|
134
|
+
# RuleCollection of rules
|
135
|
+
#
|
136
|
+
# == Returns:
|
137
|
+
# Boolean indicating the status of the request
|
138
|
+
#
|
139
|
+
def post_rules(new_rules)
|
140
|
+
fail(ArgumentError, 'Rules must be a RuleCollection instance') unless
|
141
|
+
new_rules.is_a?(RuleCollection)
|
142
|
+
uri = URI("#{self.class.base_uri}/rules/#{name}")
|
143
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
144
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
145
|
+
request['Content-Type'] = 'application/json'
|
146
|
+
request.body = new_rules.map(&:to_h).to_json
|
147
|
+
response = http.request(request)
|
148
|
+
# Check statys
|
149
|
+
response.code.to_i == 200
|
150
|
+
end
|
89
151
|
end
|
90
152
|
end
|
91
153
|
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module DruidConfig
|
2
|
+
module Entities
|
3
|
+
#
|
4
|
+
# Rule class
|
5
|
+
#
|
6
|
+
class Rule
|
7
|
+
# Variables
|
8
|
+
attr_reader :datasource, :type, :time_type, :replicants, :period,
|
9
|
+
:interval
|
10
|
+
|
11
|
+
# Identifier for type
|
12
|
+
FOREVER_DRUID_STRING = 'Forever'
|
13
|
+
INTERVAL_DRUID_STRING = 'ByInterval'
|
14
|
+
PERIOD_DRUID_STRING = 'ByPeriod'
|
15
|
+
|
16
|
+
#
|
17
|
+
# Parse data from a Druid API response an initialize an object of
|
18
|
+
# Rule class
|
19
|
+
#
|
20
|
+
# == Parameters:
|
21
|
+
# datasource::
|
22
|
+
# String with the name of the datsource
|
23
|
+
# data::
|
24
|
+
# Hash provided by the API
|
25
|
+
#
|
26
|
+
# == Returns:
|
27
|
+
# Rule instance
|
28
|
+
#
|
29
|
+
def self.parse(data, datasource = nil)
|
30
|
+
type, time_type = detect_type(data['type'])
|
31
|
+
options = { replicants: data['tieredReplicants'] }
|
32
|
+
options.merge!(datasource: datasource) if datasource
|
33
|
+
if time_type == :period
|
34
|
+
options.merge!(period: data['period'])
|
35
|
+
elsif time_type == :interval
|
36
|
+
options.merge!(interval: data['interval'])
|
37
|
+
end
|
38
|
+
# Instance the class
|
39
|
+
new(type, time_type, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Initialize a Rule object. This constructor accepts a Hash with
|
44
|
+
# format defined in:
|
45
|
+
# http://druid.io/docs/latest/operations/rule-configuration.html
|
46
|
+
#
|
47
|
+
# == Parameters:
|
48
|
+
# datasource::
|
49
|
+
# String with the name of the data source
|
50
|
+
# type::
|
51
|
+
# Type of the rule, it can be :drop or :load
|
52
|
+
# time_type::
|
53
|
+
# Time reference. It can be :forever, :period or :interval
|
54
|
+
# options::
|
55
|
+
# Hash with extra data to the rules.
|
56
|
+
# - replicants: Hash with format
|
57
|
+
# { 'tier' => NumberOfReplicants, 'tier' => ... }
|
58
|
+
# - period: String with a period in ISO8601 format.
|
59
|
+
# Only available when type is :period.
|
60
|
+
# - interval: String with a interval in ISO8601 format.
|
61
|
+
# Only available when type is :interval.
|
62
|
+
# - datasource: Name of the datasource
|
63
|
+
#
|
64
|
+
def initialize(type, time_type, options = {})
|
65
|
+
@type = type
|
66
|
+
@time_type = time_type
|
67
|
+
@datasource = options[:datasource]
|
68
|
+
@replicants = options[:replicants]
|
69
|
+
if period?
|
70
|
+
@period = ISO8601::Duration.new(options[:period])
|
71
|
+
elsif interval?
|
72
|
+
# TODO: https://github.com/arnau/ISO8601/issues/15
|
73
|
+
@interval = options[:interval]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Functions to check the rule type
|
79
|
+
#
|
80
|
+
%w(drop load).each do |rule|
|
81
|
+
define_method("#{rule}?") do
|
82
|
+
@type == rule.to_sym
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Functions to check how rule time is defined
|
88
|
+
#
|
89
|
+
%w(interval forever period).each do |time|
|
90
|
+
define_method("#{time}?") do
|
91
|
+
@time_type == time.to_sym
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Return the rule as Hash format
|
97
|
+
#
|
98
|
+
# == Returns:
|
99
|
+
# Hash
|
100
|
+
#
|
101
|
+
def to_h
|
102
|
+
base = { type: type_to_druid }
|
103
|
+
base.merge!(tieredReplicants: @replicants) if @replicants
|
104
|
+
if period?
|
105
|
+
base.merge(period: @period.to_s)
|
106
|
+
elsif interval?
|
107
|
+
base.merge(interval: @interval.to_s)
|
108
|
+
else
|
109
|
+
base
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Return the rule as valid JSON for Druid
|
115
|
+
#
|
116
|
+
# == Returns:
|
117
|
+
# JSON String
|
118
|
+
#
|
119
|
+
def to_json
|
120
|
+
to_h.to_json
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Detect the type of the rule based on 'type' field. This method will
|
125
|
+
# detect if is a drop/load rule and how it defines time.
|
126
|
+
#
|
127
|
+
# == Parameters:
|
128
|
+
# type_to_parse::
|
129
|
+
# String with the content of type field
|
130
|
+
#
|
131
|
+
def self.detect_type(type_to_parse)
|
132
|
+
type = type_to_parse.starts_with?('drop') ? :drop : :load
|
133
|
+
time_type = case type_to_parse.gsub(type.to_s, '')
|
134
|
+
when INTERVAL_DRUID_STRING
|
135
|
+
:interval
|
136
|
+
when FOREVER_DRUID_STRING
|
137
|
+
:forever
|
138
|
+
when PERIOD_DRUID_STRING
|
139
|
+
:period
|
140
|
+
end
|
141
|
+
[type, time_type]
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
#
|
147
|
+
# Convert the type to an String Druid can identify
|
148
|
+
#
|
149
|
+
# == Returns:
|
150
|
+
# String with the type of the rule
|
151
|
+
#
|
152
|
+
def type_to_druid
|
153
|
+
time = if period?
|
154
|
+
PERIOD_DRUID_STRING
|
155
|
+
elsif interval?
|
156
|
+
INTERVAL_DRUID_STRING
|
157
|
+
else
|
158
|
+
FOREVER_DRUID_STRING
|
159
|
+
end
|
160
|
+
"#{@type}#{time}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module DruidConfig
|
2
|
+
module Entities
|
3
|
+
#
|
4
|
+
# Rule set of a data source
|
5
|
+
#
|
6
|
+
class RuleCollection < Array
|
7
|
+
#
|
8
|
+
# Check the consistency of the rules. For example, if you define the
|
9
|
+
# following rules:
|
10
|
+
#
|
11
|
+
# 1 - LOAD ByPeriod PT12M replicants -> { _default_tier => 1 }
|
12
|
+
# 2 - LOAD ByPeriod PT3M replicants -> { _default_tier => 1 }
|
13
|
+
#
|
14
|
+
# The second rule never be applied.
|
15
|
+
#
|
16
|
+
# This method don't raise any exception and your Druid installation will
|
17
|
+
# work unless there are an inconsistence. The purpose of this method is
|
18
|
+
# to advise you could be wrong about rules
|
19
|
+
#
|
20
|
+
# == Returns:
|
21
|
+
# A boolean. True when the rules are consistent.
|
22
|
+
#
|
23
|
+
def consistent?
|
24
|
+
# TODO: implement this method
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Return the collection of rules as a valid JSON for Druid
|
30
|
+
#
|
31
|
+
# == Parameters:
|
32
|
+
# include_datasources::
|
33
|
+
# True if you want to include the name of the datasources as keys
|
34
|
+
# (False by default)
|
35
|
+
#
|
36
|
+
# == Returns:
|
37
|
+
# JSON String
|
38
|
+
#
|
39
|
+
def to_json(include_datasources = false)
|
40
|
+
return to_json_with_datasources if include_datasources
|
41
|
+
map(&:to_h).to_json
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
#
|
47
|
+
# Return a JSON string with the datasources and the rules associated to
|
48
|
+
# them
|
49
|
+
#
|
50
|
+
# == Returns:
|
51
|
+
# JSON string with format:
|
52
|
+
# { 'datasource' => [ { "type" => ... }, ...], 'datasource2' => [...] }
|
53
|
+
#
|
54
|
+
def to_json_with_datasources
|
55
|
+
rules_with_ds = {}
|
56
|
+
map do |rule|
|
57
|
+
rules_with_ds[rule.datasource] ||= []
|
58
|
+
rules_with_ds[rule.datasource] << rule.to_h
|
59
|
+
end
|
60
|
+
rules_with_ds.to_json
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/druid_config/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: druid_config
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Angel M Miguel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zk
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.8.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: iso8601
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.8.7
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.8.7
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -151,11 +165,13 @@ files:
|
|
151
165
|
- lib/druid_config/entities/data_source.rb
|
152
166
|
- lib/druid_config/entities/node.rb
|
153
167
|
- lib/druid_config/entities/rule.rb
|
168
|
+
- lib/druid_config/entities/rule_collection.rb
|
154
169
|
- lib/druid_config/entities/segment.rb
|
155
170
|
- lib/druid_config/entities/task.rb
|
156
171
|
- lib/druid_config/entities/tier.rb
|
157
172
|
- lib/druid_config/entities/worker.rb
|
158
173
|
- lib/druid_config/util.rb
|
174
|
+
- lib/druid_config/validators/rule_validator.rb
|
159
175
|
- lib/druid_config/version.rb
|
160
176
|
- lib/druid_config/zk.rb
|
161
177
|
- spec/cluster_spec.rb
|