druid_config 0.2.0 → 0.3.0
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 +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
|