fluentd 0.12.26 → 0.12.27

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ecb5a38a503480766ad28bd9c90d8e650e857a7
4
- data.tar.gz: 9e033240aed3098e153ccf552987a72acfa2ab0b
3
+ metadata.gz: f6f3ea036e68e5a0a46afbe518b05590fec7addf
4
+ data.tar.gz: 1e73ba803fdbceec16d3e7d39f80744b696d271b
5
5
  SHA512:
6
- metadata.gz: 71605065f90941ffc682fe7811f19fe88e2899e312fdf7e0633fb3b552e00e53fc8ba68f41114ce2ca885fae825d457d86b48c0266093f55699a1571d280a4d2
7
- data.tar.gz: c9f4609a941738a2bf2e99a9a123c074a0444c6aa9b8fdc6584e9a9d50492d47b98ad0c57e96e5440a3f94f42cac19f39ba8242604863dbf51d8454e7f588e7c
6
+ metadata.gz: 8ea885c9bbd62870782c04c8da7e3a29e78fa28c2ecc944d57454cc271238dc11b7d0a9de56c77aaa922871151eba2520133399c48826711d6a2b6d3154c4910
7
+ data.tar.gz: 189f245592e68f78d61cda44c7253d7e5ec90a01369adf90e05a463a2a2461e6f26f838635d939b626f317af864d9c13e421db3a3190b6f2d59efd86c9a11567
data/ChangeLog CHANGED
@@ -1,5 +1,29 @@
1
1
  # v0.12
2
2
 
3
+ ## Release 0.12.27 - 2016/08/03
4
+
5
+ ### New features / Enhancement
6
+
7
+ * config: Add simplified syntax for hash and array
8
+ https://github.com/fluent/fluentd/pull/939
9
+ * config: Add include support for Ruby DSL
10
+ https://github.com/fluent/fluentd/pull/950
11
+ * in_http: Add support of X-Forwarded-For header
12
+ https://github.com/fluent/fluentd/pull/1051
13
+ * in_syslog: Add message_length_limit option to receive longer udp syslog messages
14
+ https://github.com/fluent/fluentd/pull/1128
15
+
16
+ ### Bug fixes
17
+
18
+ * test: Fix test for TimeSlicedOutput
19
+ https://github.com/fluent/fluentd/pull/941
20
+ * example: Use `@type` in example confs
21
+ https://github.com/fluent/fluentd/pull/1043
22
+ * Fix regression of fluent-debug command
23
+ https://github.com/fluent/fluentd/pull/1046
24
+ * config: Fix wrong message for unsupported parameter
25
+ https://github.com/fluent/fluentd/pull/1132
26
+
3
27
  ## Release 0.12.26 - 2016/05/30
4
28
 
5
29
  ### Bug fixes
@@ -1,22 +1,22 @@
1
1
  <source>
2
- type dummy
2
+ @type dummy
3
3
  tag dummy
4
4
  </source>
5
5
 
6
6
  <filter **>
7
- type stdout
7
+ @type stdout
8
8
  </filter>
9
9
 
10
10
  <filter **>
11
- type stdout
11
+ @type stdout
12
12
  output_type hash
13
13
  </filter>
14
14
 
15
15
  <filter **>
16
- type stdout
16
+ @type stdout
17
17
  format ltsv
18
18
  </filter>
19
19
 
20
20
  <match **>
21
- type null
21
+ @type null
22
22
  </match>
@@ -1,7 +1,7 @@
1
1
  <source>
2
- type forward
2
+ @type forward
3
3
  </source>
4
4
 
5
5
  <match test>
6
- type stdout
6
+ @type stdout
7
7
  </match>
@@ -1,5 +1,5 @@
1
1
  <source>
2
- type http
2
+ @type http
3
3
  bind 0.0.0.0
4
4
  port 9880
5
5
  body_size_limit 32MB
@@ -10,5 +10,5 @@
10
10
  </source>
11
11
 
12
12
  <match test>
13
- type stdout
13
+ @type stdout
14
14
  </match>
@@ -1,5 +1,5 @@
1
1
  <source>
2
- type syslog
2
+ @type syslog
3
3
  bind 0.0.0.0
4
4
  port 5140
5
5
  tag test
@@ -11,5 +11,5 @@
11
11
  </source>
12
12
 
13
13
  <match test>
14
- type stdout
14
+ @type stdout
15
15
  </match>
@@ -1,5 +1,5 @@
1
1
  <source>
2
- type tail
2
+ @type tail
3
3
  format none
4
4
  path /var/log/fluentd_test.log
5
5
  pos_file /var/log/fluentd_test.pos
@@ -10,5 +10,5 @@
10
10
  </source>
11
11
 
12
12
  <match test>
13
- type stdout
13
+ @type stdout
14
14
  </match>
@@ -1,5 +1,5 @@
1
1
  <source>
2
- type tcp
2
+ @type tcp
3
3
  format none
4
4
  bind 0.0.0.0
5
5
  port 5170
@@ -9,5 +9,5 @@
9
9
  </source>
10
10
 
11
11
  <match test>
12
- type stdout
12
+ @type stdout
13
13
  </match>
@@ -1,5 +1,5 @@
1
1
  <source>
2
- type udp
2
+ @type udp
3
3
  format none
4
4
  bind 0.0.0.0
5
5
  port 5160
@@ -9,5 +9,5 @@
9
9
  </source>
10
10
 
11
11
  <match test>
12
- type stdout
12
+ @type stdout
13
13
  </match>
@@ -1,15 +1,15 @@
1
1
  <source>
2
- type forward
2
+ @type forward
3
3
  </source>
4
4
 
5
5
  <match test>
6
- type copy
6
+ @type copy
7
7
  deep_copy false
8
8
  <store>
9
- type stdout
9
+ @type stdout
10
10
  </store>
11
11
  <store>
12
- type file
12
+ @type file
13
13
  path /var/log/fluentd/out_file_test
14
14
  format json
15
15
  buffer_type memory
