fluentd-ui 1.0.1 → 1.1.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-ui might be problematic. Click here for more details.

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +5 -0
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/api/settings_controller.rb +46 -16
  5. data/app/controllers/concerns/setting_concern.rb +1 -1
  6. data/app/controllers/fluentd/settings/filter_grep_controller.rb +9 -0
  7. data/app/controllers/fluentd/settings/filter_parser_controller.rb +9 -0
  8. data/app/controllers/fluentd/settings/filter_record_transformer_controller.rb +14 -0
  9. data/app/controllers/fluentd/settings/filter_stdout_controller.rb +9 -0
  10. data/app/javascript/packs/filter_grep_setting.js +39 -0
  11. data/app/javascript/packs/grep_container.js +53 -0
  12. data/app/javascript/packs/grep_pattern.js +43 -0
  13. data/app/javascript/packs/owned_plugin_form.js +9 -7
  14. data/app/javascript/packs/settings.js +34 -16
  15. data/app/models/concerns/fluentd/setting/configurable.rb +1 -0
  16. data/app/models/concerns/fluentd/setting/label.rb +11 -0
  17. data/app/models/concerns/fluentd/setting/plugin.rb +2 -0
  18. data/app/models/concerns/fluentd/setting/plugin_config.rb +34 -4
  19. data/app/models/concerns/fluentd/setting/section_config.rb +10 -2
  20. data/app/models/fluentd/agent/common.rb +97 -0
  21. data/app/models/fluentd/setting/config.rb +71 -0
  22. data/app/models/fluentd/setting/filter_grep.rb +38 -0
  23. data/app/models/fluentd/setting/filter_parser.rb +34 -0
  24. data/app/models/fluentd/setting/filter_record_transformer.rb +27 -0
  25. data/app/models/fluentd/setting/filter_stdout.rb +34 -0
  26. data/app/models/fluentd/setting/in_forward.rb +1 -0
  27. data/app/models/fluentd/setting/in_http.rb +1 -1
  28. data/app/models/fluentd/setting/in_monitor_agent.rb +1 -1
  29. data/app/models/fluentd/setting/in_syslog.rb +1 -1
  30. data/app/models/fluentd/setting/out_elasticsearch.rb +1 -0
  31. data/app/models/fluentd/setting/out_forward.rb +1 -1
  32. data/app/models/fluentd/setting/out_mongo.rb +1 -0
  33. data/app/models/fluentd/setting/out_s3.rb +1 -0
  34. data/app/models/fluentd/setting/out_stdout.rb +1 -1
  35. data/app/models/fluentd/setting/out_tdlog.rb +1 -1
  36. data/app/views/api/settings/_element.json.jbuilder +3 -1
  37. data/app/views/api/settings/index.json.jbuilder +10 -2
  38. data/app/views/api/settings/show.json.jbuilder +1 -1
  39. data/app/views/fluentd/settings/filter_grep/_form.html.haml +18 -0
  40. data/app/views/fluentd/settings/filter_record_transformer/_form.html.haml +20 -0
  41. data/app/views/fluentd/settings/in_tail/_form.html.haml +1 -0
  42. data/app/views/fluentd/settings/source_and_output.html.haml +46 -14
  43. data/app/views/shared/vue/_filter_grep_setting.html.haml +19 -0
  44. data/app/views/shared/vue/_grep_container.html.haml +32 -0
  45. data/app/views/shared/vue/_grep_pattern.html.haml +24 -0
  46. data/app/views/shared/vue/_setting.html.erb +2 -2
  47. data/config/locales/translation_en.yml +39 -10
  48. data/config/locales/translation_ja.yml +39 -10
  49. data/config/routes.rb +6 -0
  50. data/lib/fluentd-ui/version.rb +1 -1
  51. data/test/factories/fluentd.rb +4 -4
  52. data/test/factories/plugins.rb +2 -2
  53. data/test/factories/user.rb +2 -2
  54. data/test/fixtures/config/label.conf +15 -0
  55. data/test/fixtures/config/multi-label.conf +40 -0
  56. data/test/fixtures/config/simple.conf +12 -0
  57. data/test/models/fluentd/agent_common_test.rb +288 -0
  58. data/test/models/fluentd/setting/config_test.rb +98 -0
  59. data/test/support/configurable_daemon_settings.rb +1 -1
  60. data/test/system/fluentd/setting/filter_grep_test.rb +96 -0
  61. data/test/system/fluentd/setting/filter_parser_test.rb +13 -0
  62. data/test/system/fluentd/setting/filter_record_transformer_test.rb +30 -0
  63. data/test/system/fluentd/setting/filter_stdout_test.rb +13 -0
  64. data/test/system/source_and_output_test.rb +168 -19
  65. data/test/test_helper.rb +4 -0
  66. metadata +98 -55
  67. data/.rspec +0 -2
