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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/MAINTAINERS.md +7 -6
- data/example/counter.conf +18 -0
- data/example/secondary_file.conf +3 -2
- data/lib/fluent/counter.rb +23 -0
- data/lib/fluent/counter/base_socket.rb +46 -0
- data/lib/fluent/counter/client.rb +288 -0
- data/lib/fluent/counter/error.rb +65 -0
- data/lib/fluent/counter/mutex_hash.rb +163 -0
- data/lib/fluent/counter/server.rb +273 -0
- data/lib/fluent/counter/store.rb +205 -0
- data/lib/fluent/counter/validator.rb +145 -0
- data/lib/fluent/env.rb +1 -0
- data/lib/fluent/log.rb +7 -0
- data/lib/fluent/plugin/filter_grep.rb +20 -24
- data/lib/fluent/plugin/output.rb +50 -1
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/retry_state.rb +15 -7
- data/lib/fluent/plugin_helper/server.rb +3 -0
- data/lib/fluent/supervisor.rb +30 -5
- data/lib/fluent/system_config.rb +26 -2
- data/lib/fluent/version.rb +1 -1
- data/test/counter/test_client.rb +549 -0
- data/test/counter/test_error.rb +44 -0
- data/test/counter/test_mutex_hash.rb +179 -0
- data/test/counter/test_server.rb +583 -0
- data/test/counter/test_store.rb +252 -0
- data/test/counter/test_validator.rb +137 -0
- data/test/plugin/test_output_as_buffered_backup.rb +271 -0
- data/test/plugin_helper/test_retry_state.rb +20 -0
- data/test/test_supervisor.rb +20 -0
- metadata +29 -5
data/lib/fluent/plugin_helper.rb
CHANGED
@@ -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
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
data/lib/fluent/supervisor.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/fluent/system_config.rb
CHANGED
@@ -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
|
|
data/lib/fluent/version.rb
CHANGED
@@ -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
|