Skip to content

Commit

Permalink
Merge pull request #157 from sdslabs/added_more_notification_channels
Browse files Browse the repository at this point in the history
  • Loading branch information
fristonio authored Oct 10, 2019
2 parents 2a04696 + 6e5fd76 commit 2d9c536
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 105 deletions.
5 changes: 5 additions & 0 deletions _examples/example.config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ ssh_key = "/home/vsts/.beast/secret.key"
url = "[email protected]:sdslabs/nonexistent.git"
name = "nonexistent"
branch = "nonexistent"

[[webhooks]]
url = ""
service_name= "discord"
active=true
23 changes: 14 additions & 9 deletions core/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,14 @@ import (
// ssh_key = "/home/fristonio/.beast/secrets/key.priv"
// ```
type BeastConfig struct {
AuthorizedKeysFile string `toml:"authorized_keys_file"`
BeastScriptsDir string `toml:"scripts_dir"`
AllowedBaseImages []string `toml:"allowed_base_images"`
AvailableSidecars []string `toml:"available_sidecars"`
GitRemote GitRemote `toml:"remote"`
JWTSecret string `toml:"jwt_secret"`
SlackWebHookURL string `toml:"slack_webhook"`

TickerFrequency int `toml:"ticker_frequency"`
AuthorizedKeysFile string `toml:"authorized_keys_file"`
BeastScriptsDir string `toml:"scripts_dir"`
AllowedBaseImages []string `toml:"allowed_base_images"`
AvailableSidecars []string `toml:"available_sidecars"`
GitRemote GitRemote `toml:"remote"`
JWTSecret string `toml:"jwt_secret"`
NotificationWebhooks []NotificationWebhook `toml:webhook`
TickerFrequency int `toml:"ticker_frequency"`

RemoteSyncPeriod time.Duration `toml:"-"`
Rsp string `toml:"remote_sync_period"`
Expand Down Expand Up @@ -216,6 +215,12 @@ func (config *GitRemote) ValidateGitConfig() error {
return nil
}

type NotificationWebhook struct {
URL string `toml:"url"`
ServiceName string `toml:"service_name"`
Active bool `toml:"active"`
}

// From the path of the config file provided as an arguement this function
// loads the parse the config file and load it into the BeastConfig
// structure. After parsing it validates the data in the config file and returns
Expand Down
6 changes: 3 additions & 3 deletions core/manager/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,14 +534,14 @@ func StartUndeployChallenge(challengeName string, purge bool) error {
if err != nil {
msg := fmt.Sprintf("UNDEPLOY ERROR: %s : %s", challengeName, err)
log.Error(msg)
notify.SendNotificationToSlack(notify.Error, msg)
notify.SendNotification(notify.Error, msg)
} else {
msg := fmt.Sprintf("UNDEPLOY SUCCESSFUL: %s", challengeName)
log.Info(msg)
notify.SendNotificationToSlack(notify.Success, msg)
notify.SendNotification(notify.Success, msg)
}

log.Infof("Notification for the event sent to slack.")
log.Info("Notification for the event sent.")
return err
}

Expand Down
2 changes: 1 addition & 1 deletion core/manager/health_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func ChallengesHealthProber(waitTime int) {
log.WithFields(log.Fields{
"ChallName": chall.Name,
}).Error(msg)
notify.SendNotificationToSlack(notify.Error, msg)
notify.SendNotification(notify.Error, msg)
} else {
log.WithFields(log.Fields{
"ChallName": chall.Name,
Expand Down
6 changes: 3 additions & 3 deletions core/manager/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,11 @@ func StartDeployPipeline(challengeDir string, skipStage bool, skipCommit bool) {

err := bootstrapDeployPipeline(challengeDir, skipStage, skipCommit)
if err != nil {
notify.SendNotificationToSlack(notify.Error, err.Error())
notify.SendNotification(notify.Error, err.Error())
} else {
msg := fmt.Sprintf("DEPLOY SUCCESS : %s : Challenge deployment pipeline successful.", challengeName)
notify.SendNotificationToSlack(notify.Success, msg)
notify.SendNotification(notify.Success, msg)
}

log.Debugf("%s: Notification sent to slack", challengeName)
log.Debugf("%s: Notification sent", challengeName)
}
13 changes: 11 additions & 2 deletions docs/Setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,18 @@ jwt_secret = "beast_jwt_secret_SUPER_STRONG_0x100010000100"


# To allow beast to send notification to a notification channel povide this webhook URL
# We are also working on implmeneting notification using Discord and IRC.
slack_webhook = ""
# We are also working on implmeneting notification using IRC.
[[webhooks]]

# The webhook URL of notification channel where notification should be sent
url = ""

# The service name to be used. It can be `discord` and `slack`
service_name = "discord"

# Status of webhook URL to be used.
# If it is false then notification will not be sent on this URL
active = true

# The sidecar that we support with beast, currently we only support two MySQL and
# MongoDB.
Expand Down
6 changes: 6 additions & 0 deletions pkg/notify/notification.go → pkg/notify/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ const (
WarningColor NotificationColor = "#FF4500"
SuccessColor NotificationColor = "#32CD32"
)

const (
USERNAME string = "Beast"
ICON_URL string = "https://i.ibb.co/sjC5dRY/beast-eye-39371.png"
CHANNEL_NAME string = "#beast"
)
68 changes: 68 additions & 0 deletions pkg/notify/discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package notify

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)

type DiscordNotificationProvider struct {
Request
}

func (d *DiscordNotificationProvider) SendNotification(nType NotificationType, msg string) error {
if d.Request.WebHookURL == "" {
return fmt.Errorf("Need a WebHookURL to send notification.")
}

d.Request.FillReqParams()

nAttachment := Attachment{
AuthorName: "Beast Notifier",
AuthorLink: "https://backdoor.sdslabs.co",
Footer: "Beast Discord API",
FooterIcon: "https://discordapp.com/assets/e05ead6e6ebc08df9291738d0aa6986d.png",
Timestamp: time.Now().Unix(),
Text: msg,
}

switch nType {
case Success:
nAttachment.Color = SuccessColor
nAttachment.Title = "Beast Deployment Success"
break
case Error:
nAttachment.Color = ErrorColor
nAttachment.Title = "Beast Deployment Error"
break
}

d.Request.PostPayload.Attachments = []Attachment{nAttachment}

if d.Request.PostPayload.Channel == "" || d.Request.PostPayload.Username == "" {
return fmt.Errorf("Username and Channel required to send the notification.")
}

payload, err := json.Marshal(d.PostPayload)
if err != nil {
return fmt.Errorf("Error while converting payload to JSON : %s", err)
}

payloadReader := bytes.NewReader(payload)
req, err := http.NewRequest("POST", d.Request.WebHookURL, payloadReader)
if err != nil {
return fmt.Errorf("Error while connecting to webhook url host : %s", err)
}

req.Header.Set("Content-Type", "application/json")
client := http.Client{}
_, err = client.Do(req)

if err != nil {
return fmt.Errorf("Error while posting payload for notification : %s", err)
}

return nil
}
116 changes: 116 additions & 0 deletions pkg/notify/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package notify

import (
"fmt"
"net/url"

"github.com/sdslabs/beastv4/core/config"
log "github.com/sirupsen/logrus"
)

type Attachment struct {
Fallback string `json:"fallback,omitempty"`
Color NotificationColor `json:"color,omitempty"`
PreText string `json:"pretext,omitempty"`
AuthorName string `json:"author_name,omitempty"`
AuthorLink string `json:"author_link,omitempty"`
AuthorIcon string `json:"author_icon,omitempty"`
Title string `json:"title,omitempty"`
TitleLink string `json:"title_link,omitempty"`
Text string `json:"text,omitempty"`
ImageUrl string `json:"image_url,omitempty"`
Footer string `json:"footer,omitempty"`
FooterIcon string `json:"footer_icon,omitempty"`
Timestamp int64 `json:"ts,omitempty"`
MarkdownIn []string `json:"mrkdwn_in,omitempty"`
CallbackID string `json:"callback_id,omitempty"`
ThumbnailUrl string `json:"thumb_url,omitempty"`
}

type PostPayload struct {
Parse string `json:"parse,omitempty"`
Username string `json:"username,omitempty"`
IconUrl string `json:"icon_url,omitempty"`
IconEmoji string `json:"icon_emoji,omitempty"`
Channel string `json:"channel,omitempty"`
Text string `json:"text,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
Markdown bool `json:"mrkdwn,omitempty"`
}

type Notifier interface {
SendNotification(nType NotificationType, msg string) error
}

type Request struct {
WebHookURL string
PostPayload
}

type ProviderTypeEnum int

const (
DiscordProvider ProviderTypeEnum = 1 + iota
SlackProvider
)

//In the Discord notification provider it was using the same payload which was used for slack.
//By writing "/slack" in the discord WebHookURL, it execute Slack-Compatible Webhook
func NewNotifier(URL string, ProviderType ProviderTypeEnum) Notifier {
url, err := url.ParseRequestURI(URL)
if url == nil || err != nil {
log.Error("Invalid notification webhook URL")
return nil
}
switch ProviderType {
case SlackProvider:
return &SlackNotificationProvider{
Request{
WebHookURL: url.String(),
},
}
case DiscordProvider:
return &DiscordNotificationProvider{
Request{
WebHookURL: url.String() + "/slack",
},
}
}
return nil
}

func (req *Request) FillReqParams() error {
req.PostPayload = PostPayload{
Username: USERNAME,
IconUrl: ICON_URL,
Channel: CHANNEL_NAME,
}
return nil
}

func SendNotification(nType NotificationType, message string) error {
for _, webhook := range config.Cfg.NotificationWebhooks {
if webhook.ServiceName != "" || webhook.Active == true {
var Provider ProviderTypeEnum
if webhook.ServiceName == "slack" {
Provider = SlackProvider
}
if webhook.ServiceName == "discord" {
Provider = DiscordProvider
}
Notifier := NewNotifier(webhook.URL, Provider)

err := Notifier.SendNotification(nType, message)
if err != nil {
log.Errorf("Error while sending notification to %s : %s", webhook.ServiceName, err)
fmt.Errorf("NOTIFICATION_SEND_ERROR: %s", err)
}

log.Infof("Notfication sent to %s.", webhook.ServiceName)
} else {
log.Warnf("No %s webhook url provided in beast config, cannot send notification.", webhook.ServiceName)
fmt.Errorf("No webhook URL in beast config.")
}
}
return nil
}
Loading

0 comments on commit 2d9c536

Please sign in to comment.