@@ -1,9 +1,9 @@
1
1
  <source>
2
- type forward
2
+ @type forward
3
3
  </source>
4
4
 
5
5
  <match test>
6
- type file
6
+ @type file
7
7
  path /var/log/fluentd/out_file_test
8
8
  format json
9
9
  buffer_type memory
@@ -1,10 +1,10 @@
1
1
  <source>
2
- type dummy
2
+ @type dummy
3
3
  tag test
4
4
  </source>
5
5
 
6
6
  <match test>
7
- type forward
7
+ @type forward
8
8
 
9
9
  <server>
10
10
  # first server
@@ -9,7 +9,7 @@
9
9
  # $ echo '{"message":"hello world"}' | fluent-cat foo
10
10
 
11
11
  <source>
12
- type forward
12
+ @type forward
13
13
  port 24224
14
14
  </source>
15
15
 
@@ -21,13 +21,13 @@
21
21
  # - {"messag22":"keep this please"} is filtered out.
22
22
 
23
23
  <filter foo>
24
- type grep
24
+ @type grep
25
25
  regexp1 message keep this
26
26
  </filter>
27
27
 
28
28
  # Matches the events that was kept by the above filter
29
29
  <match foo>
30
- type stdout
30
+ @type stdout
31
31
  </match>
32
32
 
33
33
  # For all events with the tag "bar", add the machine's hostname with
@@ -35,7 +35,7 @@
35
35
  # at 123.4.2.4:24224.
36
36
 
37
37
  <filter bar>
38
- type record_transformer
38
+ @type record_transformer
39
39
  <record>
40
40
  hostname ${hostname}
41
41
  </record>
@@ -44,7 +44,7 @@
44
44
  # By the time it is getting matched here, the event has
45
45
  # the "hostname" field.
46
46
  <match bar>
47
- type forward
47
+ @type forward
48
48
  <server>
49
49
  host 123.4.2.4
50
50
  port 24225
@@ -61,12 +61,12 @@
61
61
  # - {"last_name":"FURUHASHI"} throws an error because it has no field called "name"
62
62
 
63
63
  <filter foo.bar>
64
- type grep
64
+ @type grep
65
65
  exclude1 hello .
66
66
  </filter>
67
67
 
68
68
  <filter foo.bar>
69
- type record_transformer
69
+ @type record_transformer
70
70
  enable_ruby true
71
71
  <record>
72
72
  name ${name.downcase}
@@ -74,5 +74,5 @@
74
74
  </filter>
75
75
 
76
76
  <match foo.bar>
77
- type stdout
77
+ @type stdout
78
78
  </match>
@@ -62,12 +62,13 @@ end
62
62
 
63
63
  require 'fluent/log'
64
64
  require 'fluent/engine'
65
+ require 'fluent/config'
65
66
  require 'fluent/system_config'
66
67
 
67
- include Fluent::SystemConfig::Mixin
68
-
69
68
  $log = Fluent::Log.new(STDERR, Fluent::Log::LEVEL_TRACE)
70
- Fluent::Engine.init(system_config)
69
+ conf = Fluent::Config.parse('', 'fluent-debug.conf', '', true)
70
+ system_conf = Fluent::SystemConfig.create(conf)
71
+ Fluent::Engine.init(system_conf)
71
72
 
72
73
  DRb::DRbObject.class_eval do
73
74
  undef_method :methods
@@ -35,14 +35,19 @@ module Fluent
35
35
  end
36
36
 
37
37
  class Proxy
38
- def initialize(name, arg)
38
+ def initialize(name, arg, include_basepath = Dir.pwd)
39
39
  @element = Element.new(name, arg, self)
40
+ @include_basepath = include_basepath
40
41
  end
41
42
 
42
43
  def element
43
44
  @element
44
45
  end
45
46
 
47
+ def include_basepath
48
+ @include_basepath
49
+ end
50
+
46
51
  def eval(source, source_path)
47
52
  @element.instance_eval(source, source_path)
48
53
  self
@@ -96,6 +101,18 @@ module Fluent
96
101
  self
97
102
  end
98
103
 
104
+ def include(*args)
105
+ ::Kernel.raise ::ArgumentError, "#{name} block requires arguments for include path" if args.nil? || args.size != 1
106
+ if args.first =~ /\.rb$/
107
+ path = File.expand_path(args.first)
108
+ data = File.read(path)
109
+ self.instance_eval(data, path)
110
+ else
111
+ ss = StringScanner.new('')
112
+ Config::V1Parser.new(ss, @proxy.include_basepath, '', nil).eval_include(@attrs, @elements, args.first)
113
+ end
114
+ end
115
+
99
116
  def source(&block)
100
117
  @proxy.add_element('source', nil, block)
101
118
  end
@@ -151,6 +151,10 @@ module Fluent
151
151
  elems = conf.respond_to?(:elements) ? conf.elements : []
