feat: Extract df check config into hcl config file
This commit is contained in:
parent
a8c0b3ffbb
commit
eb9ccad432
49
cmd/main.go
49
cmd/main.go
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/slack-bot/pkg/check"
|
"go/slack-bot/pkg/check"
|
||||||
@ -17,11 +16,13 @@ import (
|
|||||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//SlackConf bot global configuration
|
||||||
type SlackConf struct {
|
type SlackConf struct {
|
||||||
BotID string `hcl:"bot_id"`
|
BotID string `hcl:"bot_id"`
|
||||||
DestChannelID string `hcl:"dest_channel_id"`
|
DestChannelID string `hcl:"dest_channel_id"`
|
||||||
WebhookID string `hcl:"webhook_id"`
|
WebhookID string `hcl:"webhook_id"`
|
||||||
DryRun *bool `hcl:"dry_run"`
|
DryRun *bool `hcl:"dry_run"`
|
||||||
|
Df []check.DfParameter `hcl:"df,block"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -32,40 +33,20 @@ func main() {
|
|||||||
conf := ParseConfiguration(configFile)
|
conf := ParseConfiguration(configFile)
|
||||||
slackNotifier := notify.NewSlackNotifier(conf.BotID, conf.DestChannelID, conf.WebhookID)
|
slackNotifier := notify.NewSlackNotifier(conf.BotID, conf.DestChannelID, conf.WebhookID)
|
||||||
|
|
||||||
dfChecker := check.NewDfChecker("disk is full", "bash", "-c", "df | grep -v 'auto_home'")
|
dfChecker := check.NewDfChecker()
|
||||||
|
|
||||||
msg := dfChecker.Check(func(stdout string) notify.Message {
|
msg := dfChecker.Check(func(stdout string) []notify.Message {
|
||||||
parsedDf, err := dfChecker.Parse(stdout)
|
var messages []notify.Message
|
||||||
if err != nil {
|
parsedDf := dfChecker.Parse(stdout)
|
||||||
return notify.Message{
|
casted := parsedDf.(check.DfStdout)
|
||||||
Content: fmt.Sprint("disk is full : ", err),
|
for _, dfCheck := range conf.Df {
|
||||||
ShouldNotify: true,
|
messages = append(messages, dfChecker.FileSystemUsedCheck(casted, dfCheck))
|
||||||
}
|
|
||||||
} else {
|
|
||||||
casted := parsedDf.(check.DfOut)
|
|
||||||
shouldNotify := check.FileSystemChecker("devfs", 70, casted)
|
|
||||||
if shouldNotify {
|
|
||||||
j, err := json.MarshalIndent(casted, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return notify.Message{
|
|
||||||
Content: fmt.Sprint("disk is full : ", err),
|
|
||||||
ShouldNotify: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return notify.Message{
|
|
||||||
Content: fmt.Sprintf("disk is full : ```%s```\n", j),
|
|
||||||
ShouldNotify: true,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return notify.Message{
|
|
||||||
ShouldNotify: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return messages
|
||||||
})
|
})
|
||||||
|
|
||||||
dryRunCtx := context.WithValue(context.Background(), notify.DryRunContextKey, conf.DryRun)
|
dryRunCtx := context.WithValue(context.Background(), notify.DryRunContextKey, conf.DryRun)
|
||||||
slackNotifier.Notify(dryRunCtx, msg)
|
slackNotifier.Notify(dryRunCtx, msg...)
|
||||||
}
|
}
|
||||||
|
|
||||||
//ParseConfiguration take filename path and parse it to Config.SlackConf
|
//ParseConfiguration take filename path and parse it to Config.SlackConf
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
bot_id = ""
|
bot_id = ""
|
||||||
dest_channel_id = ""
|
dest_channel_id = ""
|
||||||
webhook_id = ""
|
webhook_id = ""
|
||||||
|
|
||||||
|
df {
|
||||||
|
fs_name = "<filesystem name>"
|
||||||
|
critical = "<used pourcent>"
|
||||||
|
}
|
@ -14,6 +14,11 @@ resource "kubernetes_config_map" "slack_bot_config" {
|
|||||||
bot_id = "${var.bot_id}"
|
bot_id = "${var.bot_id}"
|
||||||
dest_channel_id = "${var.dest_channel_id}"
|
dest_channel_id = "${var.dest_channel_id}"
|
||||||
webhook_id = "${var.webhook_id}"
|
webhook_id = "${var.webhook_id}"
|
||||||
|
|
||||||
|
df {
|
||||||
|
fs_name = "/dev/mapper/vg1-data--kube"
|
||||||
|
critical = "85"
|
||||||
|
}
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@ import (
|
|||||||
|
|
||||||
//Checker representation of check set
|
//Checker representation of check set
|
||||||
type Checker interface {
|
type Checker interface {
|
||||||
Check(f func(stdout string) notify.Message) notify.Message
|
Check(f func(stdout string) []notify.Message) []notify.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
//AdvancedChecker like Checker except contain Parsing step
|
//AdvancedChecker like Checker except contain Parsing step
|
||||||
type AdvancedChecker interface {
|
type AdvancedChecker interface {
|
||||||
Checker
|
Checker
|
||||||
Parse(stdout string) (interface{}, error)
|
Parse(stdout string) interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
//CliChecker implement checker with cli command
|
//CliChecker implement checker with cli command
|
||||||
@ -32,16 +32,18 @@ func NewCliChecker(alertingMessage string, command ...string) Checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Check function use to run cli call to check some parameter
|
//Check function use to run cli call to check some parameter
|
||||||
func (cc *CliChecker) Check(f func(stdout string) notify.Message) notify.Message {
|
func (cc *CliChecker) Check(f func(stdout string) []notify.Message) []notify.Message {
|
||||||
message := notify.Message{ShouldNotify: false}
|
message := []notify.Message{}
|
||||||
|
|
||||||
out, err := exec.Command(cc.command[0], cc.command[1:]...).CombinedOutput()
|
out, err := exec.Command(cc.command[0], cc.command[1:]...).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// command fail notify with error
|
// command fail notify with error
|
||||||
message = notify.Message{
|
message = append(message,
|
||||||
Content: fmt.Sprint(cc.alertingMessage, " : ", string(out), err),
|
notify.Message{
|
||||||
ShouldNotify: true,
|
Content: fmt.Sprint(cc.alertingMessage, " : ", string(out), err),
|
||||||
}
|
ShouldNotify: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// command success check if condition required notify
|
// command success check if condition required notify
|
||||||
message = f(string(out))
|
message = f(string(out))
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
package check
|
package check
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/slack-bot/pkg/notify"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const dfMessage = "disk check"
|
||||||
|
|
||||||
//DfChecker df command implementation of AdvancedChecker
|
//DfChecker df command implementation of AdvancedChecker
|
||||||
type DfChecker struct {
|
type DfChecker struct {
|
||||||
CliChecker
|
CliChecker
|
||||||
pourcentRegexMatcher *regexp.Regexp
|
pourcentRegexMatcher *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
type DfOut []DfOutLine
|
type DfStdout []DfStdoutLine
|
||||||
type DfOutLine struct {
|
type DfStdoutLine struct {
|
||||||
Filesystem,
|
Filesystem,
|
||||||
Size,
|
Size,
|
||||||
Used,
|
Used,
|
||||||
@ -22,21 +26,26 @@ type DfOutLine struct {
|
|||||||
Mounted string
|
Mounted string
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewDfChecker build new checker for df command
|
//DfParameter df check configuration
|
||||||
func NewDfChecker(alertingMessage string, command ...string) AdvancedChecker {
|
type DfParameter struct {
|
||||||
|
FileSystemName string `hcl:"fs_name"`
|
||||||
|
CriticalPourcent int `hcl:"critical"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewDfChecker build new checker for df command
|
||||||
|
func NewDfChecker() *DfChecker {
|
||||||
return &DfChecker{
|
return &DfChecker{
|
||||||
CliChecker: CliChecker{
|
CliChecker: CliChecker{
|
||||||
alertingMessage: alertingMessage,
|
alertingMessage: dfMessage,
|
||||||
command: command,
|
command: []string{"bash", "-c", "df | grep -v 'auto_home'"},
|
||||||
},
|
},
|
||||||
pourcentRegexMatcher: regexp.MustCompile(`(?P<value>.*)%`),
|
pourcentRegexMatcher: regexp.MustCompile(`(?P<value>.*)%`),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//DfParser parse df stdout
|
//Parse df command stdout
|
||||||
func (dc *DfChecker) Parse(stdout string) (interface{}, error) {
|
func (dc *DfChecker) Parse(stdout string) interface{} {
|
||||||
var dfOut DfOut
|
var dfOut DfStdout
|
||||||
|
|
||||||
for _, line := range strings.Split(stdout, "\n")[1:] {
|
for _, line := range strings.Split(stdout, "\n")[1:] {
|
||||||
if strings.TrimSpace(line) == "" {
|
if strings.TrimSpace(line) == "" {
|
||||||
@ -53,10 +62,10 @@ func (dc *DfChecker) Parse(stdout string) (interface{}, error) {
|
|||||||
valueIndex := dc.pourcentRegexMatcher.SubexpIndex("value")
|
valueIndex := dc.pourcentRegexMatcher.SubexpIndex("value")
|
||||||
v, err := strconv.Atoi(dc.pourcentRegexMatcher.FindStringSubmatch(cleanedColumns[4])[valueIndex])
|
v, err := strconv.Atoi(dc.pourcentRegexMatcher.FindStringSubmatch(cleanedColumns[4])[valueIndex])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dfOut, err
|
v = -1
|
||||||
}
|
}
|
||||||
// extract pourcent used from columns
|
// extract pourcent used from columns
|
||||||
dfOut = append(dfOut, DfOutLine{
|
dfOut = append(dfOut, DfStdoutLine{
|
||||||
Filesystem: cleanedColumns[0],
|
Filesystem: cleanedColumns[0],
|
||||||
Size: cleanedColumns[1],
|
Size: cleanedColumns[1],
|
||||||
Used: cleanedColumns[2],
|
Used: cleanedColumns[2],
|
||||||
@ -65,16 +74,24 @@ func (dc *DfChecker) Parse(stdout string) (interface{}, error) {
|
|||||||
Mounted: cleanedColumns[len(cleanedColumns)-1],
|
Mounted: cleanedColumns[len(cleanedColumns)-1],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return dfOut, nil
|
return dfOut
|
||||||
}
|
}
|
||||||
|
|
||||||
func FileSystemChecker(fileSystemName string, criticalPourcent int, dfOut DfOut) bool {
|
//FileSystemUsedCheck take dfStdout and check if filesystem is full over than criticalPourcent value
|
||||||
|
func (dc *DfChecker) FileSystemUsedCheck(dfOut DfStdout, param DfParameter) notify.Message {
|
||||||
|
message := notify.Message{
|
||||||
|
ShouldNotify: false,
|
||||||
|
}
|
||||||
for _, line := range dfOut {
|
for _, line := range dfOut {
|
||||||
if line.Filesystem == fileSystemName {
|
if line.Filesystem == param.FileSystemName {
|
||||||
if line.PourcentUsed > criticalPourcent {
|
if line.PourcentUsed > param.CriticalPourcent {
|
||||||
return true
|
text := fmt.Sprintf("%s : fs '%s' critial %d%%, used %d%% \n", dc.alertingMessage, line.Filesystem, param.CriticalPourcent, line.PourcentUsed)
|
||||||
|
message = notify.Message{
|
||||||
|
Content: text,
|
||||||
|
ShouldNotify: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return message
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,18 @@ func NewSlackNotifier(BotID, DestChannelID, WebhookID string) *SlackNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Notify send notification to channel
|
//Notify send notification to channel
|
||||||
func (sn *SlackNotifier) Notify(ctx context.Context, message Message) {
|
func (sn *SlackNotifier) Notify(ctx context.Context, messages ...Message) {
|
||||||
if message.ShouldNotify {
|
shouldNotify := false
|
||||||
|
var content string
|
||||||
|
for _, message := range messages {
|
||||||
|
if message.ShouldNotify {
|
||||||
|
shouldNotify = true
|
||||||
|
content = fmt.Sprintf("%s\n%s", content, message.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if shouldNotify {
|
||||||
msg := &slack.WebhookMessage{
|
msg := &slack.WebhookMessage{
|
||||||
Text: message.Content,
|
Text: content,
|
||||||
}
|
}
|
||||||
|
|
||||||
url := fmt.Sprintf("https://%s/%s/%s/%s", slackHookBaseURL, sn.BotID, sn.DestChannelID, sn.WebhookID)
|
url := fmt.Sprintf("https://%s/%s/%s/%s", slackHookBaseURL, sn.BotID, sn.DestChannelID, sn.WebhookID)
|
||||||
|
Loading…
Reference in New Issue
Block a user