saba-webhook-gateway 1.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
+ SHA256:
3
+ metadata.gz: b360d216c14fa19ecc9e6fb814f26bf0a9d8b0f2c07e98073bd74ff27e665cec
4
+ data.tar.gz: b381d002fb7a4c526ce4a00e3026aae881c50c7656f2da73a170cf5d0b7c5b5f
5
+ SHA512:
6
+ metadata.gz: 485b0ba83cdd8b7cc51bff9bd1120ad59212ee46dc55611f0c5cf1fb5dbe341792c814a2be3413d9934619249434cfeb05fec7291e100bb8c5a0fcfc354b48b3
7
+ data.tar.gz: 0eb0f2ac4b7ef20316400481b12c79fa2d3199651d39f98df7460d254787acb7e75a7eb6b5ffea52c488fd99b844ea0922e48bf420fc276ee5a7c72da30ab4b9
data/.rubocop.yml ADDED
@@ -0,0 +1,306 @@
1
+ AllCops:
2
+ Exclude:
3
+ - samples/**/*
4
+ - vendor/**/*
5
+ DisplayCopNames: true
6
+ NewCops: enable
7
+ TargetRubyVersion: 2.7
8
+
9
+ require:
10
+ - rubocop-performance
11
+ - rubocop-rake
12
+
13
+ #### Lint
14
+
15
+ Lint/NestedMethodDefinition:
16
+ Exclude:
17
+ - 'test/*'
18
+
19
+ Lint/AssignmentInCondition:
20
+ Enabled: false
21
+
22
+ Lint/ErbNewArguments:
23
+ Enabled: false
24
+
25
+ # make it enable when supporting only Ruby 3.0 and higher.
26
+ Lint/RedundantDirGlobSort:
27
+ Enabled: false
28
+
29
+ #### Performance
30
+
31
+ Performance/DeletePrefix:
32
+ Enabled: false
33
+
34
+ Performance/StringInclude:
35
+ Enabled: false
36
+
37
+ Performance/CollectionLiteralInLoop:
38
+ Enabled: false
39
+
40
+ Performance/MapCompact:
41
+ Enabled: false
42
+
43
+ #### Style
44
+
45
+ Style/AsciiComments:
46
+ Enabled: false
47
+
48
+ # Use alias_method instead of alias.
49
+ Style/Alias:
50
+ EnforcedStyle: prefer_alias_method
51
+ Enabled: true
52
+
53
+ Style/BarePercentLiterals:
54
+ EnforcedStyle: percent_q
55
+ Enabled: true
56
+
57
+ Style/CommentedKeyword:
58
+ Enabled: false
59
+
60
+ Style/DocumentDynamicEvalDefinition:
61
+ Enabled: false
62
+
63
+ Style/Documentation:
64
+ Enabled: false
65
+
66
+ Style/FormatString:
67
+ EnforcedStyle: sprintf
68
+ Enabled: true
69
+
70
+ Style/FormatStringToken:
71
+ Enabled: false
72
+
73
+ Style/GuardClause:
74
+ Enabled: false
75
+
76
+ Style/IfUnlessModifier:
77
+ Enabled: false
78
+
79
+ Style/MutableConstant:
80
+ Enabled: false
81
+
82
+ Style/ZeroLengthPredicate:
83
+ Enabled: false
84
+
85
+ Style/NumericPredicate:
86
+ EnforcedStyle: comparison
87
+ Enabled: true
88
+
89
+ Style/TernaryParentheses:
90
+ Enabled: false
91
+
92
+ Style/FrozenStringLiteralComment:
93
+ EnforcedStyle: never
94
+ Enabled: true
95
+
96
+ Style/SafeNavigation:
97
+ Enabled: false
98
+
99
+ Style/EmptyMethod:
100
+ EnforcedStyle: expanded
101
+ Enabled: true
102
+
103
+ Style/RescueModifier:
104
+ Enabled: false
105
+
106
+ Style/ClassAndModuleChildren:
107
+ EnforcedStyle: nested
108
+ Enabled: true
109
+
110
+ Style/LineEndConcatenation:
111
+ Enabled: false
112
+
113
+ Style/MethodCallWithArgsParentheses:
114
+ AllowedPatterns:
115
+ - 'require'
116
+ - 'include'
117
+ - 'file'
118
+ - 'task'
119
+ - 'desc'
120
+ - 'info'
121
+ - 'gem'
122
+ - 'exit'
123
+ - 'print'
124
+ - 'puts'
125
+ - 'write'
126
+ - 'warn'
127
+ - 'error'
128
+ - 'error!'
129
+ - 'app_error'
130
+ - 'raise'
131
+ - 'yield'
132
+ - 'source'
133
+ - 'assert'
134
+ - 'assert_equal'
135
+ - 'assert_raise'
136
+ - 'assert_raises'
137
+ - 'assert_nothing_raised'
138
+ - 'sh'
139
+ - 'mv'
140
+ - 'rm_rf'
141
+ Enabled: true
142
+
143
+ Style/MixinUsage:
144
+ Enabled: false
145
+
146
+ Style/NumericLiterals:
147
+ MinDigits: 6
148
+
149
+ Style/PercentQLiterals:
150
+ EnforcedStyle: upper_case_q
151
+ Enabled: true
152
+
153
+ Style/PerlBackrefs:
154
+ Enabled: false
155
+
156
+ Style/RaiseArgs:
157
+ EnforcedStyle: exploded
158
+ Enabled: true
159
+
160
+ Style/RedundantBegin:
161
+ Enabled: false
162
+
163
+ Style/RedundantReturn:
164
+ Enabled: false
165
+
166
+ Style/RedundantSelf:
167
+ Enabled: false
168
+
169
+ Style/StderrPuts:
170
+ Enabled: false
171
+
172
+ Style/WordArray:
173
+ MinSize: 10
174
+
175
+ Style/RedundantPercentQ:
176
+ Enabled: false
177
+
178
+ Style/WhileUntilModifier:
179
+ Enabled: false
180
+
181
+ Style/SlicingWithRange:
182
+ Enabled: false
183
+
184
+ Style/AccessorGrouping:
185
+ Enabled: false
186
+
187
+ Style/CaseLikeIf:
188
+ Enabled: false
189
+
190
+ Style/StringConcatenation:
191
+ Enabled: false
192
+
193
+ Style/OptionalBooleanParameter:
194
+ Enabled: false
195
+
196
+ Style/CombinableLoops:
197
+ Enabled: false
198
+
199
+ Style/FetchEnvVar:
200
+ Enabled: false
201
+
202
+ ### Layout
203
+
204
+ Layout/BlockAlignment:
205
+ Enabled: false
206
+
207
+ Layout/ClosingHeredocIndentation:
208
+ Enabled: false
209
+
210
+ Layout/DotPosition:
211
+ EnforcedStyle: trailing
212
+ Enabled: true
213
+
214
+ Layout/HeredocIndentation:
215
+ Enabled: false
216
+
217
+ Layout/SpaceAroundBlockParameters:
218
+ EnforcedStyleInsidePipes: no_space
219
+ Enabled: true
220
+
221
+ Layout/SpaceInsideStringInterpolation:
222
+ EnforcedStyle: no_space
223
+ Enabled: true
224
+
225
+ Layout/TrailingWhitespace:
226
+ Enabled: true
227
+ AllowInHeredoc: true
228
+
229
+ Layout/LineLength:
230
+ Max: 256
231
+ Exclude:
232
+ - "test/**/*"
233
+
234
+ #### Metrics
235
+
236
+ Metrics/BlockNesting:
237
+ Max: 4
238
+
239
+ Metrics/ModuleLength:
240
+ Max: 300
241
+
242
+ Metrics/ParameterLists:
243
+ Max: 8
244
+
245
+ Metrics/PerceivedComplexity:
246
+ Max: 28
247
+
248
+ Metrics/ClassLength:
249
+ Max: 1500
250
+ Exclude:
251
+ - 'test/*.rb'
252
+
253
+ ### should be < 50
254
+ Metrics/MethodLength:
255
+ Max: 100
256
+ Exclude:
257
+ - "bin/review-*"
258
+ - "bin/review2*"
259
+ - "test/**/*"
260
+
261
+ ### should be < 20
262
+ Metrics/CyclomaticComplexity:
263
+ Max: 25
264
+
265
+ ### should be < 60
266
+ Metrics/AbcSize:
267
+ Max: 120
268
+ Exclude:
269
+ - "bin/review-*"
270
+ - "test/**/*"
271
+
272
+ ### shoud be < 25
273
+ Metrics/BlockLength:
274
+ CountComments: false # count full line comments?
275
+ Max: 62
276
+ Exclude:
277
+ - 'Rakefile'
278
+ - '**/*.rake'
279
+ - 'test/**/*.rb'
280
+
281
+ ## Naming
282
+
283
+ Naming/FileName:
284
+ Enabled: false
285
+
286
+ Naming/HeredocDelimiterNaming:
287
+ Enabled: false
288
+
289
+ Naming/MethodParameterName:
290
+ Enabled: false
291
+
292
+ Naming/VariableNumber:
293
+ EnforcedStyle: normalcase
294
+ Enabled: true
295
+ Exclude:
296
+ - test/*
297
+
298
+ #### Security
299
+
300
+ #### Gemspec
301
+
302
+ Gemspec/RequiredRubyVersion:
303
+ Enabled: false
304
+
305
+ Gemspec/DevelopmentDependencies:
306
+ EnforcedStyle: gemspec
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile-lambda ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'faraday'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Kenshi Muto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # Saba Webhook Gateway
2
+
3
+ ![](./saba-webhook-gateway.png)
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/saba-webhook-gateway.svg)](https://rubygems.org/gems/saba-webhook-gateway)
6
+
7
+ Saba Webhook Gatewayは、監視サービス[Mackerel](https://ja.mackerel.io)のWebhookを受け取り、ほかの形式のWebhookに変換して引き渡すゲートウェイのライブラリです。
8
+
9
+ 以下のMackerelのWebhook通知に対応します。
10
+
11
+ - サンプル(Mackerelの「テスト」で送出されるもの)
12
+ - アラート通知
13
+ - アラートグループ通知
14
+ - ホストステータス変更
15
+ - ホスト登録
16
+ - ホスト退役
17
+ - 監視ルールの操作(追加・変更・削除)
18
+
19
+ ## Google Chatでの実行
20
+
21
+ 実装のリファレンスとして、Google Chat Webhook変換を用意しています。各通知をGoogle Chatに送信できます(ただし、現時点でGoogle ChatのWebhookは有償プランのみでしか使えません)。
22
+
23
+ ![Google Chatでの通知表示](./googlechat.png)
24
+
25
+ ### Sinatraを使う場合
26
+
27
+ 以下のようにGoogle ChatスペースのWebhook URLを指定した`.env`ファイルを用意し、本Gitリポジトリを展開しているフォルダに配置します。
28
+
29
+ ```
30
+ GOOGLECHAT_WEBHOOK=https://chat.googleapis.com/v1/spaces/…
31
+ ```
32
+
33
+ Sinatra gemをインストールします。
34
+
35
+ ```
36
+ $ gem install sinatra
37
+ ```
38
+
39
+ Sinatraのサーバーを起動します。
40
+
41
+ ```
42
+ $ exe/webhook-handler-googlechat.rb
43
+ ```
44
+
45
+ デフォルトでは全IPアドレスにバインドした状態でTCPポート4567、パス`/`で待ち受けます。変更したいときには`.env`ファイルで`SERVER_BIND`・`SERVER_PORT`・`SERVER_PATH`を指定してください。
46
+
47
+ 用意したこのサーバーに対してMackerelから送信するようチャンネルを設定します。インターネット経由でアクセスできる必要があるため、ngrokやWebサーバーのプロキシを使ってアクセスできるようにしておきましょう。
48
+
49
+ Mackerelの通知チャンネルの追加で「Webhook」を選び、URLにインターネット経由でアクセスされるURLを指定します。作成されたWebhookチャンネルで「テスト」をクリックすると、サンプルの通知がGoogle Chatに飛びます。
50
+
51
+ ### AWS Lambdaでの実行
52
+
53
+ AWS LambdaのRubyランタイムと関数URLを使ってサーバーレスで実行することもできます。
54
+
55
+ まず、アップロードするsaba-webhook-gateway-googlechat.zipファイルを作成します。
56
+
57
+ ```
58
+ $ ./make-lambda-zip.sh
59
+ ```
60
+
61
+ この作成シェルスクリプトはDebian bookworm用に作っており、Ruby 3.1で用意されるフォルダ名をLambdaのRuby 3.2用に変更しています。ほかのOSやバージョンの場合は適宜変更してください。
62
+
63
+ AWSでLambdaを用意します。
64
+
65
+ - 一から作成
66
+ - 関数名: 任意
67
+ - ランタイム: 「Ruby 3.2」
68
+ - アーキテクチャ: 任意
69
+ - デフォルトの実行ロールの変更: 環境に応じて
70
+ - 詳細設定の 関数URLを有効化: チェック
71
+ - 認証タイプ: NONE(※パブリックなエンドポイントになることに注意)
72
+ - 呼び出しモード: BUFFERED(デフォルト)
73
+ - オリジン間リソース共有(CORS)を設定: 任意
74
+
75
+ 「コード」タブの「コードソース」の「アップロード元」からsaba-webhook-gateway-googlechat.zipファイルをアップロードします。
76
+
77
+ 下にスクロールして「ランタイム設定」に進み、「編集」をクリックしてハンドラを以下のとおり変更します。
78
+
79
+ ```
80
+ exe/lambda-handler-googlechat.lambda_handler
81
+ ```
82
+
83
+ 次に「コード」に戻り、「設定」タブをクリックして、「環境変数」を選びます。「環境変数の追加」をクリックし、キーに `GOOGLECHAT_WEBHOOK` 、値にGoogle ChatスペースのWebhook URLを指定します。
84
+
85
+ さらに「暗号化の設定」を選び、「転送時の暗号化に使用するヘルパーの有効化」にチェックします。値の右に「転送時の暗号化」というボタンができるので、これをクリックし、KMSキーを選びます。
86
+
87
+ 「実行ロールポリシー」に復号のためにLambdaの実行IAMロールに追加する必要のあるポリシーが示されるので、これをIAMロールに付加してください。
88
+
89
+ 「関数の概要」にWebhookエンドポイントとなる「関数URL」が表示されているので、Mackerelの通知チャンネルの追加で「Webhook」を選び、URLにその関数URLを指定します。作成されたWebhookチャンネルで「テスト」をクリックすると、サンプルの通知がGoogle Chatに飛びます。
90
+
91
+ ### カスタマイズ
92
+
93
+ メッセージを変更したいときには、`lib/saba-webhook-gateway/googlechat.rb`を直接書き換える方法もありますが、一部だけであればメソッドをオーバーライドするのが簡単です。
94
+
95
+ ```
96
+ # Saba Webhook Gateway Override
97
+ module GoogleChatOverride
98
+ # SabaWebhookGateway::GoogleChat の一部のメソッドの挙動(カード出力など)を変えたいときにはここでメソッドを上書きする
99
+ # sampleメソッドを上書きする例
100
+ def sample(h)
101
+ header = { title: '通知のテスト' }
102
+ widget1 = [{ textParagraph: { text: h[:message] } }]
103
+ sections = [{ widgets: widget1 }]
104
+ googlechat_card(header, sections)
105
+ end
106
+ end
107
+
108
+ module SabaWebhookGateway
109
+ class GoogleChat
110
+ prepend GoogleChatOverride
111
+ end
112
+ end
113
+ ```
114
+
115
+ ## MackerelのWebhook JSONスキーマについて
116
+
117
+ webhooksフォルダに、参考としてMackerelのWebhook JSONのスキーマファイルを入れています。
118
+
119
+ 内部仕様ではなく、出力から導出したものなので、正確性の保証はないことに注意してください。また、スキーマ内部で本来分岐が必要なもの(監視ルールの設定など)についても対応はしていません。
120
+
121
+ ## ライセンス
122
+ ```
123
+ MIT License
124
+
125
+ Copyright (c) 2023 Kenshi Muto
126
+
127
+ Permission is hereby granted, free of charge, to any person obtaining a copy
128
+ of this software and associated documentation files (the "Software"), to deal
129
+ in the Software without restriction, including without limitation the rights
130
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
131
+ copies of the Software, and to permit persons to whom the Software is
132
+ furnished to do so, subject to the following conditions:
133
+
134
+ The above copyright notice and this permission notice shall be included in all
135
+ copies or substantial portions of the Software.
136
+
137
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
138
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
139
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
140
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
141
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
142
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
143
+ SOFTWARE.
144
+ ```
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'fileutils'
4
+
5
+ desc 'Check with rubocop'
6
+ task :rubocop do
7
+ begin
8
+ require 'rubocop/rake_task'
9
+ RuboCop::RakeTask.new
10
+ rescue LoadError
11
+ $stderr.puts 'rubocop not found'
12
+ end
13
+ end
14
+
15
+ task default: %i[rubocop]
@@ -0,0 +1,26 @@
1
+ require_relative '../lib/saba-webhook-gateway'
2
+ require 'aws-sdk-kms'
3
+ require 'base64'
4
+
5
+ ENCRYPTED = ENV['GOOGLECHAT_WEBHOOK']
6
+ # Decrypt code should run once and variables stored outside of the function
7
+ # handler so that these are decrypted once per container
8
+ DECRYPTED = Aws::KMS::Client.new.decrypt({
9
+ ciphertext_blob: Base64.decode64(ENCRYPTED),
10
+ encryption_context: { 'LambdaFunctionName' => ENV['AWS_LAMBDA_FUNCTION_NAME'] }
11
+ }).plaintext
12
+ ENV['GOOGLECHAT_WEBHOOK'] = DECRYPTED
13
+
14
+ # rubocop:disable Lint/UnusedMethodArgument
15
+ def lambda_handler(event:, context:)
16
+ m = SabaWebhookGateway::GoogleChat.new
17
+ h = m.parse(JSON.generate(event))
18
+ if h[:body]
19
+ m.run(m.parse(h[:body]))
20
+ elsif h[:event]
21
+ m.run(h)
22
+ end
23
+
24
+ { statusCode: 200, body: JSON.generate('received on Lambda') }
25
+ end
26
+ # rubocop:enable Lint/UnusedMethodArgument
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # Saba Webhook Gateway Handler for Google Chat on Sinatra
3
+ #
4
+ # Copyright 2023 Kenshi Muto <kmuto@kmuto.jp>
5
+ require 'dotenv'
6
+ require 'sinatra'
7
+
8
+ require 'pathname'
9
+ bindir = Pathname.new(__FILE__).realpath.dirname
10
+ $LOAD_PATH.unshift((bindir + '../lib').realpath)
11
+
12
+ require 'saba-webhook-gateway'
13
+
14
+ Dotenv.load
15
+ @bind = ENV['SERVER_BIND'] || '0.0.0.0'
16
+ @port = ENV['SERVER_PORT'] || 4567
17
+ @path = ENV['SERVER_PATH'] || '/'
18
+
19
+ set :bind, @bind
20
+ set :port, @port
21
+
22
+ post @path do
23
+ status 204
24
+ request.body.rewind
25
+ request_json = request.body.read
26
+
27
+ m = SabaWebhookGateway::GoogleChat.new
28
+ h = m.parse(request_json)
29
+ m.run(h) if h
30
+ end
data/googlechat.png ADDED
Binary file