@@ -35,6 +35,7 @@ class Fluentd
35
35
  self.class._sections.each do |name, klass|
36
36
  klass.init
37
37
  if klass.multi
38
+ next if attributes.nil?
38
39
  next if attributes[name].nil?
39
40
  attributes[name].each do |attr|
40
41
  next unless attr
@@ -0,0 +1,11 @@
1
+ class Fluentd
2
+ module Setting
3
+ module Label
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ config_argument(:label, :string)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,4 +1,5 @@
1
1
  require "fluent/plugin"
2
+ require "fluent/plugin/buf_file"
2
3
  require "fluent/test/log"
3
4
  require "fluent/test/driver/input"
4
5
  require "fluent/test/driver/output"
@@ -17,6 +18,7 @@ class Fluentd
17
18
  include Fluentd::Setting::PluginConfig
18
19
  include Fluentd::Setting::SectionParser
19
20
  include Fluentd::Setting::PluginParameter
21
+ include Fluentd::Setting::Label
20
22
 
21
23
  included do
22
24
  cattr_accessor :plugin_type, :plugin_name, :config_definition
@@ -10,7 +10,12 @@ class Fluentd
10
10
  def validate_configuration
11
11
  original_log = $log
12
12
  $log = DummyLogger.logger
13
- config = to_config.to_s.lines[1..-2].join
13
+ full_config = to_config.to_s
14
+ config = if full_config.start_with?("<label ")
15
+ full_config.lines[2..-3].join
16
+ else
17
+ full_config.lines[1..-2].join
18
+ end
14
19
  self.class.create_driver(config)
15
20
  rescue Fluent::ConfigError => ex
16
21
  errors.add(:base, :invalid, message: ex.message)
@@ -40,6 +45,10 @@ class Fluentd
40
45
  end
41
46
  _attributes = { "@type" => self.plugin_name }.merge(_attributes)
42
47
  _attributes["@log_level"] = _attributes.delete("log_level")
48
+ label = _attributes.delete("label")
49
+ if plugin_type == "input"
50
+ _attributes["@label"] = label
51
+ end
43
52
  argument = case plugin_type
44
53
  when "output", "filter", "buffer"
45
54
  _attributes.delete(self._argument_name.to_s) || ""
@@ -47,7 +56,18 @@ class Fluentd
47
56
  ""
48
57
  end
49
58
  attrs, elements = parse_attributes(_attributes)
50
- config_element(name, argument, attrs, elements)
59
+ case plugin_type
60
+ when "output", "filter"
61
+ # e is <match> or <filter>
62
+ e = config_element(name, argument, attrs, elements)
63
+ if label.blank?
64
+ e
65
+ else
66
+ config_element("label", label, {}, [e])
67
+ end
68
+ else
69
+ config_element(name, argument, attrs, elements)
70
+ end
51
71
  end
52
72
 
53
73
  def parse_attributes(attributes)
@@ -70,11 +90,21 @@ class Fluentd
70
90
  }
71
91
  end
72
92
  end
