diff options
Diffstat (limited to 'worker.go')
| -rw-r--r-- | worker.go | 114 |
1 files changed, 109 insertions, 5 deletions
@@ -4,26 +4,45 @@ package lilin import ( + "bytes" + "encoding/json" + "io" "log" + "net/http" "os" "path/filepath" "strings" + "time" "git.sr.ht/~shulhan/pakakeh.go/lib/ini" ) // worker contains the report of all services. type worker struct { + // httpc client for notification for kind is mattermost. + httpc *http.Client + + cfg ServerConfig + // Internal service status, where key is service name. Services map[string]*Service `ini:"service"` reportq chan ScanReport + // notifq contains queue for notification. + notifq chan ScanReport + Reports Reports } +type mattermostWebhook struct { + Channel string `json:"channel"` + Text string `json:"text"` +} + func newWorker(cfg ServerConfig) (wrk *worker, err error) { wrk = &worker{ + cfg: cfg, Services: make(map[string]*Service), } @@ -41,11 +60,17 @@ func newWorker(cfg ServerConfig) (wrk *worker, err error) { return nil, err } } - return wrk, nil -} -type serviceConfigs struct { - Config map[string]ServiceConfig `ini:"service"` + for _, notifConfig := range cfg.Notifs { + switch notifConfig.Kind { + case notifKindMattermost: + wrk.httpc = &http.Client{ + Timeout: 5 * time.Second, + } + } + } + + return wrk, nil } // loadServiceDir Load all the service configurations. @@ -101,12 +126,15 @@ func (wrk *worker) loadServiceDir(cfg ServerConfig) (err error) { // Once new report coming, notify the upstream through the passed channel. func (wrk *worker) start(updateq chan<- struct{}) { wrk.reportq = make(chan ScanReport, len(wrk.Services)+1) + wrk.notifq = make(chan ScanReport, len(wrk.Services)+1) var svc *Service for _, svc = range wrk.Services { - go svc.Start(wrk.reportq) + go svc.Start(wrk.reportq, wrk.notifq) } + go wrk.handlePushNotif() + var scanReport ScanReport var ok bool for { @@ -120,6 +148,7 @@ func (wrk *worker) start(updateq chan<- struct{}) { } func (wrk *worker) stop() { + close(wrk.notifq) close(wrk.reportq) var svc *Service @@ -136,3 +165,78 @@ func (wrk *worker) stop() { } } } + +// handlePushNotif consume the failed ScanReport from notifq to be forwarded +// to the all notification defined in NotifConfig. +func (wrk *worker) handlePushNotif() { + for scanReport := range wrk.notifq { + for _, notifConfig := range wrk.cfg.Notifs { + switch notifConfig.Kind { + case notifKindMattermost: + wrk.pushNotifMattermost(notifConfig, &scanReport) + } + } + } +} + +func (wrk *worker) pushNotifMattermost( + notifConfig *NotifConfig, + scanReport *ScanReport, +) { + var logp = `pushNotifMattermost` + var text strings.Builder + var err error + if scanReport.Success { + err = notifConfig.upTmpl.Execute(&text, scanReport) + if err != nil { + log.Printf(`%s: %s`, logp, err) + return + } + } else { + err = notifConfig.downTmpl.Execute(&text, scanReport) + if err != nil { + log.Printf(`%s: %s`, logp, err) + return + } + } + + var msg = mattermostWebhook{ + Channel: notifConfig.Channel, + Text: text.String(), + } + var body []byte + body, err = json.Marshal(&msg) + if err != nil { + log.Printf(`%s: %s`, logp, err) + return + } + + var req = &http.Request{ + Method: http.MethodPost, + URL: notifConfig.webhookURL, + Header: http.Header{ + `Content-Type`: []string{ + `application/json`, + }, + }, + Body: io.NopCloser(bytes.NewReader(body)), + } + + var resp *http.Response + resp, err = wrk.httpc.Do(req) + if err != nil { + log.Printf(`%s: %s`, logp, err) + } + + if resp.StatusCode == 200 { + log.Printf(`%s: send %q`, logp, body) + return + } + + body, err = io.ReadAll(resp.Body) + if err != nil { + log.Printf(`%s: %s`, logp, err) + } + + log.Printf(`%s: fail with status code %d: %s`, logp, resp.StatusCode, body) +} |
