From bd2adf6e5f0d79800e48ac3da2b0468e153d1891 Mon Sep 17 00:00:00 2001 From: James Laska <1051173+jlaska@users.noreply.github.com> Date: Mon, 1 Aug 2022 14:10:36 -0400 Subject: [PATCH] Support secrets for notification_url (#1300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: nils måsén --- docs/notifications.md | 3 ++- internal/flags/flags.go | 31 ++++++++++++++++++++++++++++--- internal/flags/flags_test.go | 30 +++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/docs/notifications.md b/docs/notifications.md index 7921e19..a69b00b 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -181,7 +181,8 @@ If you want to disable TLS verification for the Gotify instance, you can use eit To send notifications via shoutrrr, the following command-line options, or their corresponding environment variables, can be set: -- `--notification-url` (env. `WATCHTOWER_NOTIFICATION_URL`): The shoutrrr service URL to be used. +- `--notification-url` (env. `WATCHTOWER_NOTIFICATION_URL`): The shoutrrr service URL to be used. This option can also reference a file, in which case the contents of the file are used. + Go to [containrrr.dev/shoutrrr/v0.6/services/overview](https://containrrr.dev/shoutrrr/v0.6/services/overview) to learn more about the different service URLs you can use. You can define multiple services by space separating the diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 2af8678..531d5d2 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -1,6 +1,7 @@ package flags import ( + "bufio" "errors" "io/ioutil" "os" @@ -444,6 +445,7 @@ func GetSecretsFromFiles(rootCmd *cobra.Command) { "notification-slack-hook-url", "notification-msteams-hook", "notification-gotify-token", + "notification-url", } for _, secret := range secrets { getSecretFromFile(flags, secret) @@ -452,10 +454,33 @@ func GetSecretsFromFiles(rootCmd *cobra.Command) { // getSecretFromFile will check if the flag contains a reference to a file; if it does, replaces the value of the flag with the contents of the file. func getSecretFromFile(flags *pflag.FlagSet, secret string) { - value, err := flags.GetString(secret) - if err != nil { - log.Error(err) + flag := flags.Lookup(secret) + if sliceValue, ok := flag.Value.(pflag.SliceValue); ok { + oldValues := sliceValue.GetSlice() + values := make([]string, 0, len(oldValues)) + for _, value := range oldValues { + if value != "" && isFile(value) { + file, err := os.Open(value) + if err != nil { + log.Fatal(err) + } + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if line == "" { + continue + } + values = append(values, line) + } + } else { + values = append(values, value) + } + } + sliceValue.Replace(values) + return } + + value := flag.Value.String() if value != "" && isFile(value) { file, err := ioutil.ReadFile(value) if err != nil { diff --git a/internal/flags/flags_test.go b/internal/flags/flags_test.go index 1a904dd..7d3c55e 100644 --- a/internal/flags/flags_test.go +++ b/internal/flags/flags_test.go @@ -50,6 +50,7 @@ func TestGetSecretsFromFilesWithString(t *testing.T) { err := os.Setenv("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD", value) require.NoError(t, err) + defer os.Unsetenv("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD") testGetSecretsFromFiles(t, "notification-email-server-password", value) } @@ -69,17 +70,40 @@ func TestGetSecretsFromFilesWithFile(t *testing.T) { err = os.Setenv("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD", file.Name()) require.NoError(t, err) + defer os.Unsetenv("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD") testGetSecretsFromFiles(t, "notification-email-server-password", value) } -func testGetSecretsFromFiles(t *testing.T, flagName string, expected string) { +func TestGetSliceSecretsFromFiles(t *testing.T) { + values := []string{"entry2", "", "entry3"} + + // Create the temporary file which will contain a secret. + file, err := ioutil.TempFile(os.TempDir(), "watchtower-") + require.NoError(t, err) + defer os.Remove(file.Name()) // Make sure to remove the temporary file later. + + // Write the secret to the temporary file. + for _, value := range values { + _, err = file.WriteString("\n" + value) + require.NoError(t, err) + } + file.Close() + + testGetSecretsFromFiles(t, "notification-url", `[entry1,entry2,entry3]`, + `--notification-url`, "entry1", + `--notification-url`, file.Name()) +} + +func testGetSecretsFromFiles(t *testing.T, flagName string, expected string, args ...string) { cmd := new(cobra.Command) SetDefaults() RegisterNotificationFlags(cmd) + require.NoError(t, cmd.ParseFlags(args)) GetSecretsFromFiles(cmd) - value, err := cmd.PersistentFlags().GetString(flagName) - require.NoError(t, err) + flag := cmd.PersistentFlags().Lookup(flagName) + require.NotNil(t, flag) + value := flag.Value.String() assert.Equal(t, expected, value) }