mirror of https://github.com/containrrr/watchtower
				
				
				
			feat(notifications): add json template (#1542)
							parent
							
								
									8464e0dece
								
							
						
					
					
						commit
						547d033460
					
				@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					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().ShortID(),
 | 
				
			||||||
 | 
								`name`:           report.Name(),
 | 
				
			||||||
 | 
								`currentImageId`: report.CurrentImageID().ShortID(),
 | 
				
			||||||
 | 
								`latestImageId`:  report.LatestImageID().ShortID(),
 | 
				
			||||||
 | 
								`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 {
 | 
				
			||||||
 | 
						var bytes []byte
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						if bytes, err = json.MarshalIndent(v, "", "  "); err != nil {
 | 
				
			||||||
 | 
							LocalLog.Errorf("failed to marshal JSON in notification template: %v", err)
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return string(bytes)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					package notifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						s "github.com/containrrr/watchtower/pkg/session"
 | 
				
			||||||
 | 
						. "github.com/onsi/ginkgo"
 | 
				
			||||||
 | 
						. "github.com/onsi/gomega"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ = Describe("JSON template", func() {
 | 
				
			||||||
 | 
						When("using report templates", func() {
 | 
				
			||||||
 | 
							When("JSON template is used", func() {
 | 
				
			||||||
 | 
								It("should format the messages to the expected format", func() {
 | 
				
			||||||
 | 
									expected := `{
 | 
				
			||||||
 | 
						"entries": [
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"data": null,
 | 
				
			||||||
 | 
									"level": "info",
 | 
				
			||||||
 | 
									"message": "foo Bar",
 | 
				
			||||||
 | 
									"time": "0001-01-01T00:00:00Z"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							"host": "Mock",
 | 
				
			||||||
 | 
							"report": {
 | 
				
			||||||
 | 
							"failed": [
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d210000000",
 | 
				
			||||||
 | 
									"error": "accidentally the whole container",
 | 
				
			||||||
 | 
									"id": "c79210000000",
 | 
				
			||||||
 | 
									"imageName": "mock/fail1:latest",
 | 
				
			||||||
 | 
									"latestImageId": "d0a210000000",
 | 
				
			||||||
 | 
									"name": "fail1",
 | 
				
			||||||
 | 
									"state": "Failed"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							"fresh": [
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d310000000",
 | 
				
			||||||
 | 
									"id": "c79310000000",
 | 
				
			||||||
 | 
									"imageName": "mock/frsh1:latest",
 | 
				
			||||||
 | 
									"latestImageId": "01d310000000",
 | 
				
			||||||
 | 
									"name": "frsh1",
 | 
				
			||||||
 | 
									"state": "Fresh"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							"scanned": [
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d110000000",
 | 
				
			||||||
 | 
									"id": "c79110000000",
 | 
				
			||||||
 | 
									"imageName": "mock/updt1:latest",
 | 
				
			||||||
 | 
									"latestImageId": "d0a110000000",
 | 
				
			||||||
 | 
									"name": "updt1",
 | 
				
			||||||
 | 
									"state": "Updated"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d120000000",
 | 
				
			||||||
 | 
									"id": "c79120000000",
 | 
				
			||||||
 | 
									"imageName": "mock/updt2:latest",
 | 
				
			||||||
 | 
									"latestImageId": "d0a120000000",
 | 
				
			||||||
 | 
									"name": "updt2",
 | 
				
			||||||
 | 
									"state": "Updated"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d210000000",
 | 
				
			||||||
 | 
									"error": "accidentally the whole container",
 | 
				
			||||||
 | 
									"id": "c79210000000",
 | 
				
			||||||
 | 
									"imageName": "mock/fail1:latest",
 | 
				
			||||||
 | 
									"latestImageId": "d0a210000000",
 | 
				
			||||||
 | 
									"name": "fail1",
 | 
				
			||||||
 | 
									"state": "Failed"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d310000000",
 | 
				
			||||||
 | 
									"id": "c79310000000",
 | 
				
			||||||
 | 
									"imageName": "mock/frsh1:latest",
 | 
				
			||||||
 | 
									"latestImageId": "01d310000000",
 | 
				
			||||||
 | 
									"name": "frsh1",
 | 
				
			||||||
 | 
									"state": "Fresh"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							"skipped": [
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d410000000",
 | 
				
			||||||
 | 
									"error": "unpossible",
 | 
				
			||||||
 | 
									"id": "c79410000000",
 | 
				
			||||||
 | 
									"imageName": "mock/skip1:latest",
 | 
				
			||||||
 | 
									"latestImageId": "01d410000000",
 | 
				
			||||||
 | 
									"name": "skip1",
 | 
				
			||||||
 | 
									"state": "Skipped"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							"stale": [],
 | 
				
			||||||
 | 
							"updated": [
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d110000000",
 | 
				
			||||||
 | 
									"id": "c79110000000",
 | 
				
			||||||
 | 
									"imageName": "mock/updt1:latest",
 | 
				
			||||||
 | 
									"latestImageId": "d0a110000000",
 | 
				
			||||||
 | 
									"name": "updt1",
 | 
				
			||||||
 | 
									"state": "Updated"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"currentImageId": "01d120000000",
 | 
				
			||||||
 | 
									"id": "c79120000000",
 | 
				
			||||||
 | 
									"imageName": "mock/updt2:latest",
 | 
				
			||||||
 | 
									"latestImageId": "d0a120000000",
 | 
				
			||||||
 | 
									"name": "updt2",
 | 
				
			||||||
 | 
									"state": "Updated"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"title": "Watchtower updates on Mock"
 | 
				
			||||||
 | 
					}`
 | 
				
			||||||
 | 
									data := mockDataFromStates(s.UpdatedState, s.FreshState, s.FailedState, s.SkippedState, s.UpdatedState)
 | 
				
			||||||
 | 
									Expect(getTemplatedResult(`json.v1`, false, data)).To(MatchJSON(expected))
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
					Loading…
					
					
				
		Reference in New Issue