Skip to content

Commit

Permalink
Merge pull request #233 from SomtochiAma/matrix-support
Browse files Browse the repository at this point in the history
Add Matrix alerting provider
  • Loading branch information
stefanprodan authored Aug 26, 2021
2 parents 9ecfb37 + 61103fc commit eb73033
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 1 deletion.
3 changes: 2 additions & 1 deletion api/v1beta1/provider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const (
// ProviderSpec defines the desired state of Provider
type ProviderSpec struct {
// Type of provider
// +kubebuilder:validation:Enum=slack;discord;msteams;rocket;generic;github;gitlab;bitbucket;azuredevops;googlechat;webex;sentry;azureeventhub;telegram;lark
// +kubebuilder:validation:Enum=slack;discord;msteams;rocket;generic;github;gitlab;bitbucket;azuredevops;googlechat;webex;sentry;azureeventhub;telegram;lark;matrix;
// +required
Type string `json:"type"`

Expand Down Expand Up @@ -79,6 +79,7 @@ const (
AzureEventHubProvider string = "azureeventhub"
TelegramProvider string = "telegram"
LarkProvider string = "lark"
Matrix string = "matrix"
)

// ProviderStatus defines the observed state of Provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ spec:
- azureeventhub
- telegram
- lark
- matrix
type: string
username:
description: Bot username for this provider
Expand Down
25 changes: 25 additions & 0 deletions docs/spec/v1beta1/provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,28 @@ spec:
secretRef:
name: lark-token
```

## Matrix
For Matrix, the address is the homeserver URL and the token is the access token returned by a call
to /login or /register

To create secret
```
kubectl create secret generic matrix-token \
--from-literal=token=<access-token> \
--from-literal=address=https://matrix.org # replace with if using a different server
```
`spec.channel` holds the room id.
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: matrix
namespace: flux-system
spec:
type: matrix
channel: "!jezptmDwEeLapMLjOc:matrix.org"
secretRef:
name: matrix-token
```
2 changes: 2 additions & 0 deletions internal/notifier/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func (f Factory) Notifier(provider string) (Interface, error) {
n, err = NewTelegram(f.Channel, f.Token)
case v1beta1.LarkProvider:
n, err = NewLark(f.URL)
case v1beta1.Matrix:
n, err = NewMatrix(f.URL, f.Token, f.Channel)
default:
err = fmt.Errorf("provider %s not supported", provider)
}
Expand Down
82 changes: 82 additions & 0 deletions internal/notifier/matrix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package notifier

import (
"crypto/sha1"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"

"github.com/fluxcd/pkg/runtime/events"
"github.com/hashicorp/go-retryablehttp"
)

type Matrix struct {
Token string
URL string
RoomId string
}

type MatrixPayload struct {
Body string `json:"body"`
MsgType string `json:"msgtype"`
}

func NewMatrix(serverURL, token, roomId string) (*Matrix, error) {
_, err := url.ParseRequestURI(serverURL)
if err != nil {
return nil, fmt.Errorf("invalid Matrix homeserver URL %s", serverURL)
}

return &Matrix{
URL: serverURL,
RoomId: roomId,
Token: token,
}, nil
}

func (m *Matrix) Post(event events.Event) error {
txId, err := sha1sum(event)
if err != nil {
return fmt.Errorf("unable to generate unique tx id: %s", err)
}
fullURL := fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message/%s",
m.URL, m.RoomId, txId)

emoji := "💫"
if event.Severity == events.EventSeverityError {
emoji = "🚨"
}
var metadata string
for k, v := range event.Metadata {
metadata = metadata + fmt.Sprintf("- %s: %s\n", k, v)
}
heading := fmt.Sprintf("%s %s/%s.%s", emoji, strings.ToLower(event.InvolvedObject.Kind),
event.InvolvedObject.Name, event.InvolvedObject.Namespace)
msg := fmt.Sprintf("%s\n%s\n%s", heading, event.Message, metadata)

payload := MatrixPayload{
Body: msg,
MsgType: "m.text",
}

err = postMessage(fullURL, "", nil, payload, func(request *retryablehttp.Request) {
request.Method = http.MethodPut
request.Header.Add("Authorization", "Bearer "+m.Token)
})
if err != nil {
return fmt.Errorf("postMessage failed: %w", err)
}

return nil
}

func sha1sum(event events.Event) (string, error) {
val, err := json.Marshal(event)
if err != nil {
return "", err
}
digest := sha1.Sum(val)
return fmt.Sprintf("%x", digest), nil
}
50 changes: 50 additions & 0 deletions internal/notifier/matrix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package notifier

import (
"testing"
"time"

"github.com/fluxcd/pkg/runtime/events"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestSha1Sum(t *testing.T) {
timestamp, err := time.Parse("Jan 2, 2006 at 3:04pm (WAT)", "Aug 24, 2021 at 4:18pm (WAT)")
if err != nil {
t.Fatalf("unexpected error getting timestamp: %s", err)
}

tests := []struct {
event events.Event
sha1 string
}{
{
event: events.Event{
InvolvedObject: corev1.ObjectReference{},
Severity: events.EventSeverityInfo,
Timestamp: metav1.Time{
Time: timestamp,
},
Message: "update successful",
Reason: "update sucesful",
Metadata: nil,
ReportingController: "",
ReportingInstance: "",
},
sha1: "37d91b4f6a1e44c6a38273b0a0fd408fade7b0f5",
},
}

for _, tt := range tests {
hash, err := sha1sum(tt.event)
if err != nil {
t.Fatalf("unexpected err: %s", err)
}

if tt.sha1 != hash {
t.Errorf("wrong sha1 sum from event %v. expected %q got %q",
tt.event, tt.sha1, hash)
}
}
}

0 comments on commit eb73033

Please sign in to comment.