Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/Manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ EaseProbe has the following major modules:
- [2.11 Log](#211-log)
- [2.12 Shell](#212-shell)
- [2.13 RingCentral](#213-ringcentral)
- [2.14 HTTP](#214-http)
- [3. Report](#3-report)
- [3.1 SLA Report Notification](#31-sla-report-notification)
- [3.2 SLA Live Report](#32-sla-live-report)
Expand Down Expand Up @@ -1194,6 +1195,32 @@ notify:
webhook: "https://hooks.ringcentral.com/webhook/v2/.........."
```

## 2.14 HTTP
This notification method sends notifications as HTTP POST requests to a specified endpoint.

The plugin supports the following parameters:
- `name`: A unique name for this notification endpoint
- `url`: The HTTP endpoint URL to send notifications to
- `success_status`: The expected HTTP status code for successful delivery
- `headers`: Optional headers to include in the request (see example below)

`Note`: The http method is always `POST`, and the body of the request will contain the notification data

Example:
```YAML
# Notification Configuration
notify:
http:
- name: "Custom HTTP Notify"
url: "https://api.example.com/notify"
success_status: 201
headers:
- name: "Content-Type"
value: "application/json"
- name: "Authorization"
value: "Bearer token123"
```

# 3. Report

## 3.1 SLA Report Notification
Expand Down
87 changes: 87 additions & 0 deletions notify/http/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Package http is the HTTP notification
package http

import (
"bytes"
"fmt"
"io"
"net/http"

"github.com/megaease/easeprobe/global"
"github.com/megaease/easeprobe/notify/base"
"github.com/megaease/easeprobe/report"
log "github.com/sirupsen/logrus"
)

// NotifyConfig is the HTTP notification configuration
type NotifyConfig struct {
base.DefaultNotify `yaml:",inline"`

URL string `yaml:"url" json:"url,omitempty" jsonschema:"title=HTTP URL,description=The HTTP endpoint to send notifications"`
SuccessStatus int `yaml:"success_status" json:"success_status,omitempty" jsonschema:"title=Success Status,description=The success status code of the HTTP request"`
Headers []Header `yaml:"headers" json:"headers,omitempty" jsonschema:"title=HTTP Headers,description=Custom headers for the HTTP request"`
}

// Header represents HTTP header for HTTP notification
type Header struct {
Name string `yaml:"name" json:"name,omitempty" jsonschema:"title=Header Name,description=The name of the header to send"`
Value string `yaml:"value" json:"value,omitempty" jsonschema:"title=Header Value,description=The value of the header to send"`
}

// Config configures the HTTP notification
func (c *NotifyConfig) Config(gConf global.NotifySettings) error {
c.NotifyKind = "http"
c.NotifyFormat = report.Markdown
c.NotifySendFunc = c.SendHTTP
c.DefaultNotify.Config(gConf)
log.Debugf("Notification [%s] - [%s] configuration: %+v", c.NotifyKind, c.NotifyName, c)
return nil
}

// SendHTTP sends the HTTP notification
func (c *NotifyConfig) SendHTTP(title, text string) error {
req, err := http.NewRequest(http.MethodPost, c.URL, bytes.NewBuffer([]byte(text)))
if err != nil {
return err
}
req.Close = true
for _, h := range c.Headers {
req.Header.Set(h.Name, h.Value)
}
req.Header.Set("User-Agent", "EaseProbe")

client := &http.Client{Timeout: c.Timeout}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

log.Debugf("[%s] - [%s] sending notification to %s", c.Kind(), c.Name(), c.URL)

buf, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != c.SuccessStatus {
return fmt.Errorf("Error response from HTTP - code [%d] - msg [%s]", resp.StatusCode, string(buf))
}
return nil
}
93 changes: 93 additions & 0 deletions notify/http/http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package http

import (
"errors"
"io"
"net/http"
"reflect"
"strings"
"testing"

"github.com/megaease/easeprobe/global"
"github.com/megaease/easeprobe/monkey"
"github.com/stretchr/testify/assert"
)

func assertError(t *testing.T, err error, msg string) {
t.Helper()
assert.Error(t, err)
assert.Equal(t, msg, err.Error())
}

func TestHTTPNotify(t *testing.T) {
conf := &NotifyConfig{
URL: "http://example.com/notify",
SuccessStatus: 200,
Headers: []Header{
{Name: "Content-Type", Value: "application/json"},
},
}
conf.NotifyName = "dummy"
err := conf.Config(global.NotifySettings{})
assert.NoError(t, err)
assert.Equal(t, "http", conf.Kind())
assert.Equal(t, "application/json", conf.Headers[0].Value)

var client http.Client
monkey.PatchInstanceMethod(reflect.TypeOf(&client), "Do", func(_ *http.Client, req *http.Request) (*http.Response, error) {
r := io.NopCloser(strings.NewReader(`ok`))
return &http.Response{
StatusCode: 200,
Body: r,
}, nil
})
err = conf.SendHTTP("title", "message")
assert.NoError(t, err)

monkey.PatchInstanceMethod(reflect.TypeOf(&client), "Do", func(_ *http.Client, req *http.Request) (*http.Response, error) {
r := io.NopCloser(strings.NewReader(`error`))
return &http.Response{
StatusCode: 500,
Body: r,
}, nil
})
err = conf.SendHTTP("title", "message")
assertError(t, err, "Error response from HTTP - code [500] - msg [error]")

monkey.Patch(io.ReadAll, func(_ io.Reader) ([]byte, error) {
return nil, errors.New("read error")
})
err = conf.SendHTTP("title", "message")
assertError(t, err, "read error")

monkey.PatchInstanceMethod(reflect.TypeOf(&client), "Do", func(_ *http.Client, req *http.Request) (*http.Response, error) {
return nil, errors.New("http do error")
})
err = conf.SendHTTP("title", "message")
assertError(t, err, "http do error")

monkey.Patch(http.NewRequest, func(method string, url string, body io.Reader) (*http.Request, error) {
return nil, errors.New("new request error")
})
err = conf.SendHTTP("title", "message")
assertError(t, err, "new request error")

monkey.UnpatchAll()
}
2 changes: 2 additions & 0 deletions notify/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/megaease/easeprobe/notify/dingtalk"
"github.com/megaease/easeprobe/notify/discord"
"github.com/megaease/easeprobe/notify/email"
"github.com/megaease/easeprobe/notify/http"
"github.com/megaease/easeprobe/notify/lark"
"github.com/megaease/easeprobe/notify/log"
"github.com/megaease/easeprobe/notify/ringcentral"
Expand Down Expand Up @@ -51,6 +52,7 @@ type Config struct {
Teams []teams.NotifyConfig `yaml:"teams,omitempty" json:"teams,omitempty" jsonschema:"title=Teams Notification,description=Teams Notification Configuration"`
Shell []shell.NotifyConfig `yaml:"shell,omitempty" json:"shell,omitempty" jsonschema:"title=Shell Notification,description=Shell Notification Configuration"`
RingCentral []ringcentral.NotifyConfig `yaml:"ringcentral,omitempty" json:"ringcentral,omitempty" jsonschema:"title=RingCentral Notification,description=RingCentral Notification Configuration"`
HTTP []http.NotifyConfig `yaml:"http,omitempty" json:"http,omitempty" jsonschema:"title=HTTP Notification,description=HTTP Notification Configuration"`
}

// Notify is the configuration of the Notify
Expand Down
Loading