fluent-plugin-redmine 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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