73
- elements = section_params.map do |index, _section_params|
93
+ _elements = section_params.map do |index, _section_params|
74
94
  section_class.new(_section_params).to_config
75
95
  end.compact
96
+ elements.concat(_elements)
97
+ end
98
+ params = params.to_h
99
+ if plugin_type == "filter" && plugin_name == "record_transformer"
100
+ record_params = {}
101
+ params.delete("record").lines.each do |line|
102
+ k, v = line.split(" ", 2)
103
+ record_params[k] = v
104
+ end
105
+ elements << config_element("record", "", record_params, [])
76
106
  end
77
- attrs = params.to_h.reject do |key, value|
107
+ attrs = params.reject do |key, value|
78
108
  skip?(key.to_sym, value)
79
109
  end
80
110
  return attrs, elements
@@ -22,9 +22,17 @@ class Fluentd
22
22
  end
23
23
  elements = sections.map do |key, section_params|
24
24
  if section_params.present?
25
- self._sections[key.to_sym].new(section_params).to_config
25
+ section_class = self._sections[key.to_sym]
26
+ if section_class.multi?
27
+ section_params.map do |index, _section_params|
28
+ section_class.new(_section_params).to_config
29
+ end
30
+ else
31
+ section_class.new(section_params).to_config
32
+ end
26
33
  end
27
- end.compact
34
+ end
35
+ elements = elements.flatten.compact
28
36
  attrs = params.to_h.reject do |key, value|
29
37
  skip?(key.to_sym, value)
30
38
  end
@@ -18,6 +18,8 @@
18
18
  # - https://github.com/treasure-data/omnibus-td-agent/blob/master/templates/etc/systemd/td-agent.service.erb#L14
19
19
  # fluentd: /etc/fluent/fluent.conf (created by fluentd -s)
20
20
 
21
+ require "strscan"
22
+
21
23
  class Fluentd
22
24
  class Agent
23
25
  module Common
@@ -74,6 +76,25 @@ class Fluentd
74
76
  end
75
77
  end
76
78
 
79
+ def config_merge(content)
80
+ if content.start_with?("<label ")
81
+ label = content.slice(/<label\s+(.+?)>/, 1)
82
+ key = "label:#{label}"
83
+ parsed_config = parse_config(config)
84
+ if parsed_config.key?(key)
85
+ offset = parsed_config[key][0][:pos] + parsed_config[key][0][:size]
86
+ label, sections = parse_label_section(content, offset)
87
+ parsed_config[key][0][:sections]["filter"].concat(sections["filter"])
88
+ parsed_config[key][0][:sections]["match"].concat(sections["match"])
89
+ config_write(dump_parsed_config(parsed_config))
90
+ else
91
+ config_append(content)
92
+ end
93
+ else
94
+ config_append(content)
95
+ end
96
+ end
97
+
77
98
  def configuration
78
99
  if File.exists? config_file
79
100
  ::Fluentd::Agent::Configuration.new(config_file)
@@ -142,6 +163,82 @@ class Fluentd
142
163
  FileUtils.rm(file) if File.exist? file
143
164
  end
144
165
  end
