Repository: containrrr/watchtower
Pull request #593 (2020-07-05 09:26:08)

Added string functions for lowercase, uppercase and capitalize to shoutrrr templates
Proposal for #592

PssbleTrngle: That was my first try to, but if I give the `ToUpper` function an argument of another type than stirng, the log level for example, an error is thrown. That's why I first convert the input to string in each function
piksel: Yeah, but you could just pipe it to `printf "%v"` in those cases. I'm not sure it should be up to the template functions to format non-strings.
simskij: I agree with @piksel 

Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index d96ff8c..65b6a43 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -620,6 +620,15 @@
       "contributions": [
         "code"
       ]
+    },
+    {
+      "login": "bugficks",
+      "name": "bugficks",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/2992895?v=4",
+      "profile": "https://github.com/bugficks",
+      "contributions": [
+        "doc"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 9826a5d..bf304be 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
     <td align="center"><a href="https://github.com/pgrimaud"><img src="https://avatars1.githubusercontent.com/u/1866496?v=4" width="100px;" alt=""/><br /><sub><b>Pierre Grimaud</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=pgrimaud" title="Documentation">📖</a></td>
     <td align="center"><a href="https://github.com/mattdoran"><img src="https://avatars0.githubusercontent.com/u/577779?v=4" width="100px;" alt=""/><br /><sub><b>Matt Doran</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mattdoran" title="Documentation">📖</a></td>
     <td align="center"><a href="https://github.com/MihailITPlace"><img src="https://avatars2.githubusercontent.com/u/28401551?v=4" width="100px;" alt=""/><br /><sub><b>MihailITPlace</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=MihailITPlace" title="Code">💻</a></td>
+    <td align="center"><a href="https://github.com/bugficks"><img src="https://avatars1.githubusercontent.com/u/2992895?v=4" width="100px;" alt=""/><br /><sub><b>bugficks</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=bugficks" title="Documentation">📖</a></td>
   </tr>
 </table>
 
diff --git a/docs/private-registries.md b/docs/private-registries.md
index dab0c46..147e307 100644
--- a/docs/private-registries.md
+++ b/docs/private-registries.md
@@ -32,9 +32,11 @@ echo -n 'username:password' | base64
 
 > ### ℹ️ Username and Password for GCloud
 >
-> For gcloud, we'll use `__json_key` as our username and the content
+> For gcloud, we'll use `_json_key` as our username and the content
 > of `gcloudauth.json` as the password.
-
+>```bash
+> echo -n "_json_key:$(cat gcloudauth.json)" | base64 -w0
+>```
 When the watchtower Docker container is started, the created configuration file
 (`<PATH>/config.json` in this example) needs to be passed to the container:
 
@@ -67,6 +69,21 @@ watchtower:
 [...]
 ```
 
+#### Docker Config path
+By default, watchtower will look for the `config.json` file in `/`, but this can be changed by setting the `DOCKER_CONFIG` environment variable to the directory path where your config is located. This is useful for setups where the config.json file is changed while the watchtower instance is running, as the changes will not be picked up for a mounted file if the inode changes.
+Example usage:
+
+```yaml
+watchtower:
+  image: containrrr/watchtower
+  environment:
+      DOCKER_CONFIG: /config
+    volumes:
+      - /etc/watchtower/config/:/config/
+      - /var/run/docker.sock:/var/run/docker.sock
+```
+
+
 ## Credential helpers
 Some private Docker registries (the most prominent probably being AWS ECR) use non-standard ways of authentication.
 To be able to use this together with watchtower, we need to use a credential helper.
diff --git a/pkg/notifications/shoutrrr.go b/pkg/notifications/shoutrrr.go
index 8cf3d7c..9a7cd62 100644
--- a/pkg/notifications/shoutrrr.go
+++ b/pkg/notifications/shoutrrr.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"text/template"
+    "strings"
 
 	"github.com/containrrr/shoutrrr"
 	"github.com/containrrr/shoutrrr/pkg/router"
@@ -109,10 +110,16 @@ func getShoutrrrTemplate(c *cobra.Command) *template.Template {
 
 	tplString, err := flags.GetString("notification-template")
 
+	funcs := template.FuncMap{
+		"ToUpper": strings.ToUpper,
+		"ToLower": strings.ToLower,
+		"Title": strings.Title,
+	}
+
 	// If we succeed in getting a non-empty template configuration
 	// try to parse the template string.
 	if tplString != "" && err == nil {
-		tpl, err = template.New("").Parse(tplString)
+		tpl, err = template.New("").Funcs(funcs).Parse(tplString)
 	}
 
 	// In case of errors (either from parsing the template string
@@ -128,7 +135,7 @@ func getShoutrrrTemplate(c *cobra.Command) *template.Template {
 	// template wasn't configured (the empty template string)
 	// fallback to using the default template.
 	if err != nil || tplString == "" {
-		tpl = template.Must(template.New("").Parse(shoutrrrDefaultTemplate))
+		tpl = template.Must(template.New("").Funcs(funcs).Parse(shoutrrrDefaultTemplate))
 	}
 
 	return tpl


Test patch:
diff --git a/pkg/notifications/shoutrrr_test.go b/pkg/notifications/shoutrrr_test.go
index 9795c2a..5db7473 100644
--- a/pkg/notifications/shoutrrr_test.go
+++ b/pkg/notifications/shoutrrr_test.go
@@ -51,6 +51,30 @@ func TestShoutrrrTemplate(t *testing.T) {
 	require.Equal(t, "info: foo bar\n", s)
 }
 
+func TestShoutrrrStringFunctions(t *testing.T) {
+	cmd := new(cobra.Command)
+	flags.RegisterNotificationFlags(cmd)
+	err := cmd.ParseFlags([]string{"--notification-template={{range .}}{{.Level | printf \"%v\" | ToUpper }}: {{.Message | ToLower }} {{.Message | Title }}{{println}}{{end}}"})
+
+	require.NoError(t, err)
+
+	shoutrrr := &shoutrrrTypeNotifier{
+		template: getShoutrrrTemplate(cmd),
+	}
+
+	entries := []*log.Entry{
+		{
+			Level:   log.InfoLevel,
+			Message: "foo Bar",
+		},
+	}
+
+	s := shoutrrr.buildMessage(entries)
+
+	require.Equal(t, "INFO: foo bar Foo Bar\n", s)
+}
+
+
 func TestShoutrrrInvalidTemplateUsesTemplate(t *testing.T) {
 	cmd := new(cobra.Command)