Skip to content

Commit

Permalink
initial commit of annotation-based configuration overrides!
Browse files Browse the repository at this point in the history
  • Loading branch information
Caleb Hailey committed Jan 24, 2019
1 parent b419bbb commit 266b461
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 21 deletions.
94 changes: 73 additions & 21 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,32 @@ import (
"log"
"os"
"strings"
"strconv"
"reflect"

"github.com/bluele/slack"
"github.com/sensu/sensu-go/types"
"github.com/spf13/cobra"
)

type HandlerConfig struct {
// A "Keyspace" field and corresponding "path" Field tags must be set to
// enable configuration overrides.
SlackWebhookUrl string `path:"webhook-url"`
SlackChannel string `path:"channel"`
SlackUsername string `path:"username"`
SlackIconUrl string `path:"icon-url"`
Timeout int
Keyspace string
}

var (
webhookURL string
channel string
username string
iconURL string
timeout int
stdin *os.File
stdin *os.File
config = HandlerConfig{
// default values
Timeout: 10,
Keyspace: "sensu.io/integrations/slack/config",
}
)

func main() {
Expand All @@ -43,31 +56,31 @@ func configureRootCommand() *cobra.Command {
do not mark as required
manually test for empty value
*/
cmd.Flags().StringVarP(&webhookURL,
cmd.Flags().StringVarP(&config.SlackWebhookUrl,
"webhook-url",
"w",
os.Getenv("SLACK_WEBHOOK_URL"),
"The webhook url to send messages to, defaults to value of SLACK_WEBHOOK_URL env variable")

cmd.Flags().StringVarP(&channel,
cmd.Flags().StringVarP(&config.SlackChannel,
"channel",
"c",
"#general",
"The channel to post messages to")

cmd.Flags().StringVarP(&username,
cmd.Flags().StringVarP(&config.SlackUsername,
"username",
"u",
"sensu",
"The username that messages will be sent as")

cmd.Flags().StringVarP(&iconURL,
cmd.Flags().StringVarP(&config.SlackIconUrl,
"icon-url",
"i",
"http://s3-us-west-2.amazonaws.com/sensuapp.org/sensu.png",
"A URL to an image to use as the user avatar")

cmd.Flags().IntVarP(&timeout,
cmd.Flags().IntVarP(&config.Timeout,
"timeout",
"t",
10,
Expand All @@ -81,26 +94,29 @@ func run(cmd *cobra.Command, args []string) error {
_ = cmd.Help()
return errors.New("invalid argument(s) received")
}
if webhookURL == "" {
_ = cmd.Help()
return fmt.Errorf("webhook url is empty")

}
// load & parse stdin
if stdin == nil {
stdin = os.Stdin
}

eventJSON, err := ioutil.ReadAll(stdin)
if err != nil {
return fmt.Errorf("failed to read stdin: %s", err.Error())
}

event := &types.Event{}
err = json.Unmarshal(eventJSON, event)
if err != nil {
return fmt.Errorf("failed to unmarshal stdin data: %s", eventJSON)
}

// configuration validation & overrides
if config.SlackWebhookUrl == "" {
_ = cmd.Help()
return fmt.Errorf("webhook url is empty")
}

configurationOverrides(&config,event)

if err = validateEvent(event); err != nil {
return errors.New(err.Error())
}
Expand All @@ -112,6 +128,42 @@ func run(cmd *cobra.Command, args []string) error {
return nil
}

func overrideConfig(t reflect.StructField, v *reflect.Value, o string) {
switch t.Type.Name() {
case "string":
v.FieldByName(t.Name).SetString(o)
case "int":
i,err := strconv.Atoi(o)
if err != nil {
log.Fatal(err)
}
v.FieldByName(t.Name).SetInt(int64(i))
}
}

func configurationOverrides(c *HandlerConfig, event *types.Event) {
if c.Keyspace != "" {
// Use Golang reflection to dynamically walk the configuration object
t := reflect.TypeOf(HandlerConfig{})
v := reflect.ValueOf(c).Elem()
for i := 0; i < t.NumField(); i++ {
// For any field
if path := t.Field(i).Tag.Get("path"); path != "" {
// compile the Annotation keyspace to look for configuration overrides
k := fmt.Sprintf("%s/%s",c.Keyspace,path)
switch {
case event.Entity.Annotations[k] != "":
overrideConfig(t.Field(i),&v,event.Entity.Annotations[k])
log.Printf("Overriding default handler configuration with value of \"Entity.Annotations.%s\" (\"%s\")\n",k,event.Entity.Annotations[k])
case event.Check.Annotations[k] != "":
overrideConfig(t.Field(i),&v,event.Check.Annotations[k])
log.Printf("Overriding default handler configuration with value of \"Check.Annotations.%s\" (\"%s\")\n",k,event.Check.Annotations[k])
}
}
}
}
}

func formattedEventAction(event *types.Event) string {
switch event.Check.Status {
case 0:
Expand Down Expand Up @@ -191,12 +243,12 @@ func messageAttachment(event *types.Event) *slack.Attachment {
}

func sendMessage(event *types.Event) error {
hook := slack.NewWebHook(webhookURL)
hook := slack.NewWebHook(config.SlackWebhookUrl)
return hook.PostMessage(&slack.WebHookPostPayload{
Attachments: []*slack.Attachment{messageAttachment(event)},
Channel: channel,
IconUrl: iconURL,
Username: username,
Channel: config.SlackChannel,
IconUrl: config.SlackIconUrl,
Username: config.SlackUsername,
})
}

Expand Down
193 changes: 193 additions & 0 deletions testing/data/example-event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
{
"timestamp": 1548109478,
"entity": {
"entity_class": "agent",
"system": {
"hostname": "70faa7c58706",
"os": "linux",
"platform": "debian",
"platform_family": "debian",
"platform_version": "9.6",
"network": {
"interfaces": [
{
"name": "lo",
"addresses": [
"127.0.0.1/8"
]
},
{
"name": "eth0",
"mac": "02:42:ac:15:00:07",
"addresses": [
"172.21.0.7/16"
]
}
]
},
"arch": "amd64"
},
"subscriptions": [
"linux",
"docker",
"poller",
"entity:70faa7c58706"
],
"last_seen": 1548099786,
"deregister": true,
"deregistration": {},
"user": "agent",
"redact": [
"password",
"passwd",
"pass",
"api_key",
"api_token",
"access_key",
"secret_key",
"private_key",
"secret"
],
"metadata": {
"name": "70faa7c58706",
"namespace": "default",
"labels": {
"region": "us-west-1"
}
}
},
"check": {
"command": "echo \"Hello linux world!\" && exit 1",
"handlers": [
"remediation"
],
"high_flap_threshold": 0,
"interval": 10,
"low_flap_threshold": 0,
"publish": true,
"runtime_assets": null,
"subscriptions": [
"linux"
],
"proxy_entity_name": "",
"check_hooks": null,
"stdin": false,
"subdue": null,
"ttl": 0,
"timeout": 0,
"round_robin": false,
"duration": 0.005119243,
"executed": 1548109478,
"history": [
{
"status": 1,
"executed": 1548109285
},
{
"status": 1,
"executed": 1548109295
},
{
"status": 1,
"executed": 1548109305
},
{
"status": 1,
"executed": 1548109315
},
{
"status": 1,
"executed": 1548109325
},
{
"status": 1,
"executed": 1548109335
},
{
"status": 1,
"executed": 1548109345
},
{
"status": 1,
"executed": 1548109355
},
{
"status": 1,
"executed": 1548109365
},
{
"status": 1,
"executed": 1548109375
},
{
"status": 1,
"executed": 1548109385
},
{
"status": 1,
"executed": 1548109395
},
{
"status": 1,
"executed": 1548109405
},
{
"status": 1,
"executed": 1548109415
},
{
"status": 1,
"executed": 1548109425
},
{
"status": 1,
"executed": 1548109435
},
{
"status": 1,
"executed": 1548109445
},
{
"status": 1,
"executed": 1548109455
},
{
"status": 1,
"executed": 1548109458
},
{
"status": 1,
"executed": 1548109468
},
{
"status": 1,
"executed": 1548109478
}
],
"issued": 1548109478,
"output": "Hello linux world!\n",
"state": "failing",
"status": 1,
"total_state_change": 0,
"last_ok": 1548105266,
"occurrences": 423,
"occurrences_watermark": 423,
"output_metric_format": "",
"output_metric_handlers": null,
"env_vars": null,
"metadata": {
"name": "helloworld",
"namespace": "default",
"labels": {
"foo": "bar"
},
"annotations": {
"sensu.io/integrations/slack/config/channel": "#demo-service-a",
"sensu.io/integrations/slack/config/username": "Sensu Go FTW!"
}
}
},
"metadata": {
"namespace": "default"
}
}
22 changes: 22 additions & 0 deletions testing/manifests/check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
type: CheckConfig
api_version: core/v2
metadata:
name: helloworld
namespace: default
labels:
foo: bar
annotations:
senus.io/integrations/slack/config/webhook_url: nil
sensu.io/integrations/slack/config/channel: "#demo"
sensu.io/integrations/slack/config/username: SensuBot
sensu.io/integrations/slack/config/icon_url: nil
sensu.io/integrations/slack/config/timeout: 10
spec:
command: echo "Hello {{ .system.os }} world!" && exit 1
publish: false
interval: 10
handlers:
- slack
subscriptions:
- development

0 comments on commit 266b461

Please sign in to comment.