Skip to content

Commit

Permalink
Merge pull request #236 from SomtochiAma/lark-notification
Browse files Browse the repository at this point in the history
Add Lark alerting provider
  • Loading branch information
stefanprodan authored Aug 26, 2021
2 parents dc0d0da + f95cb2a commit 9ecfb37
Show file tree
Hide file tree
Showing 6 changed files with 170 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
// +kubebuilder:validation:Enum=slack;discord;msteams;rocket;generic;github;gitlab;bitbucket;azuredevops;googlechat;webex;sentry;azureeventhub;telegram;lark
// +required
Type string `json:"type"`

Expand Down Expand Up @@ -78,6 +78,7 @@ const (
SentryProvider string = "sentry"
AzureEventHubProvider string = "azureeventhub"
TelegramProvider string = "telegram"
LarkProvider string = "lark"
)

// ProviderStatus defines the observed state of Provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ spec:
- sentry
- azureeventhub
- telegram
- lark
type: string
username:
description: Bot username for this provider
Expand Down
22 changes: 22 additions & 0 deletions docs/spec/v1beta1/provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,25 @@ To create the needed secret:
kubectl create secret generic webhook-url \
--from-literal=address="Endpoint=sb://fluxv2.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=yoursaskeygeneatedbyazure"
```

## Lark

For sending notifications to Lark, You will have to [add a bot to the group](https://www.larksuite.com/hc/en-US/articles/360048487736-Bot-Use-bots-in-groups#III.%20How%20to%20configure%20custom%20bots%20in%20a%20group%C2%A0)
and set up a webhook for the bot. This serves as the address field in the secret
```shell
kubectl create secret generic lark-token \
--from-literal=address=<lark-webhook-url>
```

Then reference the secret in `spec.secretRef`:
```yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: lark
namespace: flux-system
spec:
type: lark
secretRef:
name: lark-token
```
2 changes: 2 additions & 0 deletions internal/notifier/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ func (f Factory) Notifier(provider string) (Interface, error) {
n, err = NewAzureEventHub(f.URL, f.Token, f.Channel)
case v1beta1.TelegramProvider:
n, err = NewTelegram(f.Channel, f.Token)
case v1beta1.LarkProvider:
n, err = NewLark(f.URL)
default:
err = fmt.Errorf("provider %s not supported", provider)
}
Expand Down
112 changes: 112 additions & 0 deletions internal/notifier/lark.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package notifier

import (
"fmt"
"net/url"
"strings"

"github.com/fluxcd/pkg/runtime/events"
)

type Lark struct {
URL string
}

type LarkPayload struct {
MsgType string `json:"msg_type"`
Card LarkCard `json:"card"`
}

type LarkCard struct {
Config LarkConfig `json:"config"`

Header LarkHeader `json:"header"`

Elements []LarkElement `json:"elements"`
}

type LarkConfig struct {
WideScreenMode bool `json:"wide_screen_mode"`
}

type LarkHeader struct {
Title LarkTitle `json:"title"`
Template string `json:"template"`
}

type LarkTitle struct {
Tag string `json:"tag"`
Content string `json:"content"`
}

type LarkElement struct {
Tag string `json:"tag"`
Text LarkText `json:"text"`
}

type LarkText struct {
Tag string `json:"tag"`
Content string `json:"content"`
}

func NewLark(address string) (*Lark, error) {
_, err := url.ParseRequestURI(address)
if err != nil {
return nil, fmt.Errorf("invalid Slack hook URL %s", address)
}

return &Lark{
URL: address,
}, nil
}

func (l *Lark) Post(event events.Event) error {
// Skip any update events
if isCommitStatus(event.Metadata, "update") {
return nil
}

emoji := "💫"
color := "turquoise"
if event.Severity == events.EventSeverityError {
emoji = "🚨"
color = "red"
}

message := fmt.Sprintf("**%s**\n\n", event.Message)
for k, v := range event.Metadata {
message = message + fmt.Sprintf("%s: %s\n", k, v)
}

element := LarkElement{
Tag: "div",
Text: LarkText{
Tag: "lark_md",
Content: message,
},
}

card := LarkCard{
Config: LarkConfig{
WideScreenMode: true,
},
Header: LarkHeader{
Title: LarkTitle{
Tag: "plain_text",
Content: fmt.Sprintf("%s %s/%s.%s", emoji, strings.ToLower(event.InvolvedObject.Kind),
event.InvolvedObject.Name, event.InvolvedObject.Namespace),
},
Template: color,
},
Elements: []LarkElement{
element,
},
}

payload := LarkPayload{
MsgType: "interactive",
Card: card,
}

return postMessage(l.URL, "", nil, payload)
}
31 changes: 31 additions & 0 deletions internal/notifier/lark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package notifier

import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/require"
)

func TestLark_Post(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
var payload LarkPayload
err = json.Unmarshal(b, &payload)
require.NoError(t, err)

require.Equal(t, "💫 gitrepository/webapp.gitops-system", payload.Card.Header.Title.Content)
require.Equal(t, "turquoise", payload.Card.Header.Template)
}))
defer ts.Close()

lark, err := NewLark(ts.URL)
require.NoError(t, err)

err = lark.Post(testEvent())
require.NoError(t, err)
}

0 comments on commit 9ecfb37

Please sign in to comment.