fluent-plugin-redmine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a74312227f9285179b5f863968c01f87b2a838f3
4
+ data.tar.gz: 8ebda1d917fe773ad253643a1cbe10a5dc8cf269
5
+ SHA512:
6
+ metadata.gz: 9399d0e446fbb7a11e3ec429230a7e02a946fae7334658e66567c45855b2d87a8edc37a2a3c7a9852b0238be87ca15358c1c7585617645c89509bd8e55174484
7
+ data.tar.gz: 9e51506a72aa1ff0b2cbc6505abf30d414a32c6e15e2cbb693c57701245e4a0f482689ad75bf5b5a6a7c53673658ea39eb5cd6fe51943a0a789cc2c219f67cf7
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-redmine.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012- Takuma Kanari
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # fluent-plugin-redmine
2
+
3
+ ## Overview
4
+
5
+ Output fplugin for [Fluentd](http://fluentd.org). Create and regist a ticket to redmine from messages.
6
+
7
+ ## Installation
8
+
9
+ Install with `gem`, `fluent-gem` or `td-agent-gem` command as:
10
+
11
+ ```
12
+ # for system installed fluentd
13
+ $ gem install fluent-plugin-redmine
14
+
15
+ # for td-agent
16
+ $ sudo /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-redmine
17
+
18
+ # for td-agent2
19
+ $ sudo td-agent-gem install fluent-plugin-redmine
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ In your fluentd configration, use type redmine.
25
+
26
+ Here is example settings:
27
+
28
+ type redmine
29
+ url http://localhost:3000/
30
+ api_key 40a96d43a98b1626c542b04c5780f881c1e1a969
31
+ project_id apitest
32
+ tracker_id 1
33
+ priority_id 3
34
+ subject The new issue %{issue}
35
+ description This is the new issue called %{name}. we cought new exception %{error}!
36
+
37
+
38
+ and here is optional configuration:
39
+
40
+ tag_key my_tag # 'tag' is used by default
41
+
42
+ ### placeholders
43
+
44
+ You can look values in records by using `%{hoge}` for subject and description. If you specify *tag_key* in configuration, you can also look tag value by `%{your_specified_tag_key}`.
45
+
46
+ ## TODO
47
+
48
+ Pull requests are very welcome!!
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fluent-plugin-redmine"
7
+ spec.version = "0.1.0"
8
+ spec.authors = ["Takuma kanari"]
9
+ spec.email = ["chemtrails.t@gmail.com"]
10
+ spec.summary = %q{Output filter plugin to create ticket in redmine}
11
+ spec.description = %q{Output filter plugin to create ticket in redmine}
12
+ spec.homepage = "https://github.com/takumakanari"
13
+ spec.rubyforge_project = "fluent-plugin-redmine"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "rake"
21
+ spec.add_development_dependency "fluentd"
22
+ end
@@ -0,0 +1,127 @@
1
+ module Fluent
2
+
3
+ class RedmineOutput < Output
4
+ Fluent::Plugin.register_output('redmine', self)
5
+
6
+ # Define `log` method for v0.10.42 or earlier
7
+ unless method_defined?(:log)
8
+ define_method("log") { $log }
9
+ end
10
+
11
+ config_param :url, :string, :default => nil
12
+ config_param :api_key, :string, :default => nil
13
+ config_param :tag_key, :string, :default => "tag"
14
+ config_param :project_id, :string, :default => nil
15
+ config_param :tracker_id, :integer
16
+ config_param :priority_id, :integer
17
+ config_param :subject, :string, :default => "Fluent::RedmineOutput plugin"
18
+ config_param :description, :string, :default => ""
19
+
20
+ def initialize
21
+ super
22
+ require "json"
23
+ require "net/http"
24
+ end
25
+
26
+ def configure(conf)
27
+ super
28
+
29
+ if @url.nil?
30
+ raise Fluent::ConfigError, "'url' must be specified."
31
+ end
32
+
33
+ if @api_key.nil?
34
+ raise Fluent::ConfigError, "'api_key' must be specified."
35
+ end
36
+
37
+ @subject_expander = TemplateExpander.new(@subject)
38
+ @description_expander = TemplateExpander.new(@description)
39
+ @redmine_uri = URI.parse("#{@url}/issues.json")
40
+
41
+ @redmine_request_header = {
42
+ "Content-Type" => "application/json",
43
+ "X-Redmine-API-Key" => @api_key
44
+ }
45
+ end
46
+
47
+ def emit(tag, es, chain)
48
+ bulk = []
49
+ es.each do |time,record|
50
+ bulk << [
51
+ @subject_expander.bind(make_record(tag, record)),
52
+ @description_expander.bind(make_record(tag, record))
53
+ ]
54
+ end
55
+ bulk.each do |subject, desc|
56
+ begin
57
+ submit_ticket(subject, desc)
58
+ rescue => e
59
+ log.warn "out_redmine: failed to create ticket to #{@redmine_uri}, subject: #{subject}, description: #{desc}, error_class: #{e.class}, error_message: #{e.message}, error_backtrace: #{e.backtrace.first}"
60
+ end
61
+ end
62
+ chain.next
63
+ end
64
+
65
+ def submit_ticket(subject, desc)
66
+ request = Net::HTTP::Post.new(
67
+ @redmine_uri.request_uri,
68
+ initheader = @redmine_request_header
69
+ )
70
+ request.body = JSON.generate(make_payload(subject, desc))
71
+ Net::HTTP.new(@redmine_uri.host, @redmine_uri.port).start do |http|
72
+ res = http.request(request)
73
+ unless res.code.to_i == 201
74
+ raise Exception.new("Error: #{res.code}, #{res.body}")
75
+ end
76
+ return res.body
77
+ end
78
+ end
79
+
80
+ def make_payload(subject, desc)
81
+ {
82
+ :issue => {
83
+ :project_id => @project_id,
84
+ :tracker_id => @tracker_id,
85
+ :priority_id => @priority_id,
86
+ :subject => subject,
87
+ :description => desc
88
+ }
89
+ }
90
+ end
91
+
92
+ private
93
+
94
+ def make_record(tag, record)
95
+ dest = Hash.new
96
+ dest[:"#{@tag_key}"] = tag
97
+ record.map do |key, value|
98
+ dest[:"#{key}"] = value
99
+ end
100
+ dest
101
+ end
102
+
103
+ class TemplateExpander
104
+ attr_reader :template, :placeholders
105
+
106
+ Empty = ''
107
+
108
+ def initialize(template)
109
+ @template = template
110
+ @placeholders = Array.new
111
+ @template.gsub(/%{([^}]+)}/) do
112
+ @placeholders << $1 unless @placeholders.include?($1)
113
+ end
114
+ end
115
+
116
+ def bind(values)
117
+ @placeholders.each do |key|
118
+ key_ = :"#{key}"
119
+ values[key_] = Empty unless values.key?(key_)
120
+ end
121
+ @template % values
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'fluent/test'
15
+ unless ENV.has_key?('VERBOSE')
16
+ nulllogger = Object.new
17
+ nulllogger.instance_eval {|obj|
18
+ def method_missing(method, *args)
19
+ # pass
20
+ end
21
+ }
22
+ $log = nulllogger
23
+ end
24
+
25
+ require 'fluent/plugin/out_redmine'
26
+
27
+ class Test::Unit::TestCase
28
+ end
@@ -0,0 +1,174 @@
1
+ require "helper"
2
+ require "webrick"
3
+ require "thread"
4
+ require "net/http"
5
+ require "uri"
6
+ require "fluent/plugin/out_redmine"
7
+
8
+
9
+ class RedmineOutputTest < Test::Unit::TestCase
10
+
11
+ CONFIG_DEFAULT = %[
12
+ type redmine
13
+ url http://localhost:4000
14
+ api_key test-api-key
15
+ tag_key test
16
+ project_id 1
17
+ tracker_id 2
18
+ priority_id 3
19
+ subject awesome
20
+ description this is description %{d1} - %{d2} - %{d3}
21
+ ]
22
+
23
+ CONFIG_TO_FORMAT = %[
24
+ type redmine
25
+ url http://localhost:4000
26
+ api_key test-api-key
27
+ project_id 1
28
+ tracker_id 2
29
+ priority_id 3
30
+ subject %{tag}: awesome %{name}, %{age}, %{message}, unknown:%{unknown}
31
+ description %{tag}: this is description %{name}, %{age}, %{message}, unknown:%{unknown}
32
+ ]
33
+
34
+ def setup
35
+ Fluent::Test.setup
36
+ boot_dummy_redmine_server
37
+ end
38
+
39
+ def boot_dummy_redmine_server
40
+ @tickets = []
41
+ @dummy_redmine = Thread.new do
42
+ s = WEBrick::HTTPServer.new({
43
+ :BindAddress => "127.0.0.1",
44
+ :Port => 4000,
45
+ :DoNotReverseLookup => true
46
+ })
47
+ begin
48
+ s.mount_proc("/") do |req, res|
49
+ unless req.request_method == "POST"
50
+ res.status = 405
51
+ res.body = "request method mismatch"
52
+ next
53
+ end
54
+ if req.path == "/"
55
+ res.status = 200
56
+ elsif req.path == "/issues.json"
57
+ @tickets << JSON.parse(req.body)
58
+ res.status = 201
59
+ else
60
+ res.status = 404
61
+ next
62
+ end
63
+ res.body = "OK"
64
+ end
65
+ s.start
66
+ ensure
67
+ s.shutdown
68
+ end
69
+ end
70
+
71
+ cv = ConditionVariable.new
72
+
73
+ watcher = Thread.new {
74
+ connected = false
75
+ while not connected
76
+ begin
77
+ client = Net::HTTP.start("localhost", 4000)
78
+ connected = client.request_post("/", "").code.to_i == 200
79
+ puts connected
80
+ rescue Errno::ECONNREFUSED
81
+ sleep 0.1
82
+ rescue StandardError => e
83
+ p e
84
+ sleep 0.1
85
+ end
86
+ end
87
+ cv.signal
88
+ }
89
+ mutex = Mutex.new
90
+ mutex.synchronize {
91
+ cv.wait(mutex)
92
+ }
93
+ end
94
+
95
+ def teardown
96
+ @dummy_redmine.kill
97
+ @dummy_redmine.join
98
+ end
99
+
100
+ def create_driver(conf=CONFIG_OUT_KEYS,tag='test')
101
+ Fluent::Test::OutputTestDriver.new(Fluent::RedmineOutput, tag).configure(conf)
102
+ end
103
+
104
+ def test_configure
105
+ p = nil
106
+ assert_nothing_raised { p = create_driver(CONFIG_DEFAULT).instance }
107
+ assert_equal "http://localhost:4000", p.url
108
+ assert_equal "test", p.tag_key
109
+ assert_equal "1", p.project_id
110
+ assert_equal 2, p.tracker_id
111
+ assert_equal 3, p.priority_id
112
+ assert_equal "awesome", p.subject
113
+ assert_equal "this is description %{d1} - %{d2} - %{d3}", p.description
114
+ end
115
+
116
+ def test_configure_fail_by_url
117
+ assert_raise(Fluent::ConfigError){ create_driver(<<CONFIG) }
118
+ type redmine
119
+ api_key test-api-key
120
+ project_id 1
121
+ tracker_id 2
122
+ priority_id 3
123
+ subject awesome
124
+ description this is description
125
+ CONFIG
126
+ end
127
+
128
+ def test_configure_fail_by_api_key
129
+ assert_raise(Fluent::ConfigError){ create_driver(<<CONFIG) }
130
+ type redmine
131
+ url http://localhost:4000
132
+ project_id 1
133
+ tracker_id 2
134
+ priority_id 3
135
+ subject awesome
136
+ description this is description
137
+ CONFIG
138
+ end
139
+
140
+ def test_make_payload
141
+ p = create_driver(CONFIG_DEFAULT).instance
142
+ ret = p.make_payload("subject", "description")
143
+ assert_equal "subject", ret[:issue][:subject]
144
+ assert_equal "description", ret[:issue][:description]
145
+ assert_equal p.project_id, ret[:issue][:project_id]
146
+ assert_equal p.tracker_id, ret[:issue][:tracker_id]
147
+ assert_equal p.priority_id, ret[:issue][:priority_id]
148
+ end
149
+
150
+ def test_emit
151
+ d = create_driver(CONFIG_TO_FORMAT)
152
+ record = {
153
+ "name" => "John",
154
+ "age" => 25,
155
+ "message" => "this is message!"
156
+ }
157
+ t = Time.now
158
+ d.run do
159
+ d.emit(record, t.to_i)
160
+ end
161
+
162
+ assert_equal @tickets.size, 1
163
+
164
+ issue = @tickets.first["issue"]
165
+
166
+ assert_equal "1", issue["project_id"]
167
+ assert_equal 2, issue["tracker_id"]
168
+ assert_equal 3, issue["priority_id"]
169
+ assert_equal "test: awesome John, 25, this is message!, unknown:", issue["subject"]
170
+ assert_equal "test: this is description John, 25, this is message!, unknown:", issue["description"]
171
+ end
172
+
173
+ end
174
+
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-redmine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Takuma kanari
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fluentd
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Output filter plugin to create ticket in redmine
42
+ email:
43
+ - chemtrails.t@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - fluent-plugin-redmine.gemspec
54
+ - lib/fluent/plugin/out_redmine.rb
55
+ - test/helper.rb
56
+ - test/plugin/test_out_redmine.rb
57
+ homepage: https://github.com/takumakanari
58
+ licenses: []
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project: fluent-plugin-redmine
76
+ rubygems_version: 2.2.2
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Output filter plugin to create ticket in redmine
80
+ test_files:
81
+ - test/helper.rb
82
+ - test/plugin/test_out_redmine.rb