152
152
  elems.each { |e|
153
153
  next if plugin_class.nil? && Fluent::Config::V1Parser::ELEM_SYMBOLS.include?(e.name) # skip pre-defined non-plugin elements because it doens't have proxy section
154
+ # In v0.12, buffer and output parameters are defined in same place.
155
+ # It causes same configuration is used in different buffer / output
156
+ # and buffer plugin's sections are always empty. It should be skipped.
157
+ next if proxy.sections.empty?
154
158
 
155
159
  unless proxy.sections.any? { |name, subproxy| e.name == subproxy.name.to_s || e.name == subproxy.alias.to_s }
156
160
  parent_name = if conf.arg.empty?
@@ -80,19 +80,58 @@ module Fluent
80
80
  SIZE_TYPE = Proc.new { |val, opts| Config.size_value(val) }
81
81
  BOOL_TYPE = Proc.new { |val, opts| Config.bool_value(val) }
82
82
  TIME_TYPE = Proc.new { |val, opts| Config.time_value(val) }
83
+
84
+ REFORMAT_VALUE = ->(type, value) {
85
+ if value.nil?
86
+ value
87
+ else
88
+ case type
89
+ when :string then value.to_s
90
+ when :integer then value.to_i
91
+ when :float then value.to_f
92
+ when :size then Config.size_value(value)
93
+ when :bool then Config.bool_value(value)
94
+ when :time then Config.time_value(value)
95
+ else
96
+ raise "unknown type in REFORMAT: #{type}"
97
+ end
98
+ end
99
+ }
100
+
83
101
  HASH_TYPE = Proc.new { |val, opts|
84
- param = val.is_a?(String) ? JSON.load(val) : val
102
+ param = if val.is_a?(String)
103
+ val.start_with?('{') ? JSON.load(val) : Hash[val.strip.split(/\s*,\s*/).map{|v| v.split(':', 2)}]
104
+ else
105
+ val
106
+ end
85
107
  if param.class != Hash
86
108
  raise ConfigError, "hash required but got #{val.inspect}"
87
109
  end
88
- param
110
+ if opts.empty?
111
+ param
112
+ else
113
+ newparam = {}
114
+ param.each_pair do |key, value|
115
+ new_key = opts[:symbolize_keys] ? key.to_sym : key
116
+ newparam[new_key] = opts[:value_type] ? REFORMAT_VALUE.call(opts[:value_type], value) : value
117
+ end
118
+ newparam
119
+ end
89
120
  }
90
121
  ARRAY_TYPE = Proc.new { |val, opts|
91
- param = val.is_a?(String) ? JSON.load(val) : val
122
+ param = if val.is_a?(String)
123
+ val.start_with?('[') ? JSON.load(val) : val.strip.split(/\s*,\s*/)
124
+ else
125
+ val
126
+ end
92
127
  if param.class != Array
93
128
  raise ConfigError, "array required but got #{val.inspect}"
94
129
  end
95
- param
130
+ if opts[:value_type]
131
+ param.map{|v| REFORMAT_VALUE.call(opts[:value_type], v) }
132
+ else
133
+ param
134
+ end
96
135
  }
97
136
  end
98
137
 
@@ -148,7 +148,9 @@ module Fluent
148
148
 
149
149
  def eval_include(attrs, elems, uri)
150
150
  u = URI.parse(uri)
151
- if u.scheme == 'file' || u.path == uri # file path
151
+ if u.scheme == 'file' || (!u.scheme.nil? && u.scheme.length == 1) || u.path == uri # file path
152
+ # When the Windows absolute path then u.scheme.length == 1
153
+ # e.g. C:
152
154
  path = u.path
153
155
  if path[0] != ?/
154
156
  pattern = File.expand_path("#{@include_basepath}/#{path}")
@@ -156,10 +158,10 @@ module Fluent
156
158
  pattern = path
157
159
  end
158
160
 
159
- Dir.glob(pattern).sort.each { |path|
160
- basepath = File.dirname(path)
161
- fname = File.basename(path)
162
- data = File.read(path)
161
+ Dir.glob(pattern).sort.each { |entry|
162
+ basepath = File.dirname(entry)
163
+ fname = File.basename(entry)
164
+ data = File.read(entry)
163
165
  data.force_encoding('UTF-8')
164
166
  ss = StringScanner.new(data)
165
167
  V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
@@ -300,6 +300,8 @@ module Fluent
300
300
  end
301
301
  when /Origin/i
302
302
  @origin = v
303
+ when /X-Forwarded-For/i
304
+ @remote_addr = v.split(",").first
303
305
  end
304
306
  }
305
307
  if expect
@@ -92,6 +92,7 @@ module Fluent
92
92
  desc 'Specify key of source host when include_source_host is true.'
93
93
  config_param :source_host_key, :string, default: 'source_host'.freeze
94
94
  config_param :blocking_timeout, :time, default: 0.5
95
+ config_param :message_length_limit, :size, default: 2048
95
96
 
96
97
  def configure(conf)
97
98
  super
@@ -183,7 +184,7 @@ module Fluent
183
184
  if @protocol_type == :udp
184
185
  @usock = SocketUtil.create_udp_socket(@bind)
185
186
  @usock.bind(@bind, @port)
186
- SocketUtil::UdpHandler.new(@usock, log, 2048, callback)
187
+ SocketUtil::UdpHandler.new(@usock, log, @message_length_limit, callback)
187
188
  else
188
189
  # syslog family add "\n" to each message and this seems only way to split messages in tcp stream
189
190
  Coolio::TCPServer.new(@bind, @port, SocketUtil::TcpHandler, log, "\n", callback)
@@ -145,17 +145,19 @@ module Fluent
145
145
  assert_equal(@expected_buffer, buffer)
146
146
  end
147
147
 
148
- chunks = @instance.instance_eval {
149
- @buffer.instance_eval {
150
- chunks = []
151
- @map.keys.each {|key|
152
- chunks.push(@map.delete(key))
153
- }
154
- chunks
155
- }
156
- }
148
+ chunks = []
149
+ @instance.instance_eval do
150
+ @buffer.instance_eval{ @map.keys }.each do |key|
151
+ @buffer.push(key)
152
+ chunks << @buffer.instance_eval{ @queue.pop }
153
+ end
154
+ end
157
155
  chunks.each { |chunk|
158
- result.push(@instance.write(chunk))
156
+ begin
157
+ result.push(@instance.write(chunk))
158
+ ensure
159
+ chunk.purge
160
+ end
159
161
  }
160
162
  }
161
163
  result
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.26'
19
+ VERSION = '0.12.27'
20
20
 
21
21
  end
@@ -2,6 +2,66 @@ require_relative '../helper'
2
2
  require 'fluent/config/element'
3
3
  require "fluent/config/dsl"
4
4
 
5
+ TMP_DIR = File.dirname(__FILE__) + "/tmp/config_dsl#{ENV['TEST_ENV_NUMBER']}"
6
+ def write_config(path, data)
7
+ FileUtils.mkdir_p(File.dirname(path))
8
+ File.open(path, "w") {|f| f.write data }
9
+ end
10
+
11
+ def prepare_config1
12
+ write_config "#{TMP_DIR}/config_test_1.conf", %[
13
+ k1 root_config
14
+ include dir/config_test_2.conf #
15
+ @include #{TMP_DIR}/config_test_4.conf
16
+ include file://#{TMP_DIR}/config_test_5.conf
17
+ @include config.d/*.conf
18
+ ]
19
+ write_config "#{TMP_DIR}/dir/config_test_2.conf", %[
20
+ k2 relative_path_include
21
+ @include ../config_test_3.conf
22
+ ]
23
+ write_config "#{TMP_DIR}/config_test_3.conf", %[
24
+ k3 relative_include_in_included_file
25
+ ]
26
+ write_config "#{TMP_DIR}/config_test_4.conf", %[
27
+ k4 absolute_path_include
28
+ ]
29
+ write_config "#{TMP_DIR}/config_test_5.conf", %[
30
+ k5 uri_include
31
+ ]
32
+ write_config "#{TMP_DIR}/config.d/config_test_6.conf", %[
33
+ k6 wildcard_include_1
34
+ <elem1 name>
35
+ include normal_parameter
36
+ </elem1>
37
+ ]
38
+ write_config "#{TMP_DIR}/config.d/config_test_7.conf", %[
39
+ k7 wildcard_include_2
40
+ ]
41
+ write_config "#{TMP_DIR}/config.d/config_test_8.conf", %[
42
+ <elem2 name>
43
+ @include ../dir/config_test_9.conf
44
+ </elem2>
45
+ ]
46
+ write_config "#{TMP_DIR}/dir/config_test_9.conf", %[
47
+ k9 embeded
48
+ <elem3 name>
49
+ nested nested_value
50
+ include hoge
51
+ </elem3>
52
+ ]
53
+ write_config "#{TMP_DIR}/config.d/00_config_test_8.conf", %[
54
+ k8 wildcard_include_3
55
+ <elem4 name>
56
+ include normal_parameter
57
+ </elem4>
58
+ ]
59
+ end
60
+
61
+ def prepare_config2
62
+ write_config "#{TMP_DIR}/config_test_1.rb", DSL_CONFIG_EXAMPLE
63
+ end
64
+
5
65
  DSL_CONFIG_EXAMPLE = %q[
6
66
  worker {
7
67
  hostname = "myhostname"
@@ -57,6 +117,14 @@ source {
57
117
  }
58
118
  ]
59
119
 
120
+ DSL_CONFIG_EXAMPLE_FOR_INCLUDE_CONF = %q[
121
+ include "#{TMP_DIR}/config_test_1.conf"
122
+ ]
123
+
124
+ DSL_CONFIG_EXAMPLE_FOR_INCLUDE_RB = %q[
125
+ include "#{TMP_DIR}/config_test_1.rb"
126
+ ]
127
+
60
128
  DSL_CONFIG_RETURNS_NON_ELEMENT = %q[
61
129
  worker {
62
130
  }
@@ -74,6 +142,9 @@ match('aa','bb'){
74
142
  DSL_CONFIG_WRONG_SYNTAX3 = %q[
75
143
  match('aa','bb')
76
144
  ]
145
+ DSL_CONFIG_WRONG_SYNTAX4 = %q[
146
+ include
147
+ ]
77
148
 
78
149
  module Fluent::Config
79
150
  class TestDSLParser < ::Test::Unit::TestCase
@@ -158,6 +229,112 @@ module Fluent::Config
158
229
  end
159
230
  end
160
231
 
232
+ sub_test_case 'with include conf' do
233
+ def setup
234
+ prepare_config1
235
+ @root = Fluent::Config::DSL::Parser.parse(DSL_CONFIG_EXAMPLE_FOR_INCLUDE_CONF, 'dsl_config_for_include.conf')
236
+ end
237
+ test 'include config' do
238
+ assert_equal('root_config', @root['k1'])
239
+ assert_equal('relative_path_include', @root['k2'])
240
+ assert_equal('relative_include_in_included_file', @root['k3'])
241
+ assert_equal('absolute_path_include', @root['k4'])
242
+ assert_equal('uri_include', @root['k5'])
243
+ assert_equal('wildcard_include_1', @root['k6'])
244
+ assert_equal('wildcard_include_2', @root['k7'])
245
+ assert_equal('wildcard_include_3', @root['k8'])
246
+ assert_equal([
247
+ 'k1',
248
+ 'k2',
249
+ 'k3',
250
+ 'k4',
251
+ 'k5',
252
+ 'k8', # Because of the file name this comes first.
253
+ 'k6',
254
+ 'k7',
255
+ ], @root.keys)
256
+
257
+ elem1 = @root.elements.find { |e| e.name == 'elem1' }
258
+ assert(elem1)
259
+ assert_equal('name', elem1.arg)
260
+ assert_equal('normal_parameter', elem1['include'])
261
+
262
+ elem2 = @root.elements.find { |e| e.name == 'elem2' }
263
+ assert(elem2)
264
+ assert_equal('name', elem2.arg)
265
+ assert_equal('embeded', elem2['k9'])
266
+ assert_not_include(elem2, 'include')
267
+
268
+ elem3 = elem2.elements.find { |e| e.name == 'elem3' }
269
+ assert(elem3)
270
+ assert_equal('nested_value', elem3['nested'])
271
+ assert_equal('hoge', elem3['include'])
272
+ end
273
+
274
+ # TODO: Add uri based include spec
275
+ end
276
+
277
+ sub_test_case 'with include rb' do
278
+ def setup
279
+ prepare_config2
280
+ @root = Fluent::Config::DSL::Parser.parse(DSL_CONFIG_EXAMPLE_FOR_INCLUDE_RB, 'dsl_config_for_include.rb')
281
+ end
282
+ sub_test_case '.parse' do
283
+ test 'makes root element' do
284
+ assert_equal('ROOT', @root.name)
285
+ assert_predicate(@root.arg, :empty?)
286
+ assert_equal(0, @root.keys.size)
287
+ end
288
+
289
+ test 'makes worker element for worker tag' do
290
+ assert_equal(1, @root.elements.size)
291
+
292
+ worker = @root.elements.first
293
+ assert_equal('worker', worker.name)
294
+ assert_predicate(worker.arg, :empty?)
295
+ assert_equal(0, worker.keys.size)
296
+ assert_equal(10, worker.elements.size)
297
+ end
298
+
299
+ test 'makes subsections for blocks, with variable substitution' do
300
+ ele4 = @root.elements.first.elements[4]
301
+
302
+ assert_equal('source', ele4.name)
303
+ assert_predicate(ele4.arg, :empty?)
304
+ assert_equal(2, ele4.keys.size)
305
+ assert_equal('tail', ele4['type'])
306
+ assert_equal("/var/log/httpd/access.part4.log", ele4['path'])
307
+ end
308
+
309
+ test 'makes user-defined sections with blocks' do
310
+ filter0 = @root.elements.first.elements[4].elements.first
311
+
312
+ assert_equal('filter', filter0.name)
313
+ assert_equal('bar.**', filter0.arg)
314
+ assert_equal('hoge', filter0['type'])
315
+ assert_equal('moge', filter0['val1'])
316
+ assert_equal(JSON.dump(['foo', 'bar', 'baz']), filter0['val2'])
317
+ assert_equal('10', filter0['val3'])
318
+ assert_equal('hoge', filter0['id'])
319
+
320
+ assert_equal(2, filter0.elements.size)
321
+ assert_equal('subsection', filter0.elements[0].name)
322
+ assert_equal('bar', filter0.elements[0]['foo'])
323
+ assert_equal('subsection', filter0.elements[1].name)
324
+ assert_equal('baz', filter0.elements[1]['foo'])
325
+ end
326
+
327
+ test 'makes values with user-assigned variable substitutions' do
328
+ match0 = @root.elements.first.elements[4].elements.last
329
+
330
+ assert_equal('match', match0.name)
331
+ assert_equal('{foo,bar}.**', match0.arg)
332
+ assert_equal('file', match0['type'])
333
+ assert_equal('/var/log/httpd/access.myhostname.4.log', match0['path'])
334
+ end
335
+ end
336
+ end
337
+
161
338
  sub_test_case 'with configuration that returns non element on top' do
162
339
  sub_test_case '.parse' do
163
340
  test 'does not crash' do
@@ -170,8 +347,9 @@ module Fluent::Config
170
347
  sub_test_case '.parse' do
171
348
  test 'raises ArgumentError correctly' do
172
349
  assert_raise(ArgumentError) { Fluent::Config::DSL::Parser.parse(DSL_CONFIG_WRONG_SYNTAX1, 'dsl_config_wrong_syntax1') }
173
- assert_raise(ArgumentError) { Fluent::Config::DSL::Parser.parse(DSL_CONFIG_WRONG_SYNTAX2, 'dsl_config_wrong_syntax1') }
174
- assert_raise(ArgumentError) { Fluent::Config::DSL::Parser.parse(DSL_CONFIG_WRONG_SYNTAX3, 'dsl_config_wrong_syntax1') }
350
+ assert_raise(ArgumentError) { Fluent::Config::DSL::Parser.parse(DSL_CONFIG_WRONG_SYNTAX2, 'dsl_config_wrong_syntax2') }
351
+ assert_raise(ArgumentError) { Fluent::Config::DSL::Parser.parse(DSL_CONFIG_WRONG_SYNTAX3, 'dsl_config_wrong_syntax3') }
352
+ assert_raise(ArgumentError) { Fluent::Config::DSL::Parser.parse(DSL_CONFIG_WRONG_SYNTAX4, 'dsl_config_wrong_syntax4') }
175
353
  end
176
354
  end
177
355
  end
@@ -124,10 +124,44 @@ class TestConfigTypes < ::Test::Unit::TestCase
124
124
 
125
125
  test 'hash' do
126
126
  assert_equal({"x"=>"v","k"=>1}, Config::HASH_TYPE.call('{"x":"v","k":1}', {}))
127
+ assert_equal({"x"=>"v","k"=>"1"}, Config::HASH_TYPE.call('x:v,k:1', {}))
128
+ assert_equal({"x"=>"v","k"=>"1"}, Config::HASH_TYPE.call('x:v, k:1', {}))
129
+ assert_equal({"x"=>"v","k"=>"1"}, Config::HASH_TYPE.call(' x:v, k:1 ', {}))
130
+ assert_equal({"x"=>"v","k"=>"1"}, Config::HASH_TYPE.call('x:v , k:1 ', {}))
131
+
132
+ assert_equal({"x"=>"v:v","k"=>"1"}, Config::HASH_TYPE.call('x:v:v, k:1', {}))
133
+
134
+ assert_equal({x: "v", k: 1}, Config::HASH_TYPE.call('{"x":"v","k":1}', {symbolize_keys: true}))
135
+ assert_equal({x: "v", k: "1"}, Config::HASH_TYPE.call('x:v,k:1', {symbolize_keys: true, value_type: :string}))
136
+ assert_equal({x: "v", k: "1"}, Config::HASH_TYPE.call('{"x":"v","k":1}', {symbolize_keys: true, value_type: :string}))
137
+ assert_equal({x: 0, k: 1}, Config::HASH_TYPE.call('x:0,k:1', {symbolize_keys: true, value_type: :integer}))
138
+
139
+ assert_equal({"x"=>1,"y"=>60,"z"=>3600}, Config::HASH_TYPE.call('{"x":"1s","y":"1m","z":"1h"}', {value_type: :time}))
140
+ assert_equal({"x"=>1,"y"=>60,"z"=>3600}, Config::HASH_TYPE.call('x:1s,y:1m,z:1h', {value_type: :time}))
127
141
  end
128
142
 
129
143
  test 'array' do
130
144
  assert_equal(["1","2",1], Config::ARRAY_TYPE.call('["1","2",1]', {}))
145
+ assert_equal(["1","2","1"], Config::ARRAY_TYPE.call('1,2,1', {}))
146
+
147
+ assert_equal(["a","b","c"], Config::ARRAY_TYPE.call('["a","b","c"]', {}))
148
+ assert_equal(["a","b","c"], Config::ARRAY_TYPE.call('a,b,c', {}))
149
+ assert_equal(["a","b","c"], Config::ARRAY_TYPE.call('a, b, c', {}))
150
+ assert_equal(["a","b","c"], Config::ARRAY_TYPE.call('a , b , c', {}))
151
+
152
+ assert_equal(["a a","b,b"," c "], Config::ARRAY_TYPE.call('["a a","b,b"," c "]', {}))
153
+
154
+ assert_equal(["a a","b","c"], Config::ARRAY_TYPE.call('a a,b,c', {}))
155
+
156
+ assert_equal([1,2,1], Config::ARRAY_TYPE.call('[1,2,1]', {}))
157
+ assert_equal([1,2,1], Config::ARRAY_TYPE.call('["1","2","1"]', {value_type: :integer}))
158
+ assert_equal([1,2,1], Config::ARRAY_TYPE.call('1,2,1', {value_type: :integer}))
159
+
160
+ array_options = {
161
+ default: [],
162
+ }
163
+ assert_equal(["1","2"], Config::ARRAY_TYPE.call('["1","2"]', array_options))
164
+ assert_equal(["3"], Config::ARRAY_TYPE.call('["3"]', array_options))
131
165
  end
132
166
  end
133
167
  end
@@ -42,6 +42,10 @@ class DummyTest < Test::Unit::TestCase
42
42
  assert_equal 10, d.instance.rate
43
43
  end
44
44
 
45
+ def json_error_messages_regexp
46
+ /JSON::ParserError|got incomplete JSON|0th element of dummy, foo, is not a hash/
47
+ end
48
+
45
49
  test 'dummy' do
46
50
  # hash is okay
47
51
  d = create_driver(config + %[dummy {"foo":"bar"}])
@@ -51,7 +55,7 @@ class DummyTest < Test::Unit::TestCase
51
55
  d = create_driver(config + %[dummy [{"foo":"bar"}]])
52
56
  assert_equal [{"foo"=>"bar"}], d.instance.dummy
53
57
 
54
- assert_raise_message(/JSON::ParserError|got incomplete JSON/) do
58
+ assert_raise_message(json_error_messages_regexp) do
55
59
  create_driver(config + %[dummy "foo"])
56
60
  end
57
61
 
@@ -119,6 +119,39 @@ class HttpInputTest < Test::Unit::TestCase
119
119
 
120
120
  end
121
121
 
122
+ def test_json_with_add_remote_addr_given_x_forwarded_for
123
+ d = create_driver(CONFIG + "add_remote_addr true")
124
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
125
+
126
+ d.expect_emit "tag1", time, {"REMOTE_ADDR"=>"129.78.138.66", "a"=>1}
127
+ d.expect_emit "tag2", time, {"REMOTE_ADDR"=>"129.78.138.66", "a"=>1}
128
+
129
+ d.run do
130
+ d.expected_emits.each {|tag,_time,record|
131
+ res = post("/#{tag}", {"json"=>record.to_json, "time"=>_time.to_s}, {"X-Forwarded-For"=>"129.78.138.66, 127.0.0.1"})
132
+ assert_equal "200", res.code
133
+ }
134
+ end
135
+
136
+ end
137
+
138
+ def test_multi_json_with_add_remote_addr_given_x_forwarded_for
139
+ d = create_driver(CONFIG + "add_remote_addr true")
140
+
141
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
142
+ events = [{"a"=>1},{"a"=>2}]
143
+ tag = "tag1"
144
+
145
+ d.expect_emit "tag1", time, {"REMOTE_ADDR"=>"129.78.138.66", "a"=>1}
146
+ d.expect_emit "tag1", time, {"REMOTE_ADDR"=>"129.78.138.66", "a"=>2}
147
+
148
+ d.run do
149
+ res = post("/#{tag}", {"json"=>events.to_json, "time"=>time.to_s}, {"X-Forwarded-For"=>"129.78.138.66, 127.0.0.1"})
150
+ assert_equal "200", res.code
151
+ end
152
+
153
+ end
154
+
122
155
  def test_multi_json_with_add_http_headers
123
156
  d = create_driver(CONFIG + "add_http_headers true")
124
157
 
@@ -274,50 +307,33 @@ class HttpInputTest < Test::Unit::TestCase
274
307
  d = create_driver(CONFIG + "cors_allow_origins [\"http://foo.com\"]")
275
308
  assert_equal ["http://foo.com"], d.instance.cors_allow_origins
276
309
 
277
- test_in_http_cros_allowed = nil
278
- acao = nil
310
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
279
311
 
280
- begin
281
- d.run do
282
- Net::HTTP.start("127.0.0.1", PORT) do |http|
283
- req = Net::HTTP::Post.new("/foo/bar", {"Origin" => "http://foo.com"})
284
- res = http.request(req)
312
+ d.expect_emit "tag1", time, {"a"=>1}
313
+ d.expect_emit "tag2", time, {"a"=>1}
285
314
 
286
- acao = res["Access-Control-Allow-Origin"]
287
- end
288
- end
289
- test_in_http_cros_allowed = true
290
- rescue
291
- test_in_http_cros_allowed = false
315
+ d.run do
316
+ d.expected_emits.each {|tag,_time,record|
317
+ res = post("/#{tag}", {"json"=>record.to_json, "time"=>_time.to_s}, {"Origin"=>"http://foo.com"})
318
+ assert_equal "200", res.code
319
+ assert_equal "http://foo.com", res["Access-Control-Allow-Origin"]
320
+ }
292
321
  end
293
-
294
- assert_equal true, test_in_http_cros_allowed
295
- assert_equal "http://foo.com", acao
296
322
  end
297
323
 
298
324
  def test_cors_disallowed
299
325
  d = create_driver(CONFIG + "cors_allow_origins [\"http://foo.com\"]")
300
326
  assert_equal ["http://foo.com"], d.instance.cors_allow_origins
301
327
 
302
- test_in_http_cros_disallowed = nil
303
- response_code = nil
304
-
305
- begin
306
- d.run do
307
- Net::HTTP.start("127.0.0.1", PORT) do |http|
308
- req = Net::HTTP::Post.new("/foo/bar", {"Origin" => "http://bar.com"})
309
- res = http.request(req)
328
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
310
329
 
311
- response_code = res.code
312
- end
313
- end
314
- test_in_http_cros_disallowed = true
315
- rescue
316
- test_in_http_cros_disallowed = false
330
+ d.expected_emits_length = 0
331
+ d.run do
332
+ res = post("/tag1", {"json"=>{"a"=>1}.to_json, "time"=>time.to_s}, {"Origin"=>"http://bar.com"})
333
+ assert_equal "403", res.code
334
+ res = post("/tag2", {"json"=>{"a"=>1}.to_json, "time"=>time.to_s}, {"Origin"=>"http://bar.com"})
335
+ assert_equal "403", res.code
317
336
  end
318
-
319
- assert_equal true, test_in_http_cros_disallowed
320
- assert_equal "403", response_code
321
337
  end
322
338
 
323
339
  $test_in_http_connection_object_ids = []
@@ -79,6 +79,24 @@ class SyslogInputTest < Test::Unit::TestCase
79
79
  compare_test_result(d.emits, tests)
80
80
  end
81
81
 
82
+ def test_msg_size_udp_for_large_msg
83
+ d = create_driver(CONFIG + %[
84
+ message_length_limit 5k
85
+ ])
86
+ tests = create_test_case(true)
87
+
88
+ d.run do
89
+ u = UDPSocket.new
90
+ u.connect('127.0.0.1', PORT)
91
+ tests.each {|test|
92
+ u.send(test['msg'], 0)
93
+ }
94
+ sleep 1
95
+ end
96
+
97
+ compare_test_result(d.emits, tests)
98
+ end
99
+
82
100
  def test_msg_size_with_tcp
83
101
  d = create_driver([CONFIG, 'protocol_type tcp'].join("\n"))
84
102
  tests = create_test_case
@@ -149,12 +167,20 @@ class SyslogInputTest < Test::Unit::TestCase
149
167
  compare_test_result(d.emits, tests, host)
150
168
  end
151
169
 
152
- def create_test_case
170
+ def create_test_case(large_message = false)
153
171
  # actual syslog message has "\n"
154
- [
155
- {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100 + "\n", 'expected' => 'x' * 100},
156
- {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 1024 + "\n", 'expected' => 'x' * 1024},
157
- ]
172
+ if large_message
173
+ [
174
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100 + "\n", 'expected' => 'x' * 100},
175
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 1024 + "\n", 'expected' => 'x' * 1024},
176
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 4096 + "\n", 'expected' => 'x' * 4096},
177
+ ]
178
+ else
179
+ [
180
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100 + "\n", 'expected' => 'x' * 100},
181
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 1024 + "\n", 'expected' => 'x' * 1024},
182
+ ]
183
+ end
158
184
  end
159
185
 
160
186
  def compare_test_result(emits, tests, host = nil)
@@ -20,7 +20,7 @@ class FileOutputTest < Test::Unit::TestCase
20
20
  ]
21
21
 
22
22
  def create_driver(conf = CONFIG)
23
- Fluent::Test::BufferedOutputTestDriver.new(Fluent::FileOutput).configure(conf)
23
+ Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::FileOutput).configure(conf)
24
24
  end
25
25
 
26
26
  def with_timezone(timezone = 'UTC', &block)
@@ -143,11 +143,11 @@ class FileOutputTest < Test::Unit::TestCase
143
143
  d.emit({"a"=>2}, time)
144
144
 
145
145
  # FileOutput#write returns path
146
- path = d.run
147
- expect_path = "#{TMP_DIR}/out_file_test.20110102_0.log.gz"
148
- assert_equal expect_path, path
146
+ paths = d.run
147
+ expect_paths = ["#{TMP_DIR}/out_file_test.20110102_0.log.gz"]
148
+ assert_equal expect_paths, paths
149
149
 
150
- check_gzipped_result(path, %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n])
150
+ check_gzipped_result(paths[0], %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n])
151
151
  end
152
152
 
153
153
  def test_write_with_format_json
@@ -158,8 +158,8 @@ class FileOutputTest < Test::Unit::TestCase
158
158
  d.emit({"a"=>2}, time)
159
159
 
160
160
  # FileOutput#write returns path
161
- path = d.run
162
- check_gzipped_result(path, %[#{Yajl.dump({"a" => 1, 'time' => time})}\n] + %[#{Yajl.dump({"a" => 2, 'time' => time})}\n])
161
+ paths = d.run
162
+ check_gzipped_result(paths[0], %[#{Yajl.dump({"a" => 1, 'time' => time})}\n] + %[#{Yajl.dump({"a" => 2, 'time' => time})}\n])
163
163
  end
164
164
 
165
165
  def test_write_with_format_ltsv
@@ -170,8 +170,8 @@ class FileOutputTest < Test::Unit::TestCase
170
170
  d.emit({"a"=>2}, time)
171
171
 
172
172
  # FileOutput#write returns path
173
- path = d.run
174
- check_gzipped_result(path, %[a:1\ttime:2011-01-02T13:14:15Z\n] + %[a:2\ttime:2011-01-02T13:14:15Z\n])
173
+ paths = d.run
174
+ check_gzipped_result(paths[0], %[a:1\ttime:2011-01-02T13:14:15Z\n] + %[a:2\ttime:2011-01-02T13:14:15Z\n])
175
175
  end
176
176
 
177
177
  def test_write_with_format_single_value
@@ -182,53 +182,66 @@ class FileOutputTest < Test::Unit::TestCase
182
182
  d.emit({"a"=>2}, time)
183
183
 
184
184
  # FileOutput#write returns path
185
- path = d.run
186
- check_gzipped_result(path, %[1\n] + %[2\n])
185
+ paths = d.run
186
+ check_gzipped_result(paths[0], %[1\n] + %[2\n])
187
187
  end
188
188
 
189
189
  def test_write_path_increment
190
- d = create_driver
191
-
192
190
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
193
- d.emit({"a"=>1}, time)
194
- d.emit({"a"=>2}, time)
195
191
  formatted_lines = %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
196
192
 
193
+ write_once = ->(){
194
+ d = create_driver
195
+ d.emit({"a"=>1}, time)
196
+ d.emit({"a"=>2}, time)
197
+ d.run
198
+ }
199
+
200
+ assert !File.exist?("#{TMP_DIR}/out_file_test.20110102_0.log.gz")
201
+
197
202
  # FileOutput#write returns path
198
- path = d.run
199
- assert_equal "#{TMP_DIR}/out_file_test.20110102_0.log.gz", path
200
- check_gzipped_result(path, formatted_lines)
201
- path = d.run
202
- assert_equal "#{TMP_DIR}/out_file_test.20110102_1.log.gz", path
203
- check_gzipped_result(path, formatted_lines)
204
- path = d.run
205
- assert_equal "#{TMP_DIR}/out_file_test.20110102_2.log.gz", path
206
- check_gzipped_result(path, formatted_lines)
203
+ paths = write_once.call
204
+ assert_equal ["#{TMP_DIR}/out_file_test.20110102_0.log.gz"], paths
205
+ check_gzipped_result(paths[0], formatted_lines)
206
+ assert_equal 1, Dir.glob("#{TMP_DIR}/out_file_test.*").size
207
+
208
+ paths = write_once.call
209
+ assert_equal ["#{TMP_DIR}/out_file_test.20110102_1.log.gz"], paths
210
+ check_gzipped_result(paths[0], formatted_lines)
211
+ assert_equal 2, Dir.glob("#{TMP_DIR}/out_file_test.*").size
212
+
213
+ paths = write_once.call
214
+ assert_equal ["#{TMP_DIR}/out_file_test.20110102_2.log.gz"], paths
215
+ check_gzipped_result(paths[0], formatted_lines)
216
+ assert_equal 3, Dir.glob("#{TMP_DIR}/out_file_test.*").size
207
217
  end
208
218
 
209
219
  def test_write_with_append
210
- d = create_driver %[
211
- path #{TMP_DIR}/out_file_test
212
- compress gz
213
- utc
214
- append true
215
- ]
216
-
217
220
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
218
- d.emit({"a"=>1}, time)
219
- d.emit({"a"=>2}, time)
220
221
  formatted_lines = %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
221
222
 
223
+ write_once = ->(){
224
+ d = create_driver %[
225
+ path #{TMP_DIR}/out_file_test
226
+ compress gz
227
+ utc
228
+ append true
229
+ ]
230
+ d.emit({"a"=>1}, time)
231
+ d.emit({"a"=>2}, time)
232
+ d.run
233
+ }
234
+
222
235
  # FileOutput#write returns path
223
- path = d.run
224
- assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
225
- check_gzipped_result(path, formatted_lines)
226
- path = d.run
227
- assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
228
- check_gzipped_result(path, formatted_lines * 2)
229
- path = d.run
230
- assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
231
- check_gzipped_result(path, formatted_lines * 3)
236
+ paths = write_once.call
237
+ assert_equal ["#{TMP_DIR}/out_file_test.20110102.log.gz"], paths
238
+ check_gzipped_result(paths[0], formatted_lines)
239
+ paths = write_once.call
240
+ assert_equal ["#{TMP_DIR}/out_file_test.20110102.log.gz"], paths
241
+ check_gzipped_result(paths[0], formatted_lines * 2)
242
+ paths = write_once.call
243
+ assert_equal ["#{TMP_DIR}/out_file_test.20110102.log.gz"], paths
244
+ check_gzipped_result(paths[0], formatted_lines * 3)
232
245
  end
233
246
 
234
247
  def test_write_with_symlink
@@ -270,8 +283,8 @@ class FileOutputTest < Test::Unit::TestCase
270
283
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
271
284
  d.emit({"a"=>1}, time)
272
285
  # FileOutput#write returns path
273
- path = d.run
274
- assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13_0.log", path
286
+ paths = d.run
287
+ assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13_0.log"], paths
275
288
  end
276
289
 
277
290
  test 'normal with append' do
@@ -283,8 +296,8 @@ class FileOutputTest < Test::Unit::TestCase
283
296
  ])
284
297
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
285
298
  d.emit({"a"=>1}, time)
286
- path = d.run
287
- assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13.log", path
299
+ paths = d.run
300
+ assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13.log"], paths
288
301
  end
289
302
 
290
303
  test '*' do
@@ -295,8 +308,8 @@ class FileOutputTest < Test::Unit::TestCase
295
308
  ])
296
309
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
297
310
  d.emit({"a"=>1}, time)
298
- path = d.run
299
- assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13_0.txt", path
311
+ paths = d.run
312
+ assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13_0.txt"], paths
300
313
  end
301
314
 
302
315
  test '* with append' do
@@ -308,8 +321,8 @@ class FileOutputTest < Test::Unit::TestCase
308
321
  ])
309
322
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
310
323
  d.emit({"a"=>1}, time)
311
- path = d.run
312
- assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13.txt", path
324
+ paths = d.run
325
+ assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13.txt"], paths
313
326
  end
314
327
  end
315
328
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluentd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.26
4
+ version: 0.12.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-30 00:00:00.000000000 Z
11
+ date: 2016-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack