diff --git a/pkg/notifications/common_templates.go b/pkg/notifications/common_templates.go index 64a53c0..84c0f54 100644 --- a/pkg/notifications/common_templates.go +++ b/pkg/notifications/common_templates.go @@ -35,5 +35,6 @@ var commonTemplates = map[string]string{ no containers matched filter {{- end -}} {{- end -}}`, -} + `json.v1`: `{{ . | ToJSON }}`, +} diff --git a/pkg/notifications/json.go b/pkg/notifications/json.go new file mode 100644 index 0000000..a750862 --- /dev/null +++ b/pkg/notifications/json.go @@ -0,0 +1,70 @@ +package notifications + +import ( + "encoding/json" + + t "github.com/containrrr/watchtower/pkg/types" +) + +type JSONMap = map[string]interface{} + +// MarshalJSON implements json.Marshaler +func (d Data) MarshalJSON() ([]byte, error) { + var entries = make([]JSONMap, len(d.Entries)) + for i, entry := range d.Entries { + entries[i] = JSONMap{ + `level`: entry.Level, + `message`: entry.Message, + `data`: entry.Data, + `time`: entry.Time, + } + } + + var report JSONMap + if d.Report != nil { + report = JSONMap{ + `scanned`: marshalReports(d.Report.Scanned()), + `updated`: marshalReports(d.Report.Updated()), + `failed`: marshalReports(d.Report.Failed()), + `skipped`: marshalReports(d.Report.Skipped()), + `stale`: marshalReports(d.Report.Stale()), + `fresh`: marshalReports(d.Report.Fresh()), + } + } + + return json.Marshal(JSONMap{ + `report`: report, + `title`: d.Title, + `host`: d.Host, + `entries`: entries, + }) +} + +func marshalReports(reports []t.ContainerReport) []JSONMap { + jsonReports := make([]JSONMap, len(reports)) + for i, report := range reports { + jsonReports[i] = JSONMap{ + `id`: report.ID(), + `name`: report.Name(), + `currentImageId`: report.CurrentImageID(), + `latestImageId`: report.LatestImageID(), + `imageName`: report.ImageName(), + `state`: report.State(), + } + if errorMessage := report.Error(); errorMessage != "" { + jsonReports[i][`error`] = errorMessage + } + } + return jsonReports +} + +var _ json.Marshaler = &Data{} + +func toJSON(v interface{}) string { + if bytes, err := json.MarshalIndent(v, "", " "); err != nil { + LocalLog.Errorf("failed to marshal JSON in notification template: %v", err) + return "" + } else { + return string(bytes) + } +} diff --git a/pkg/notifications/model.go b/pkg/notifications/model.go new file mode 100644 index 0000000..83c97ba --- /dev/null +++ b/pkg/notifications/model.go @@ -0,0 +1,19 @@ +package notifications + +import ( + t "github.com/containrrr/watchtower/pkg/types" + log "github.com/sirupsen/logrus" +) + +// StaticData is the part of the notification template data model set upon initialization +type StaticData struct { + Title string + Host string +} + +// Data is the notification template data model +type Data struct { + StaticData + Entries []*log.Entry + Report t.Report +} diff --git a/pkg/notifications/shoutrrr.go b/pkg/notifications/shoutrrr.go index 47141e8..d6ce859 100644 --- a/pkg/notifications/shoutrrr.go +++ b/pkg/notifications/shoutrrr.go @@ -210,6 +210,7 @@ func getShoutrrrTemplate(tplString string, legacy bool) (tpl *template.Template, funcs := template.FuncMap{ "ToUpper": strings.ToUpper, "ToLower": strings.ToLower, + "ToJSON": toJSON, "Title": cases.Title(language.AmericanEnglish).String, } tplBase := template.New("").Funcs(funcs) @@ -240,16 +241,3 @@ func getShoutrrrTemplate(tplString string, legacy bool) (tpl *template.Template, return } - -// StaticData is the part of the notification template data model set upon initialization -type StaticData struct { - Title string - Host string -} - -// Data is the notification template data model -type Data struct { - StaticData - Entries []*log.Entry - Report t.Report -}