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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +48 -0
- data/Rakefile +11 -0
- data/fluent-plugin-redmine.gemspec +22 -0
- data/lib/fluent/plugin/out_redmine.rb +127 -0
- data/test/helper.rb +28 -0
- data/test/plugin/test_out_redmine.rb +174 -0
- metadata +82 -0
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
data/Gemfile
ADDED
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,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
|