fluentd 1.2.0.pre1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

@@ -26,6 +26,7 @@ require 'fluent/plugin_helper/inject'
26
26
  require 'fluent/plugin_helper/extract'
27
27
  require 'fluent/plugin_helper/socket'
28
28
  require 'fluent/plugin_helper/server'
29
+ require 'fluent/plugin_helper/counter'
29
30
  require 'fluent/plugin_helper/retry_state'
30
31
  require 'fluent/plugin_helper/record_accessor'
31
32
  require 'fluent/plugin_helper/compat_parameters'
@@ -0,0 +1,51 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'fluent/plugin'
18
+ require 'fluent/counter/client'
19
+
20
+ module Fluent
21
+ module PluginHelper
22
+ module Counter
23
+ def counter_client_create(scope:, loop: Coolio::Loop.new)
24
+ client_conf = system_config.counter_client
25
+ raise Fluent::ConfigError, '<counter_client> is required in <system>' unless client_conf
26
+ counter_client = Fluent::Counter::Client.new(loop, port: client_conf.port, host: client_conf.host, log: log, timeout: client_conf.timeout)
27
+ counter_client.start
28
+ counter_client.establish(scope)
29
+ @_counter_client = counter_client
30
+ counter_client
31
+ end
32
+
33
+ attr_reader :_counter_client
34
+
35
+ def initialize
36
+ super
37
+ @_counter_client = nil
38
+ end
39
+
40
+ def stop
41
+ super
42
+ @_counter_client.stop
43
+ end
44
+
45
+ def terminate
46
+ @_counter_client = nil
47
+ super
48
+ end
49
+ end
50
+ end
51
+ end
@@ -165,18 +165,26 @@ module Fluent
165
165
  end
166
166
 
167
167
  def calc_interval(num)
168
- # make it infinite if calculated "interval" is too big
169
- interval = @constant_factor.to_f * (@backoff_base ** (num - 1))
170
- if interval.finite?
171
- if @max_interval && interval > @max_interval
172
- @max_interval
168
+ interval = raw_interval(num - 1)
169
+ if @max_interval && interval > @max_interval
170
+ @max_interval
171
+ else
172
+ if interval.finite?
173
+ interval
173
174
  else
175
+ # Calculate previous finite value to avoid inf related errors. If this re-computing is heavy, use cache.
176
+ until interval.finite?
177
+ num -= 1
178
+ interval = raw_interval(num - 1)
179
+ end
174
180
  interval
175
181
  end
176
- else
177
- interval
178
182
  end
179
183
  end
184
+
185
+ def raw_interval(num)
186
+ @constant_factor.to_f * (@backoff_base ** (num))
187
+ end
180
188
  end
181
189
 
182
190
  class PeriodicRetry < RetryStateMachine
@@ -717,6 +717,9 @@ module Fluent
717
717
  end
718
718
  rescue IO::WaitReadable, IO::WaitWritable
719
719
  # ignore and return with doing nothing
720
+ rescue OpenSSL::SSL::SSLError => e
721
+ @log.warn "close socket due to unexpected ssl error: #{e}"
722
+ close rescue nil
720
723
  end
721
724
 
722
725
  def on_writable
@@ -17,6 +17,7 @@
17
17
  require 'fileutils'
18
18
 
19
19
  require 'fluent/config'
20
+ require 'fluent/counter'
20
21
  require 'fluent/env'
21
22
  require 'fluent/engine'
22
23
  require 'fluent/error'
@@ -41,6 +42,8 @@ module Fluent
41
42
  module ServerModule
42
43
  def before_run
43
44
  @start_time = Time.now
45
+ @rpc_server = nil
46
+ @counter = nil
44
47
 
45
48
  if config[:rpc_endpoint]
46
49
  @rpc_endpoint = config[:rpc_endpoint]
@@ -54,6 +57,10 @@ module Fluent
54
57
  install_windows_event_handler
55
58
  end
56
59
 