166
+
167
+ def parse_config(content)
168
+ scanner = StringScanner.new(content)
169
+ contents = Hash.new {|h, k| h[k] = [] }
170
+ until scanner.eos? do
171
+ started = scanner.pos
172
+ header = scanner.scan_until(/^<(source|filter|match|label)/)
173
+ section_type = scanner[1]
174
+ break unless header
175
+ case section_type
176
+ when "source", "filter", "match"
177
+ current_source = header + scanner.scan_until(%r{^</(?:source|filter|match)>})
178
+ contents[section_type] << { pos: started, content: current_source.strip }
179
+ when "label"
180
+ label_content = header + scanner.scan_until(%r{^</label>})
181
+ label, sections = parse_label_section(label_content, started)
182
+ contents["label:#{label}"] << { label: label, pos: started, sections: sections, size: label_content.size }
183
+ else
184
+ raise TypeError, "Unknown section: #{started}: #{section_type}"
185
+ end
186
+ end
187
+ contents
188
+ end
189
+
190
+ def parse_label_section(content, offset)
191
+ scanner = StringScanner.new(content)
192
+ scanner.scan_until(/^<label\s+?([^\s]+?)>/)
193
+ label = scanner[1]
194
+ sections = Hash.new {|h, k| h[k] = [] }
195
+ loop do
196
+ break if scanner.match?(%r{\s+?</label>})
197
+ pos = scanner.pos
198
+ header = scanner.scan_until(/^\s*<(filter|match)/)
199
+ type = scanner[1]
200
+ source = header + scanner.scan_until(%r{^\s*</(?:filter|match)>})
201
+ sections[type] << { label: label, pos: pos + offset, content: source.sub(/\n+/, "") }
202
+ end
203
+ return label, sections
204
+ end
205
+
206
+ def dump_parsed_config(parsed_config)
207
+ content = "".dup
208
+ sources = parsed_config["source"] || []
209
+ filters = parsed_config["filter"] || []
210
+ matches = parsed_config["match"] || []
211
+ labels = parsed_config.select do |key, sections|
212
+ key.start_with?("label:")
213
+ end
214
+ labels = labels.values.flatten
215
+ sorted_sections = (sources + filters + matches + labels).sort_by do |section|
216
+ section[:pos]
217
+ end
218
+ sorted_sections.each do |section|
219
+ if section.key?(:label)
220
+ label = section[:label]
221
+ sub_filters = section.dig(:sections, "filter") || []
222
+ sub_matches = section.dig(:sections, "match") || []
223
+ sorted_sub_filters = sub_filters.sort_by do |sub_section|
224
+ sub_section[:pos]
225
+ end
226
+ sorted_sub_matches = sub_matches.sort_by do |sub_section|
227
+ sub_section[:pos]
228
+ end
229
+ sub_sections = sorted_sub_filters + sorted_sub_matches
230
+ content << "<label #{label}>\n"
231
+ sub_sections.each do |sub_section|
232
+ content << sub_section[:content] << "\n\n"
233
+ end
234
+ content.chomp!
235
+ content << "</label>\n\n"
236
+ else
237
+ content << section[:content] << "\n\n"
238
+ end
239
+ end
240
+ content.chomp
241
+ end
145
242
  end
146
243
  end
147
244
  end
@@ -21,12 +21,83 @@ class Fluentd
21
21
  end
22
22
  end
23
23
 
24
+ def filters
25
+ elements.find_all do |elm|
26
+ elm.name == "filter"
27
+ end
28
+ end
29
+
24
30
  def matches
25
31
  elements.find_all do |elm|
26
32
  elm.name == "match"
27
33
  end
28
34
  end
29
35
 
36
+ def labels
37
+ elements.find_all do |elm|
38
+ elm.name == "label"
39
+ end
40
+ end
41
+
42
+ def group_by_label
43
+ hash = Hash.new{|h, k| h[k] = {} }
44
+ sources.each do |source|
45
+ label = source["@label"] || source["label"]
46
+ if label
47
+ hash[label][:sources] = [source]
48
+ else
49
+ hash["ROOT"][:sources] = [source]
50
+ end
51
+ end
52
+ hash["ROOT"][:filters] = filters unless filters.empty?
53
+ hash["ROOT"][:matches] = matches unless matches.empty?
54
+
55
+ labels.each do |label|
56
+ hash[label.arg][:filters] = label.elements.find_all do |e|
57
+ e.name == "filter"
58
+ end
59
+ hash[label.arg][:matches] = label.elements.find_all do |e|
60
+ e.name == "match"
61
+ end
62
+ end
63
+ hash
64
+ end
65
+
66
+ def delete_element(name, arg, element)
67
+ if name == "label"
68
+ label_section = fl_config.elements(name: name, arg: arg).first
69
+ original_size = label_section.elements.size
70
+ remaining_elements = label_section.elements.reject do |e|
71
+ element == e
72
+ end
73
+ if remaining_elements.empty?
74
+ remaining_elements = fl_config.elements.reject do |e|
75
+ label_section == e
76
+ end
77
+ fl_config.elements = remaining_elements
78
+ return element
79
+ else
80
+ label_section.elements = remaining_elements
81
+ if original_size == label_section.elements.size
82
+ return nil
83
+ else
84
+ return element
85
+ end
86
+ end
87
+ else
88
+ original_size = fl_config.elements.size
89
+ remaining_elements = fl_config.elements.reject do |e|
90
+ element == e
91
+ end
92
+ fl_config.elements = remaining_elements
93
+ if original_size == fl_config.elements.size
94
+ return nil
95
+ else
96
+ return element
97
+ end
98
+ end
99
+ end
100
+
30
101
  def write_to_file
