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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a03eb8b4f7df4223fe098516a23443beac52be00
4
- data.tar.gz: 230450c3d105e2327c681f1bc1beb225926361be
3
+ metadata.gz: 34faeb23be9dfd54181862f172bb5db8b794f489
4
+ data.tar.gz: 58c09b7c65c33be02e1b2cf0f9ca8eaa3754666f
5
5
  SHA512:
6
- metadata.gz: 151efb283126cbf585549a0e1d0a7f766f48c51e3f21e18ab373c34d59a247c64fbcc1188157ccaadb7b5940ccdcc480c24b87f33a2f0e890e019be817b9bcab
7
- data.tar.gz: db35a7cc044f47bb4de83ea1467e35186389471485f71f48e3f4363087dc8f9326150a0df93a7844d901b804bf38348d51ab2cfb452a2aa708240cbd08a5ba76
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'
@@ -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
@@ -60,7 +60,8 @@ module DruidConfig
60
60
  # Calculate free space
61
61
  #
62
62
  def free
63
- max_size - size
63
+ return @free if @free
64
+ @free = (max_size - size) > 0 ? (max_size - size) : 0
64
65
  end
65
66
 
66
67
  #
@@ -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
+
@@ -25,7 +25,8 @@ module DruidConfig
25
25
  end
26
26
 
27
27
  def free
28
- @free ||= (max_size - size)
28
+ return @free if @free
29
+ @free = (max_size - size) > 0 ? (max_size - size) : 0
29
30
  end
30
31
 
31
32
  def used_percent
@@ -37,7 +37,8 @@ module DruidConfig
37
37
  # Return free capacity
38
38
  #
39
39
  def free
40
- @free ||= (capacity - capacity_used)
40
+ return @free if @free
41
+ @free = (capacity - capacity_used) > 0 ? (capacity - capacity_used) : 0
41
42
  end
42
43
 
43
44
  #
@@ -0,0 +1,10 @@
1
+ module DruidConfig
2
+ module Validators
3
+ #
4
+ # Validator of rule class.
5
+ #
6
+ class RuleValidator
7
+
8
+ end
9
+ end
10
+ end
@@ -4,7 +4,7 @@
4
4
  module DruidConfig
5
5
  module Version
6
6
  # Version of the gem
7
- VERSION = '0.2.0'
7
+ VERSION = '0.3.0'
8
8
 
9
9
  # Base URI foor coordinator queries
10
10
  API_VERSION = 'v1'
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.2.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-10-30 00:00:00.000000000 Z
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