60
+ if counter = config[:counter_server]
61
+ run_counter_server(counter)
62
+ end
63
+
57
64
  socket_manager_path = ServerEngine::SocketManager::Server.generate_path
58
65
  ServerEngine::SocketManager::Server.open(socket_manager_path)
59
66
  ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
@@ -61,6 +68,7 @@ module Fluent
61
68
 
62
69
  def after_run
63
70
  stop_rpc_server if @rpc_endpoint
71
+ stop_counter_server if @counter
64
72
  Fluent::Supervisor.cleanup_resources
65
73
  end
66
74
 
@@ -126,6 +134,18 @@ module Fluent
126
134
  @rpc_server.shutdown
127
135
  end
128
136
 
137
+ def run_counter_server(counter_conf)
138
+ @counter = Fluent::Counter::Server.new(
139
+ counter_conf.scope,
140
+ {host: counter_conf.bind, port: counter_conf.port, log: $log, path: counter_conf.backup_path}
141
+ )
142
+ @counter.start
143
+ end
144
+
145
+ def stop_counter_server
146
+ @counter.stop
147
+ end
148
+
129
149
  def install_supervisor_signal_handlers
130
150
  trap :HUP do
131
151
  $log.debug "fluentd supervisor process get SIGHUP"
@@ -160,7 +180,11 @@ module Fluent
160
180
 
161
181
  def supervisor_sigusr1_handler
162
182
  if log = config[:logger_initializer]
163
- log.reopen!
183
+ # Creating new thread due to mutex can't lock
184
+ # in main thread during trap context
185
+ Thread.new {
186
+ log.reopen!
187
+ }.run
164
188
  end
165
189
 
166
190
  if config[:worker_pid]
@@ -249,6 +273,7 @@ module Fluent
249
273
  log_rotate_size = params['log_rotate_size']
250
274
  rpc_endpoint = system_config.rpc_endpoint
251
275
  enable_get_dump = system_config.enable_get_dump
276
+ counter_server = system_config.counter_server
252
277
 
253
278
  log_opts = {suppress_repeated_stacktrace: suppress_repeated_stacktrace}
