Skip to content

Commit

Permalink
Add gotify middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
rdelcorro committed Dec 28, 2020
1 parent 5bf6adb commit e355f7f
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 3 deletions.
16 changes: 13 additions & 3 deletions cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ const (
// Config contains the configuration
type Config struct {
Global struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}
ExecJobs map[string]*ExecJobConfig `gcfg:"job-exec" mapstructure:"job-exec,squash"`
RunJobs map[string]*RunJobConfig `gcfg:"job-run" mapstructure:"job-run,squash"`
Expand Down Expand Up @@ -108,6 +109,7 @@ func (c *Config) buildSchedulerMiddlewares(sh *core.Scheduler) {
sh.Use(middlewares.NewSlack(&c.Global.SlackConfig))
sh.Use(middlewares.NewSave(&c.Global.SaveConfig))
sh.Use(middlewares.NewMail(&c.Global.MailConfig))
sh.Use(middlewares.NewGotify(&c.Global.GotifyConfig))
}

func (c *Config) dockerLabelsUpdate(labels map[string]map[string]string) {
Expand Down Expand Up @@ -175,13 +177,15 @@ type ExecJobConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

func (c *ExecJobConfig) buildMiddlewares() {
c.ExecJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.ExecJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.ExecJob.Use(middlewares.NewSave(&c.SaveConfig))
c.ExecJob.Use(middlewares.NewMail(&c.MailConfig))
c.ExecJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}

// RunServiceConfig contains all configuration params needed to build a RunJob
Expand All @@ -191,6 +195,7 @@ type RunServiceConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

type RunJobConfig struct {
Expand All @@ -199,13 +204,15 @@ type RunJobConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

func (c *RunJobConfig) buildMiddlewares() {
c.RunJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.RunJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.RunJob.Use(middlewares.NewSave(&c.SaveConfig))
c.RunJob.Use(middlewares.NewMail(&c.MailConfig))
c.RunJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}

// LocalJobConfig contains all configuration params needed to build a RunJob
Expand All @@ -215,18 +222,21 @@ type LocalJobConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

func (c *LocalJobConfig) buildMiddlewares() {
c.LocalJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.LocalJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.LocalJob.Use(middlewares.NewSave(&c.SaveConfig))
c.LocalJob.Use(middlewares.NewMail(&c.MailConfig))
c.LocalJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}

func (c *RunServiceConfig) buildMiddlewares() {
c.RunServiceJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.RunServiceJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.RunServiceJob.Use(middlewares.NewSave(&c.SaveConfig))
c.RunServiceJob.Use(middlewares.NewMail(&c.MailConfig))
c.RunServiceJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}
79 changes: 79 additions & 0 deletions middlewares/gotify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package middlewares

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

"github.com/mcuadros/ofelia/core"
)

type GotifyConfig struct {
GotifyWebhook string `gcfg:"gotify-webhook" mapstructure:"gotify-webhook"`
GotifyOnlyOnError bool `gcfg:"gotify-only-on-error" mapstructure:"gotify-only-on-error"`
GotifyPriority int64 `gcfg:"gotify-priority" mapstructure:"gotify-priority"`
}

// NewGotify returns a Gotify middleware if the given configuration is not empty
func NewGotify(c *GotifyConfig) core.Middleware {
var m core.Middleware
if !IsEmpty(c) {
m = &Gotify{*c}
}

return m
}

type Gotify struct {
GotifyConfig
}

// ContinueOnStop return allways true, we want always report the final status
func (m *Gotify) ContinueOnStop() bool {
return true
}

func (m *Gotify) Run(ctx *core.Context) error {
err := ctx.Next()
ctx.Stop(err)

if ctx.Execution.Failed || !m.GotifyOnlyOnError {
m.pushMessage(ctx)
}

return err
}

func (m *Gotify) pushMessage(ctx *core.Context) {
content, _ := json.Marshal(m.buildMessage(ctx))

r, err := http.Post(m.GotifyWebhook, "application/json", bytes.NewReader(content))
if err != nil {
ctx.Logger.Errorf("Gotify error calling %q error: %q", m.GotifyWebhook, err)
} else if r.StatusCode != 200 {
ctx.Logger.Errorf("Gotify error non-200 status code calling %q", m.GotifyWebhook)
}
}

func (m *Gotify) buildMessage(ctx *core.Context) *gotifyMessage {
msg := &gotifyMessage{Title: ctx.Job.GetName(), Priority: m.GotifyPriority}

msg.Message = fmt.Sprintf(
"Job *%q* finished in *%s*, command `%s`",
ctx.Job.GetName(), ctx.Execution.Duration, ctx.Job.GetCommand(),
)

if ctx.Execution.Failed {
msg.Message = "FAILED: " + msg.Message
} else if ctx.Execution.Skipped {
msg.Message = "Skipped: " + msg.Message
}
return msg
}

type gotifyMessage struct {
Title string `json:"title"`
Message string `json:"message"`
Priority int64 `json:"priority"`
}
69 changes: 69 additions & 0 deletions middlewares/gotify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package middlewares

import (
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"

. "gopkg.in/check.v1"
)

type SuiteGotify struct {
BaseSuite
}

var _ = Suite(&SuiteGotify{})

func (s *SuiteGotify) TestNewGotifyEmpty(c *C) {
c.Assert(NewGotify(&GotifyConfig{}), IsNil)
}

func (s *SuiteGotify) TestRunSuccess(c *C) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var m gotifyMessage
err := json.NewDecoder(r.Body).Decode(&m)
c.Assert(err, Equals, nil)
c.Assert(!strings.Contains(m.Message, "FAILED"), Equals, true)
}))

defer ts.Close()

s.ctx.Start()
s.ctx.Stop(nil)

m := NewGotify(&GotifyConfig{GotifyWebhook: ts.URL})
c.Assert(m.Run(s.ctx), IsNil)
}

func (s *SuiteGotify) TestRunSuccessFailed(c *C) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var m gotifyMessage
err := json.NewDecoder(r.Body).Decode(&m)
c.Assert(err, Equals, nil)
c.Assert(strings.Contains(m.Message, "FAILED"), Equals, true)
}))

defer ts.Close()

s.ctx.Start()
s.ctx.Stop(errors.New("foo"))

m := NewGotify(&GotifyConfig{GotifyWebhook: ts.URL})
c.Assert(m.Run(s.ctx), IsNil)
}

func (s *SuiteGotify) TestRunSuccessOnError(c *C) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Assert(true, Equals, false)
}))

defer ts.Close()

s.ctx.Start()
s.ctx.Stop(nil)

m := NewGotify(&GotifyConfig{GotifyWebhook: ts.URL, GotifyOnlyOnError: true})
c.Assert(m.Run(s.ctx), IsNil)
}

0 comments on commit e355f7f

Please sign in to comment.