31
102
  return unless Fluentd.instance
32
103
  Fluentd.instance.agent.config_write formatted
@@ -0,0 +1,38 @@
1
+ class Fluentd
2
+ module Setting
3
+ class FilterGrep
4
+ include Fluentd::Setting::Plugin
5
+
6
+ register_plugin("filter", "grep")
7
+
8
+ def self.initial_params
9
+ {
10
+ }
11
+ end
12
+
13
+ def self.permit_params
14
+ [
15
+ :label,
16
+ :pattern, :log_level, :@log_level,
17
+ { and: {regexp: [:key, :pattern], exclude: [:key, :pattern]} },
18
+ { or: {regexp: [:key, :pattern], exclude: [:key, :pattern]} }
19
+ ]
20
+ end
21
+
22
+ def common_options
23
+ [
24
+ :label,
25
+ :pattern,
26
+ ]
27
+ end
28
+
29
+ def hidden_options
30
+ regexps = (1..20).map {|n| :"regexp#{n}"}
31
+ excludes = (1..20).map {|n| :"exclude#{n}"}
32
+ [
33
+ *regexps, *excludes, :regexp, :exclude, :and, :or
34
+ ]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ class Fluentd
2
+ module Setting
3
+ class FilterParser
4
+ include Fluentd::Setting::Plugin
5
+
6
+ register_plugin("filter", "parser")
7
+
8
+ def self.initial_params
9
+ {
10
+ parse_type: "none",
11
+ parse: {
12
+ "0" => {
13
+ "type" => "none"
14
+ }
15
+ }
16
+ }
17
+ end
18
+
19
+ def common_options
20
+ [
21
+ :label,
22
+ :pattern,
23
+ :key_name,
24
+ ]
25
+ end
26
+
27
+ def hidden_options
28
+ [
29
+ :parse
30
+ ]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ class Fluentd
2
+ module Setting
3
+ class FilterRecordTransformer
4
+ include Fluentd::Setting::Plugin
5
+
6
+ register_plugin("filter", "record_transformer")
7
+
8
+ attribute(:record, :string)
9
+
10
+ def self.initial_params
11
+ {
12
+ }
13
+ end
14
+
15
+ def common_options
16
+ [
17
+ :label,
18
+ :pattern,
19
+ ]
20
+ end
21
+
22
+ def hidden_options
23
+ []
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ class Fluentd
2
+ module Setting
3
+ class FilterStdout
4
+ include Fluentd::Setting::Plugin
5
+
6
+ register_plugin("filter", "stdout")
7
+
8
+ def self.initial_params
9
+ {
10
+ format_type: "stdout",
11
+ format: {
12
+ "0" => {
13
+ "@type" => "stdout",
14
+ "output_type" => "json"
15
+ }
16
+ }
17
+ }
18
+ end
19
+
20
+ def common_options
21
+ [
22
+ :label,
23
+ :pattern,
24
+ ]
25
+ end
26
+
27
+ def hidden_options
28
+ [
29
+ :inject
30
+ ]
31
+ end
32
+ end
33
+ end
34
+ end