Skip to content

Commit 1fa6636

Browse files
committed
Introduce Webhook Channel
In its initial form, the webhook channel can be used to send notifications against a HTTP/HTTPS web server with a dynamic configurable URL and request body message.
1 parent a840808 commit 1fa6636

File tree

1 file changed

+148
-0
lines changed

1 file changed

+148
-0
lines changed

cmd/channel/webhook/main.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/icinga/icinga-notifications/internal"
8+
"github.com/icinga/icinga-notifications/pkg/plugin"
9+
"io"
10+
"net/http"
11+
"text/template"
12+
)
13+
14+
type Webhook struct {
15+
Method string `json:"method"`
16+
URLTemplate string `json:"url_template"`
17+
RequestBodyTemplate string `json:"request_body_template"`
18+
19+
tmplUrl *template.Template
20+
tmplRequestBody *template.Template
21+
}
22+
23+
func (ch *Webhook) GetInfo() *plugin.Info {
24+
elements := []*plugin.ConfigOption{
25+
{
26+
Name: "method",
27+
Type: "option",
28+
Label: map[string]string{
29+
"en_US": "HTTP Method",
30+
"de_DE": "HTTP-Methode",
31+
},
32+
Help: map[string]string{
33+
"en_US": "HTTP request method used for the web request.",
34+
"de_DE": "HTTP-Methode für die Anfrage.",
35+
},
36+
Default: "POST",
37+
Required: true,
38+
Options: map[string]string{
39+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
40+
"GET": "GET",
41+
"HEAD": "HEAD",
42+
"POST": "POST",
43+
"PUT": "PUT",
44+
"DELETE": "DELETE",
45+
"CONNECT": "CONNECT",
46+
"OPTIONS": "OPTIONS",
47+
"TRACE": "TRACE",
48+
"PATCH": "PATCH",
49+
},
50+
},
51+
{
52+
Name: "url_template",
53+
Type: "string",
54+
Label: map[string]string{
55+
"en_US": "URL Template",
56+
"de_DE": "URL-Template",
57+
},
58+
Help: map[string]string{
59+
"en_US": "URL, optionally as a Go template over the current plugin.NotificationRequest.",
60+
"de_DE": "URL, optional als Go-Template über das zu verarbeitende plugin.NotificationRequest.",
61+
},
62+
Required: true,
63+
},
64+
{
65+
Name: "request_body_template",
66+
Type: "string",
67+
Label: map[string]string{
68+
"en_US": "Request Body Template",
69+
"de_DE": "Anfragedaten-Template",
70+
},
71+
Help: map[string]string{
72+
"en_US": "Go template applied to the current plugin.NotificationRequest to create an request body.",
73+
"de_DE": "Go-Template über das zu verarbeitende plugin.NotificationRequest zum Erzeugen der mitgesendeten Anfragedaten.",
74+
},
75+
// Default as a minimal working example to send a JSON object.
76+
Default: `{"id": {{.Incident.Id}}, "type": "{{.Event.Type}}", "name": "{{.Object.Name}}", "severity": "{{.Incident.Severity}}", "url": "{{.Incident.Url}}"}`,
77+
},
78+
}
79+
80+
configAttrs, err := json.Marshal(elements)
81+
if err != nil {
82+
panic(err)
83+
}
84+
85+
return &plugin.Info{
86+
Name: "Webhook",
87+
Version: internal.Version.Version,
88+
Author: "Icinga GmbH",
89+
ConfigAttributes: configAttrs,
90+
}
91+
}
92+
93+
func (ch *Webhook) SetConfig(jsonStr json.RawMessage) error {
94+
err := json.Unmarshal(jsonStr, ch)
95+
if err != nil {
96+
return err
97+
}
98+
99+
tmplFuncs := template.FuncMap{
100+
"json": func(a any) string {
101+
var buff bytes.Buffer
102+
if err := json.NewEncoder(&buff).Encode(a); err != nil {
103+
panic(err)
104+
}
105+
return buff.String()
106+
},
107+
}
108+
109+
ch.tmplUrl, err = template.New("url").Funcs(tmplFuncs).Parse(ch.URLTemplate)
110+
if err != nil {
111+
return fmt.Errorf("cannot parse URL template: %w", err)
112+
}
113+
114+
ch.tmplRequestBody, err = template.New("request_body").Funcs(tmplFuncs).Parse(ch.RequestBodyTemplate)
115+
if err != nil {
116+
return fmt.Errorf("cannot parse Request Body template: %w", err)
117+
}
118+
119+
return nil
120+
}
121+
122+
func (ch *Webhook) SendNotification(req *plugin.NotificationRequest) error {
123+
var urlBuff, reqBodyBuff bytes.Buffer
124+
if err := ch.tmplUrl.Execute(&urlBuff, req); err != nil {
125+
return fmt.Errorf("cannot execute URL template: %w", err)
126+
}
127+
if err := ch.tmplRequestBody.Execute(&reqBodyBuff, req); err != nil {
128+
return fmt.Errorf("cannot execute Request Body template: %w", err)
129+
}
130+
131+
httpReq, err := http.NewRequest(ch.Method, urlBuff.String(), &reqBodyBuff)
132+
if err != nil {
133+
return err
134+
}
135+
httpResp, err := http.DefaultClient.Do(httpReq)
136+
if err != nil {
137+
return err
138+
}
139+
140+
_, _ = io.Copy(io.Discard, httpResp.Body)
141+
_ = httpResp.Body.Close()
142+
143+
return nil
144+
}
145+
146+
func main() {
147+
plugin.RunPlugin(&Webhook{})
148+
}

0 commit comments

Comments
 (0)