254
279
  logger_initializer = Supervisor::LoggerInitializer.new(
@@ -290,6 +315,7 @@ module Fluent
290
315
  suppress_repeated_stacktrace: suppress_repeated_stacktrace,
291
316
  daemonize: daemonize,
292
317
  rpc_endpoint: rpc_endpoint,
318
+ counter_server: counter_server,
293
319
  enable_get_dump: enable_get_dump,
294
320
  windows_daemon_cmdline: [ServerEngine.ruby_bin_path,
295
321
  File.join(File.dirname(__FILE__), 'daemon.rb'),
@@ -663,14 +689,13 @@ module Fluent
663
689
  end
664
690
 
665
691
  def flush_buffer
666
- $log.debug "fluentd main process get SIGUSR1"
667
- $log.info "force flushing buffered events"
668
- @log.reopen!
669
-
670
692
  # Creating new thread due to mutex can't lock
671
693
  # in main thread during trap context
672
694
  Thread.new {
673
695
  begin
696
+ $log.debug "fluentd main process get SIGUSR1"
697
+ $log.info "force flushing buffered events"
698
+ @log.reopen!
674
699
  Fluent::Engine.flush!
675
700
  $log.debug "flushing thread: flushed"
676
701
  rescue Exception => e
@@ -26,7 +26,7 @@ module Fluent
26
26
  :suppress_repeated_stacktrace, :emit_error_log_interval, :suppress_config_dump,
27
27
  :log_event_verbose,
28
28
  :without_source, :rpc_endpoint, :enable_get_dump, :process_name,
29
- :file_permission, :dir_permission,
29
+ :file_permission, :dir_permission, :counter_server, :counter_client,
30
30
  ]
31
31
 
32
32
  config_param :workers, :integer, default: 1
@@ -51,6 +51,28 @@ module Fluent
51
51
  config_param :time_format, :string, default: '%Y-%m-%d %H:%M:%S %z'
52
52
  end
53
53
 
54
+ config_section :counter_server, multi: false do
55
+ desc 'scope name of counter server'
56
+ config_param :scope, :string
57
+
58
+ desc 'the port of counter server to listen to'
59
+ config_param :port, :integer, default: nil
60
+ desc 'the bind address of counter server to listen to'
61
+ config_param :bind, :string, default: nil
62
+
63
+ desc 'backup file path of counter values'
64
+ config_param :backup_path, :string
65
+ end
66
+
67
+ config_section :counter_client, multi: false do
68
+ desc 'the port of counter server'
69
+ config_param :port, :integer, default: nil
70
+ desc 'the IP address or hostname of counter server'
71
+ config_param :host, :string
72
+ desc 'the timeout of each operation'
73
+ config_param :timeout, :time, default: nil
74
+ end
75
+
54
76
  def self.create(conf)
55
77
  systems = conf.elements(name: 'system')
56
78
  return SystemConfig.new if systems.empty?
@@ -98,7 +120,7 @@ module Fluent
98
120
  supervisor.instance_eval {
99
121
  SYSTEM_CONFIG_PARAMETERS.each do |param|
100
122
  case param
101
- when :rpc_endpoint, :enable_get_dump, :process_name, :file_permission, :dir_permission
123
+ when :rpc_endpoint, :enable_get_dump, :process_name, :file_permission, :dir_permission, :counter_server, :counter_client
102
124
  next # doesn't exist in command line options
103
125
  when :emit_error_log_interval
104
126
  system.emit_error_log_interval = @suppress_interval if @suppress_interval
@@ -136,6 +158,8 @@ module Fluent
136
158
  instance_variable_set("@#{param}", param_value)
137
159
  end
138
160
  end
161
+ #@counter_server = system.counter_server unless system.counter_server.nil?
162
+ #@counter_client = system.counter_client unless system.counter_client.nil?
139
163
  }
140
164
  end
141
165
 
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.2.0.pre1'
19
+ VERSION = '1.2.0'
20
20
 
21
21
  end
@@ -0,0 +1,549 @@
1
+ require_relative '../helper'
2
+ require 'fluent/counter/client'
3
+ require 'fluent/counter/store'
4
+ require 'fluent/counter/server'
5
+ require 'flexmock/test_unit'
6
+ require 'timecop'
7
+
8
+ class CounterClientTest < ::Test::Unit::TestCase
9
+ TEST_ADDR = '127.0.0.1'
10
+ TEST_PORT = '8277'
11
+
12
+ setup do
13
+ # timecop isn't compatible with EventTime
14
+ t = Time.parse('2016-09-22 16:59:59 +0900')
15
+ Timecop.freeze(t)
16
+ @now = Fluent::EventTime.now
17
+
18
+ @options = {
19
+ addr: TEST_ADDR,
20
+ port: TEST_PORT,
21
+ log: $log,
22
+ }
23
+
24
+ @server_name = 'server1'
25
+ @scope = "worker1\tplugin1"
26
+ @loop = Coolio::Loop.new
27
+ @server = Fluent::Counter::Server.new(@server_name, @options).start
28
+ @client = Fluent::Counter::Client.new(@loop, @options).start
29
+ end
30
+
31
+ teardown do
32
+ Timecop.return
33
+ @server.stop
34
+ @client.stop
35
+ end
36
+
37
+ test 'Callable API' do
38
+ [:establish, :init, :delete, :inc, :reset, :get].each do |m|
39
+ assert_true @client.respond_to?(m)
40
+ end
41
+ end
42
+
43
+ sub_test_case 'on_message' do
44
+ setup do
45
+ @future = flexmock('future')
46
+ @client.instance_variable_set(:@responses, { 1 => @future })
47
+ end
48
+
49
+ test 'call a set method to a corresponding object' do
50
+ @future.should_receive(:set).once.with(Hash)
51
+ @client.send(:on_message, { 'id' => 1 })
52
+ end
53
+
54
+ test "output a warning log when passed id doesn't exist" do
55
+ data = { 'id' => 2 }
56
+ mock($log).warn("Receiving missing id data: #{data}")
57
+ @client.send(:on_message, data)
58
+ end
59
+ end
60
+
61
+ def extract_value_from_server(server, scope, name)
62
+ store = server.instance_variable_get(:@store).instance_variable_get(:@storage).instance_variable_get(:@store)
63
+ key = Fluent::Counter::Store.gen_key(scope, name)
64
+ store[key]
65
+ end
66
+
67
+ sub_test_case 'establish' do
68
+ test 'establish a scope' do
69
+ @client.establish(@scope)
70
+ assert_equal "#{@server_name}\t#{@scope}", @client.instance_variable_get(:@scope)
71
+ end
72
+
73
+ data(
74
+ empty: '',
75
+ invalid_string: '_scope',
76
+ invalid_string2: 'Scope'
77
+ )
78
+ test 'raise an error when passed scope is invalid' do |scope|
79
+ assert_raise do
80
+ @client.establish(scope)
81
+ end
82
+ end
83
+ end
84
+
85
+ sub_test_case 'init' do
86
+ setup do
87
+ @client.instance_variable_set(:@scope, @scope)
88
+ end
89
+
90
+ data(
91
+ numeric_type: [
92
+ { name: 'key', reset_interval: 20, type: 'numeric' }, 0
93
+ ],
94
+ float_type: [
95
+ { name: 'key', reset_interval: 20, type: 'float' }, 0.0
96
+ ],
97
+ integer_type: [
98
+ { name: 'key', reset_interval: 20, type: 'integer' }, 0
99
+ ]
100
+ )
101
+ test 'create a value' do |(param, initial_value)|
102
+ assert_nil extract_value_from_server(@server, @scope, param[:name])
103
+
104
+ response = @client.init(param).get
105
+ data = response.data.first
106
+
107
+ assert_nil response.errors
108
+ assert_equal param[:name], data['name']
109
+ assert_equal param[:reset_interval], data['reset_interval']
110
+ assert_equal param[:type], data['type']
111
+ assert_equal initial_value, data['current']
112
+ assert_equal initial_value, data['total']
113
+
114
+ v = extract_value_from_server(@server, @scope, param[:name])
115
+ assert_equal param[:name], v['name']
116
+ assert_equal param[:reset_interval], v['reset_interval']
117
+ assert_equal param[:type], v['type']
118
+ assert_equal initial_value, v['total']
119
+ assert_equal initial_value, v['current']
120
+ end
121
+
122
+ test 'raise an error when @scope is nil' do
123
+ @client.instance_variable_set(:@scope, nil)
124
+ assert_raise 'Call `establish` method to get a `scope` before calling this method' do
125
+ @client.init(name: 'key1', reset_interval: 10).get
126
+ end
127
+ end
128
+
129
+ data(
130
+ already_exist_key: [
131
+ { name: 'key1', reset_interval: 10 },
132
+ { 'code' => 'invalid_params', 'message' => "worker1\tplugin1\tkey1 already exists in counter" }
133
+ ],
134
+ missing_name: [
135
+ { reset_interval: 10 },
136
+ { 'code' => 'invalid_params', 'message' => '`name` is required' },
137
+ ],
138
+ missing_reset_interval: [
139
+ { name: 'key' },
140
+ { 'code' => 'invalid_params', 'message' => '`reset_interval` is required' },
141
+ ],
142
+ invalid_name: [
143
+ { name: '\tkey' },
144
+ { 'code' => 'invalid_params', 'message' => '`name` is the invalid format' }
145
+ ]
146
+ )
147
+ test 'return an error object' do |(param, expected_error)|
148
+ @client.init(:name => 'key1', :reset_interval => 10).get
149
+ response = @client.init(param).get
150
+ errors = response.errors.first
151
+
152
+ assert_empty response.data
153
+ assert_equal expected_error, errors
154
+ end
155
+
156
+ test 'return an existing value when passed key already exists and ignore option is true' do
157
+ res1 = @client.init(name: 'key1', reset_interval: 10).get
158
+ res2 = nil
159
+ assert_nothing_raised do
160
+ res2 = @client.init({ name: 'key1', reset_interval: 10 }, options: { ignore: true }).get
161
+ end
162
+ assert_equal res1.data, res2.data
163
+ end
164
+
165
+ test 'return an error object and data object' do
166
+ param = { name: 'key1', reset_interval: 10 }
167
+ param2 = { name: 'key2', reset_interval: 10 }
168
+ @client.init(param).get
169
+
170
+ response = @client.init([param2, param]).get
171
+ data = response.data.first
172
+ error = response.errors.first
173
+
174
+ assert_equal param2[:name], data['name']
175
+ assert_equal param2[:reset_interval], data['reset_interval']
176
+
177
+ assert_equal 'invalid_params', error['code']
178
+ assert_equal "#{@scope}\t#{param[:name]} already exists in counter", error['message']
179
+ end
180
+
181
+ test 'return a future object when async call' do
182
+ param = { name: 'key', reset_interval: 10 }
183
+ r = @client.init(param)
184
+ assert_true r.is_a?(Fluent::Counter::Future)
185
+ assert_nil r.errors
186
+ end
187
+ end
188
+
189
+ sub_test_case 'delete' do
190
+ setup do
191
+ @client.instance_variable_set(:@scope, @scope)
192
+ @name = 'key'
193
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
194
+
195
+ @init_obj = { name: @name, reset_interval: 20, type: 'numeric' }
196
+ @client.init(@init_obj).get
197
+ end
198
+
199
+ test 'delete a value' do
200
+ assert extract_value_from_server(@server, @scope, @name)
201
+
202
+ response = @client.delete(@name).get
203
+ v = response.data.first
204
+
205
+ assert_nil response.errors
206
+ assert_equal @init_obj[:name], v['name']
207
+ assert_equal @init_obj[:type], v['type']
208
+ assert_equal @init_obj[:reset_interval], v['reset_interval']
209
+
210
+ assert_nil extract_value_from_server(@server, @scope, @name)
211
+ end
212
+
213
+ test 'raise an error when @scope is nil' do
214
+ @client.instance_variable_set(:@scope, nil)
215
+ assert_raise 'Call `establish` method to get a `scope` before calling this method' do
216
+ @client.delete(@name).get
217
+ end
218
+ end
219
+
220
+ data(
221
+ key_not_found: [
222
+ 'key2',
223
+ { 'code' => 'unknown_key', 'message' => "`worker1\tplugin1\tkey2` doesn't exist in counter" }
224
+ ],
225
+ invalid_key: [
226
+ '\tkey',
227
+ { 'code' => 'invalid_params', 'message' => '`key` is the invalid format' }
228
+ ]
229
+ )
230
+ test 'return an error object' do |(param, expected_error)|
231
+ response = @client.delete(param).get
232
+ errors = response.errors.first
233
+
234
+ assert_empty response.data
235
+ assert_equal expected_error, errors
236
+ end
237
+
238
+ test 'return an error object and data object' do
239
+ unknown_name = 'key2'
240
+
241
+ response = @client.delete(@name, unknown_name).get
242
+ data = response.data.first
243
+ error = response.errors.first
244
+
245
+ assert_equal @name, data['name']
246
+ assert_equal @init_obj[:reset_interval], data['reset_interval']
247
+
248
+ assert_equal 'unknown_key', error['code']
249
+ assert_equal "`#{@scope}\t#{unknown_name}` doesn't exist in counter", error['message']
250
+
251
+ assert_nil extract_value_from_server(@server, @scope, @name)
252
+ end
253
+
254
+ test 'return a future object when async call' do
255
+ r = @client.delete(@name)
256
+ assert_true r.is_a?(Fluent::Counter::Future)
257
+ assert_nil r.errors
258
+ end
259
+ end
260
+
261
+ sub_test_case 'inc' do
262
+ setup do
263
+ @client.instance_variable_set(:@scope, @scope)
264
+ @name = 'key'
265
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
266
+
267
+ @init_obj = { name: @name, reset_interval: 20, type: 'numeric' }
268
+ @client.init(@init_obj).get
269
+ end
270
+
271
+ test 'increment a value' do
272
+ v = extract_value_from_server(@server, @scope, @name)
273
+ assert_equal 0, v['total']
274
+ assert_equal 0, v['current']
275
+
276
+ Timecop.travel(1)
277
+ inc_obj = { name: @name, value: 10 }
278
+ @client.inc(inc_obj).get
279
+
280
+ v = extract_value_from_server(@server, @scope, @name)
281
+ assert_equal inc_obj[:value], v['total']
282
+ assert_equal inc_obj[:value], v['current']
283
+ assert_equal (@now + 1), Fluent::EventTime.new(*v['last_modified_at'])
284
+ end
285
+
286
+ test 'create and increment a value when force option is true' do
287
+ name = 'new_key'
288
+ param = { name: name, value: 11, reset_interval: 1 }
289
+
290
+ assert_nil extract_value_from_server(@server, @scope, name)
291
+
292
+ @client.inc(param, options: { force: true }).get
293
+
294
+ v = extract_value_from_server(@server, @scope, name)
295
+ assert v
296
+ assert_equal param[:name], v['name']
297
+ assert_equal 1, v['reset_interval']
298
+ assert_equal param[:value], v['current']
299
+ assert_equal param[:value], v['total']
300
+ end
301
+
302
+ test 'raise an error when @scope is nil' do
303
+ @client.instance_variable_set(:@scope, nil)
304
+ assert_raise 'Call `establish` method to get a `scope` before calling this method' do
305
+ @client.inc(name: 'name', value: 1).get
306
+ end
307
+ end
308
+
309
+ data(
310
+ not_exist_key: [
311
+ { name: 'key2', value: 10 },
312
+ { 'code' => 'unknown_key', 'message' => "`worker1\tplugin1\tkey2` doesn't exist in counter" }
313
+ ],
314
+ missing_name: [
315
+ { value: 10 },
316
+ { 'code' => 'invalid_params', 'message' => '`name` is required' },
317
+ ],
318
+ missing_value: [
319
+ { name: 'key' },
320
+ { 'code' => 'invalid_params', 'message' => '`value` is required' },
321
+ ],
322
+ invalid_name: [
323
+ { name: '\tkey' },
324
+ { 'code' => 'invalid_params', 'message' => '`name` is the invalid format' }
325
+ ]
326
+ )
327
+ test 'return an error object' do |(param, expected_error)|
328
+ response = @client.inc(param).get
329
+ errors = response.errors.first
330
+ assert_empty response.data
331
+ assert_equal expected_error, errors
332
+ end
333
+
334
+ test 'return an error object and data object' do
335
+ parmas = [
336
+ { name: @name, value: 10 },
337
+ { name: 'unknown_key', value: 9 },
338
+ ]
339
+ response = @client.inc(parmas).get
340
+
341
+ data = response.data.first
342
+ error = response.errors.first
343
+
344
+ assert_equal @name, data['name']
345
+ assert_equal 10, data['current']
346
+ assert_equal 10, data['total']
347
+
348
+ assert_equal 'unknown_key', error['code']
349
+ assert_equal "`#{@scope}\tunknown_key` doesn't exist in counter", error['message']
350
+ end
351
+
352
+ test 'return a future object when async call' do
353
+ param = { name: 'key', value: 10 }
354
+ r = @client.inc(param)
355
+ assert_true r.is_a?(Fluent::Counter::Future)
356
+ assert_nil r.errors
357
+ end
358
+ end
359
+
360
+ sub_test_case 'get' do
361
+ setup do
362
+ @client.instance_variable_set(:@scope, @scope)
363
+ @name = 'key'
364
+
365
+ @init_obj = { name: @name, reset_interval: 20, type: 'numeric' }
366
+ @client.init(@init_obj).get
367
+ end
368
+
369
+ test 'get a value' do
370
+ v1 = extract_value_from_server(@server, @scope, @name)
371
+ v2 = @client.get(@name).data.first
372
+
373
+ assert_equal v1['name'], v2['name']
374
+ assert_equal v1['current'], v2['current']
375
+ assert_equal v1['total'], v2['total']
376
+ assert_equal v1['type'], v2['type']
377
+ end
378
+
379
+ test 'raise an error when @scope is nil' do
380
+ @client.instance_variable_set(:@scope, nil)
381
+ assert_raise 'Call `establish` method to get a `scope` before calling this method' do
382
+ @client.get(@name).get
383
+ end
384
+ end
385
+
386
+ data(
387
+ key_not_found: [
388
+ 'key2',
389
+ { 'code' => 'unknown_key', 'message' => "`worker1\tplugin1\tkey2` doesn't exist in counter" }
390
+ ],
391
+ invalid_key: [
392
+ '\tkey',
393
+ { 'code' => 'invalid_params', 'message' => '`key` is the invalid format' }
394
+ ]
395
+ )
396
+ test 'return an error object' do |(param, expected_error)|
397
+ response = @client.get(param).get
398
+ errors = response.errors.first
399
+ assert_empty response.data
400
+ assert_equal expected_error, errors
401
+ end
402
+
403
+ test 'return an error object and data object' do
404
+ unknown_name = 'key2'
405
+
406
+ response = @client.get(@name, unknown_name).get
407
+ data = response.data.first
408
+ error = response.errors.first
409
+
410
+ assert_equal @name, data['name']
411
+ assert_equal @init_obj[:reset_interval], data['reset_interval']
412
+
413
+ assert_equal 'unknown_key', error['code']
414
+ assert_equal "`#{@scope}\t#{unknown_name}` doesn't exist in counter", error['message']
415
+ end
416
+
417
+ test 'return a future object when async call' do
418
+ r = @client.get(@name)
419
+ assert_true r.is_a?(Fluent::Counter::Future)
420
+ assert_nil r.errors
421
+ end
422
+ end
423
+
424
+ sub_test_case 'reset' do
425
+ setup do
426
+ @client.instance_variable_set(:@scope, @scope)
427
+ @name = 'key'
428
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
429
+
430
+ @init_obj = { name: @name, reset_interval: 5, type: 'numeric' }
431
+ @client.init(@init_obj).get
432
+ @inc_obj = { name: @name, value: 10 }
433
+ @client.inc(@inc_obj).get
434
+ end
435
+
436
+ test 'reset a value after `reset_interval` passed' do
437
+ v1 = extract_value_from_server(@server, @scope, @name)
438
+ assert_equal @inc_obj[:value], v1['total']
439
+ assert_equal @inc_obj[:value], v1['current']
440
+ assert_equal @now, Fluent::EventTime.new(*v1['last_reset_at'])
441
+
442
+ travel_sec = 6 # greater than reset_interval
443
+ Timecop.travel(travel_sec)
444
+
445
+ v2 = @client.reset(@name).get
446
+ data = v2.data.first
447
+
448
+ c = data['counter_data']
449
+
450
+ assert_equal travel_sec, data['elapsed_time']
451
+ assert_true data['success']
452
+
453
+ assert_equal @inc_obj[:value], c['current']
454
+ assert_equal @inc_obj[:value], c['total']
455
+ assert_equal @now, c['last_reset_at']
456
+
457
+ v1 = extract_value_from_server(@server, @scope, @name)
458
+ assert_equal 0, v1['current']
459
+ assert_equal @inc_obj[:value], v1['total']
460
+ assert_equal (@now + travel_sec), Fluent::EventTime.new(*v1['last_reset_at'])
461
+ assert_equal (@now + travel_sec), Fluent::EventTime.new(*v1['last_modified_at'])
462
+ end
463
+
464
+ test 'areturn a value object before `reset_interval` passed' do
465
+ v1 = extract_value_from_server(@server, @scope, @name)
466
+ assert_equal @inc_obj[:value], v1['total']
467
+ assert_equal @inc_obj[:value], v1['current']
468
+ assert_equal @now, Fluent::EventTime.new(*v1['last_reset_at'])
469
+
470
+ travel_sec = 4 # less than reset_interval
471
+ Timecop.travel(travel_sec)
472
+
473
+ v2 = @client.reset(@name).get
474
+ data = v2.data.first
475
+
476
+ c = data['counter_data']
477
+
478
+ assert_equal travel_sec, data['elapsed_time']
479
+ assert_equal false, data['success']
480
+
481
+ assert_equal @inc_obj[:value], c['current']
482
+ assert_equal @inc_obj[:value], c['total']
483
+ assert_equal @now, c['last_reset_at']
484
+
485
+ v1 = extract_value_from_server(@server, @scope, @name)
486
+ assert_equal @inc_obj[:value], v1['current']
487
+ assert_equal @inc_obj[:value], v1['total']
488
+ assert_equal @now, Fluent::EventTime.new(*v1['last_reset_at'])
489
+ end
490
+
491
+ test 'raise an error when @scope is nil' do
492
+ @client.instance_variable_set(:@scope, nil)
493
+ assert_raise 'Call `establish` method to get a `scope` before calling this method' do
494
+ @client.reset(@name).get
495
+ end
496
+ end
497
+
498
+ data(
499
+ key_not_found: [
500
+ 'key2',
501
+ { 'code' => 'unknown_key', 'message' => "`worker1\tplugin1\tkey2` doesn't exist in counter" }
502
+ ],
503
+ invalid_key: [
504
+ '\tkey',
505
+ { 'code' => 'invalid_params', 'message' => '`key` is the invalid format' }
506
+ ]
507
+ )
508
+ test 'return an error object' do |(param, expected_error)|
509
+ response = @client.reset(param).get
510
+ errors = response.errors.first
511
+ assert_empty response.data
512
+ assert_equal expected_error, errors
513
+ end
514
+
515
+ test 'return an error object and data object' do
516
+ unknown_name = 'key2'
517
+
518
+ travel_sec = 6 # greater than reset_interval
519
+ Timecop.travel(travel_sec)
520
+
521
+ response = @client.reset(@name, unknown_name).get
522
+ data = response.data.first
523
+ error = response.errors.first
524
+ counter = data['counter_data']
525
+
526
+ assert_true data['success']
527
+ assert_equal travel_sec, data['elapsed_time']
528
+ assert_equal @name, counter['name']
529
+ assert_equal @init_obj[:reset_interval], counter['reset_interval']
530
+ assert_equal @inc_obj[:value], counter['total']
531
+ assert_equal @inc_obj[:value], counter['current']
532
+
533
+ assert_equal 'unknown_key', error['code']
534
+ assert_equal "`#{@scope}\t#{unknown_name}` doesn't exist in counter", error['message']
535
+
536
+ v1 = extract_value_from_server(@server, @scope, @name)
537
+ assert_equal 0, v1['current']
538
+ assert_equal @inc_obj[:value], v1['total']
539
+ assert_equal (@now + travel_sec), Fluent::EventTime.new(*v1['last_reset_at'])
540
+ assert_equal (@now + travel_sec), Fluent::EventTime.new(*v1['last_modified_at'])
541
+ end
542
+
543
+ test 'return a future object when async call' do
544
+ r = @client.reset(@name)
545
+ assert_true r.is_a?(Fluent::Counter::Future)
546
+ assert_nil r.errors
547
+ end
548
+ end
549
+ end