mirror of
https://github.com/containrrr/watchtower.git
synced 2025-09-22 05:40:50 +02:00
preparations for soft deprecation of legacy notification args (#1377)
Co-authored-by: Simon Aronsson <simme@arcticbit.se>
This commit is contained in:
parent
2102a056de
commit
cb555f539d
18 changed files with 505 additions and 167 deletions
111
cmd/notify-upgrade.go
Normal file
111
cmd/notify-upgrade.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Package cmd contains the watchtower (sub-)commands
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containrrr/watchtower/internal/flags"
|
||||||
|
"github.com/containrrr/watchtower/pkg/container"
|
||||||
|
"github.com/containrrr/watchtower/pkg/notifications"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var notifyUpgradeCommand = NewNotifyUpgradeCommand()
|
||||||
|
|
||||||
|
// NewNotifyUpgradeCommand creates the notify upgrade command for watchtower
|
||||||
|
func NewNotifyUpgradeCommand() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "notify-upgrade",
|
||||||
|
Short: "Upgrade legacy notification configuration to shoutrrr URLs",
|
||||||
|
Run: runNotifyUpgrade,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runNotifyUpgrade(cmd *cobra.Command, args []string) {
|
||||||
|
if err := runNotifyUpgradeE(cmd, args); err != nil {
|
||||||
|
logf("Notification upgrade failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runNotifyUpgradeE(cmd *cobra.Command, _ []string) error {
|
||||||
|
f := cmd.Flags()
|
||||||
|
flags.ProcessFlagAliases(f)
|
||||||
|
|
||||||
|
notifier = notifications.NewNotifier(cmd)
|
||||||
|
urls := notifier.GetURLs()
|
||||||
|
|
||||||
|
logf("Found notification configurations for: %v", strings.Join(notifier.GetNames(), ", "))
|
||||||
|
|
||||||
|
outFile, err := os.CreateTemp("/", "watchtower-notif-urls-*")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create output file: %v", err)
|
||||||
|
}
|
||||||
|
logf("Writing notification URLs to %v", outFile.Name())
|
||||||
|
logf("")
|
||||||
|
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString("WATCHTOWER_NOTIFICATION_URL=")
|
||||||
|
|
||||||
|
for i, u := range urls {
|
||||||
|
if i != 0 {
|
||||||
|
sb.WriteRune(' ')
|
||||||
|
}
|
||||||
|
sb.WriteString(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fmt.Fprint(outFile, sb.String())
|
||||||
|
tryOrLog(err, "Failed to write to output file")
|
||||||
|
|
||||||
|
tryOrLog(outFile.Sync(), "Failed to sync output file")
|
||||||
|
tryOrLog(outFile.Close(), "Failed to close output file")
|
||||||
|
|
||||||
|
containerID := "<CONTAINER>"
|
||||||
|
cid, err := container.GetRunningContainerID()
|
||||||
|
tryOrLog(err, "Failed to get running container ID")
|
||||||
|
if cid != "" {
|
||||||
|
containerID = cid.ShortID()
|
||||||
|
}
|
||||||
|
logf("To get the environment file, use:")
|
||||||
|
logf("cp %v:%v ./watchtower-notifications.env", containerID, outFile.Name())
|
||||||
|
logf("")
|
||||||
|
logf("Note: This file will be removed in 5 minutes or when this container is stopped!")
|
||||||
|
|
||||||
|
signalChannel := make(chan os.Signal, 1)
|
||||||
|
time.AfterFunc(5*time.Minute, func() {
|
||||||
|
signalChannel <- syscall.SIGALRM
|
||||||
|
})
|
||||||
|
|
||||||
|
signal.Notify(signalChannel, os.Interrupt)
|
||||||
|
signal.Notify(signalChannel, syscall.SIGTERM)
|
||||||
|
|
||||||
|
switch <-signalChannel {
|
||||||
|
case syscall.SIGALRM:
|
||||||
|
logf("Timed out!")
|
||||||
|
case os.Interrupt, syscall.SIGTERM:
|
||||||
|
logf("Stopping...")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(outFile.Name()); err != nil {
|
||||||
|
logf("Failed to remove file, it may still be present in the container image! Error: %v", err)
|
||||||
|
} else {
|
||||||
|
logf("Environment file has been removed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryOrLog(err error, message string) {
|
||||||
|
if err != nil {
|
||||||
|
logf("%v: %v\n", message, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func logf(format string, v ...interface{}) {
|
||||||
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(format, v...))
|
||||||
|
}
|
|
@ -66,6 +66,7 @@ func init() {
|
||||||
|
|
||||||
// Execute the root func and exit in case of errors
|
// Execute the root func and exit in case of errors
|
||||||
func Execute() {
|
func Execute() {
|
||||||
|
rootCmd.AddCommand(notifyUpgradeCommand)
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -139,6 +140,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
|
||||||
})
|
})
|
||||||
|
|
||||||
notifier = notifications.NewNotifier(cmd)
|
notifier = notifications.NewNotifier(cmd)
|
||||||
|
notifier.AddLogHook()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run is the main execution flow of the command
|
// Run is the main execution flow of the command
|
||||||
|
|
|
@ -1,15 +1,7 @@
|
||||||
# Notifications
|
# Notifications
|
||||||
|
|
||||||
Watchtower can send notifications when containers are updated. Notifications are sent via hooks in the logging
|
Watchtower can send notifications when containers are updated. Notifications are sent via hooks in the logging
|
||||||
system, [logrus](http://github.com/sirupsen/logrus). The types of notifications to send are set by passing a
|
system, [logrus](http://github.com/sirupsen/logrus).
|
||||||
comma-separated list of values to the `--notifications` option
|
|
||||||
(or corresponding environment variable `WATCHTOWER_NOTIFICATIONS`), which has the following valid values:
|
|
||||||
|
|
||||||
- `email` to send notifications via e-mail
|
|
||||||
- `slack` to send notifications through a Slack webhook
|
|
||||||
- `msteams` to send notifications via MSTeams webhook
|
|
||||||
- `gotify` to send notifications via Gotify
|
|
||||||
- `shoutrrr` to send notifications via [containrrr/shoutrrr](https://github.com/containrrr/shoutrrr)
|
|
||||||
|
|
||||||
!!! note "Using multiple notifications with environment variables"
|
!!! note "Using multiple notifications with environment variables"
|
||||||
There is currently a bug in Viper (https://github.com/spf13/viper/issues/380), which prevents comma-separated slices to
|
There is currently a bug in Viper (https://github.com/spf13/viper/issues/380), which prevents comma-separated slices to
|
||||||
|
@ -31,7 +23,221 @@ comma-separated list of values to the `--notifications` option
|
||||||
- `notification-title-tag` (env. `WATCHTOWER_NOTIFICATION_TITLE_TAG`): Prefix to include in the title. Useful when running multiple watchtowers.
|
- `notification-title-tag` (env. `WATCHTOWER_NOTIFICATION_TITLE_TAG`): Prefix to include in the title. Useful when running multiple watchtowers.
|
||||||
- `notification-skip-title` (env. `WATCHTOWER_NOTIFICATION_SKIP_TITLE`): Do not pass the title param to notifications. This will not pass a dynamic title override to notification services. If no title is configured for the service, it will remove the title all together.
|
- `notification-skip-title` (env. `WATCHTOWER_NOTIFICATION_SKIP_TITLE`): Do not pass the title param to notifications. This will not pass a dynamic title override to notification services. If no title is configured for the service, it will remove the title all together.
|
||||||
|
|
||||||
## Available services
|
## [shoutrrr](https://github.com/containrrr/shoutrrr) notifications
|
||||||
|
|
||||||
|
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. 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
|
||||||
|
URLs. (See example below)
|
||||||
|
|
||||||
|
You can customize the message posted by setting a template.
|
||||||
|
|
||||||
|
- `--notification-template` (env. `WATCHTOWER_NOTIFICATION_TEMPLATE`): The template used for the message.
|
||||||
|
|
||||||
|
The template is a Go [template](https://golang.org/pkg/text/template/) that either format a list
|
||||||
|
of [log entries](https://pkg.go.dev/github.com/sirupsen/logrus?tab=doc#Entry) or a `notification.Data` struct.
|
||||||
|
|
||||||
|
Simple templates are used unless the `notification-report` flag is specified:
|
||||||
|
|
||||||
|
- `--notification-report` (env. `WATCHTOWER_NOTIFICATION_REPORT`): Use the session report as the notification template data.
|
||||||
|
|
||||||
|
## Simple templates
|
||||||
|
|
||||||
|
The default value if not set is `{{range .}}{{.Message}}{{println}}{{end}}`. The example below uses a template that also
|
||||||
|
outputs timestamp and log level.
|
||||||
|
|
||||||
|
!!! tip "Custom date format"
|
||||||
|
If you want to adjust the date/time format it must show how the
|
||||||
|
[reference time](https://golang.org/pkg/time/#pkg-constants) (_Mon Jan 2 15:04:05 MST 2006_) would be displayed in your
|
||||||
|
custom format.
|
||||||
|
i.e., The day of the year has to be 1, the month has to be 2 (february), the hour 3 (or 15 for 24h time) etc.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name watchtower \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-e WATCHTOWER_NOTIFICATION_URL="discord://token@channel slack://watchtower@token-a/token-b/token-c" \
|
||||||
|
-e WATCHTOWER_NOTIFICATION_TEMPLATE="{{range .}}{{.Time.Format \"2006-01-02 15:04:05\"}} ({{.Level}}): {{.Message}}{{println}}{{end}}" \
|
||||||
|
containrrr/watchtower
|
||||||
|
```
|
||||||
|
|
||||||
|
## Report templates
|
||||||
|
|
||||||
|
The default template for report notifications are the following:
|
||||||
|
```go
|
||||||
|
{{- if .Report -}}
|
||||||
|
{{- with .Report -}}
|
||||||
|
{{- if ( or .Updated .Failed ) -}}
|
||||||
|
{{len .Scanned}} Scanned, {{len .Updated}} Updated, {{len .Failed}} Failed
|
||||||
|
{{- range .Updated}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.CurrentImageID.ShortID}} updated to {{.LatestImageID.ShortID}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Fresh}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Skipped}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Failed}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{range .Entries -}}{{.Message}}{{"\n"}}{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
```
|
||||||
|
|
||||||
|
It will be used to send a summary of every session if there are any containers that were updated or which failed to update.
|
||||||
|
|
||||||
|
!!! note "Skipping notifications"
|
||||||
|
Whenever the result of applying the template results in an empty string, no notifications will
|
||||||
|
be sent. This is by default used to limit the notifications to only be sent when there something noteworthy occurred.
|
||||||
|
|
||||||
|
You can replace `{{- if ( or .Updated .Failed ) -}}` with any logic you want to decide when to send the notifications.
|
||||||
|
|
||||||
|
Example using a custom report template that always sends a session report after each run:
|
||||||
|
|
||||||
|
=== "docker run"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name watchtower \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-e WATCHTOWER_NOTIFICATION_REPORT="true"
|
||||||
|
-e WATCHTOWER_NOTIFICATION_URL="discord://token@channel slack://watchtower@token-a/token-b/token-c" \
|
||||||
|
-e WATCHTOWER_NOTIFICATION_TEMPLATE="
|
||||||
|
{{- if .Report -}}
|
||||||
|
{{- with .Report -}}
|
||||||
|
{{len .Scanned}} Scanned, {{len .Updated}} Updated, {{len .Failed}} Failed
|
||||||
|
{{- range .Updated}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.CurrentImageID.ShortID}} updated to {{.LatestImageID.ShortID}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Fresh}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Skipped}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Failed}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{range .Entries -}}{{.Message}}{{"\n"}}{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
" \
|
||||||
|
containrrr/watchtower
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "docker-compose"
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
env:
|
||||||
|
WATCHTOWER_NOTIFICATION_REPORT: "true"
|
||||||
|
WATCHTOWER_NOTIFICATION_URL: >
|
||||||
|
discord://token@channel
|
||||||
|
slack://watchtower@token-a/token-b/token-c
|
||||||
|
WATCHTOWER_NOTIFICATION_TEMPLATE: |
|
||||||
|
{{- if .Report -}}
|
||||||
|
{{- with .Report -}}
|
||||||
|
{{len .Scanned}} Scanned, {{len .Updated}} Updated, {{len .Failed}} Failed
|
||||||
|
{{- range .Updated}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.CurrentImageID.ShortID}} updated to {{.LatestImageID.ShortID}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Fresh}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Skipped}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range .Failed}}
|
||||||
|
- {{.Name}} ({{.ImageName}}): {{.State}}: {{.Error}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{range .Entries -}}{{.Message}}{{"\n"}}{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Legacy notifications
|
||||||
|
|
||||||
|
For backwards compatibility, the notifications can also be configured using legacy notification options. These will automatically be converted to shoutrrr URLs when used.
|
||||||
|
The types of notifications to send are set by passing a comma-separated list of values to the `--notifications` option
|
||||||
|
(or corresponding environment variable `WATCHTOWER_NOTIFICATIONS`), which has the following valid values:
|
||||||
|
|
||||||
|
- `email` to send notifications via e-mail
|
||||||
|
- `slack` to send notifications through a Slack webhook
|
||||||
|
- `msteams` to send notifications via MSTeams webhook
|
||||||
|
- `gotify` to send notifications via Gotify
|
||||||
|
|
||||||
|
### `notify-upgrade`
|
||||||
|
If watchtower is started with `notify-upgrade` as it's first argument, it will generate a .env file with your current legacy notification options converted to shoutrrr URLs.
|
||||||
|
|
||||||
|
=== "docker run"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker run -d \
|
||||||
|
--name watchtower \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-e WATCHTOWER_NOTIFICATIONS=slack \
|
||||||
|
-e WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL="https://hooks.slack.com/services/xxx/yyyyyyyyyyyyyyy" \
|
||||||
|
containrrr/watchtower \
|
||||||
|
notify-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "docker-compose.yml"
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
env:
|
||||||
|
WATCHTOWER_NOTIFICATIONS: slack
|
||||||
|
WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL: https://hooks.slack.com/services/xxx/yyyyyyyyyyyyyyy
|
||||||
|
command: notify-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
You can then copy this file from the container (a message with the full command to do so will be logged) and use it with your current setup:
|
||||||
|
|
||||||
|
=== "docker run"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker run -d \
|
||||||
|
--name watchtower \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
--env-file watchtower-notifications.env \
|
||||||
|
containrrr/watchtower
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "docker-compose.yml"
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
env_file:
|
||||||
|
- watchtower-notifications.env
|
||||||
|
```
|
||||||
|
|
||||||
### Email
|
### Email
|
||||||
|
|
||||||
|
@ -177,41 +383,3 @@ docker run -d \
|
||||||
|
|
||||||
If you want to disable TLS verification for the Gotify instance, you can use either `-e WATCHTOWER_NOTIFICATION_GOTIFY_TLS_SKIP_VERIFY=true` or `--notification-gotify-tls-skip-verify`.
|
If you want to disable TLS verification for the Gotify instance, you can use either `-e WATCHTOWER_NOTIFICATION_GOTIFY_TLS_SKIP_VERIFY=true` or `--notification-gotify-tls-skip-verify`.
|
||||||
|
|
||||||
### [containrrr/shoutrrr](https://github.com/containrrr/shoutrrr)
|
|
||||||
|
|
||||||
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. 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
|
|
||||||
URLs. (See example below)
|
|
||||||
|
|
||||||
You can customize the message posted by setting a template.
|
|
||||||
|
|
||||||
- `--notification-template` (env. `WATCHTOWER_NOTIFICATION_TEMPLATE`): The template used for the message.
|
|
||||||
|
|
||||||
The template is a Go [template](https://golang.org/pkg/text/template/) and that format a list
|
|
||||||
of [log entries](https://pkg.go.dev/github.com/sirupsen/logrus?tab=doc#Entry).
|
|
||||||
|
|
||||||
The default value if not set is `{{range .}}{{.Message}}{{println}}{{end}}`. The example below uses a template that also
|
|
||||||
outputs timestamp and log level.
|
|
||||||
|
|
||||||
!!! tip "Custom date format"
|
|
||||||
If you want to adjust the date/time format it must show how the
|
|
||||||
[reference time](https://golang.org/pkg/time/#pkg-constants) (_Mon Jan 2 15:04:05 MST 2006_) would be displayed in your
|
|
||||||
custom format.
|
|
||||||
i.e., The day of the year has to be 1, the month has to be 2 (february), the hour 3 (or 15 for 24h time) etc.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -d \
|
|
||||||
--name watchtower \
|
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
||||||
-e WATCHTOWER_NOTIFICATIONS=shoutrrr \
|
|
||||||
-e WATCHTOWER_NOTIFICATION_URL="discord://token@channel slack://watchtower@token-a/token-b/token-c" \
|
|
||||||
-e WATCHTOWER_NOTIFICATION_TEMPLATE="{{range .}}{{.Time.Format \"2006-01-02 15:04:05\"}} ({{.Level}}): {{.Message}}{{println}}{{end}}" \
|
|
||||||
containrrr/watchtower
|
|
||||||
```
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -8,7 +8,6 @@ require (
|
||||||
github.com/docker/distribution v2.8.1+incompatible
|
github.com/docker/distribution v2.8.1+incompatible
|
||||||
github.com/docker/docker v20.10.20+incompatible
|
github.com/docker/docker v20.10.20+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07
|
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/gomega v1.22.1
|
github.com/onsi/gomega v1.22.1
|
||||||
github.com/prometheus/client_golang v1.13.0
|
github.com/prometheus/client_golang v1.13.0
|
||||||
|
@ -36,7 +35,6 @@ require (
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/go-cmp v0.5.8 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22 // indirect
|
|
||||||
github.com/magiconair/properties v1.8.6 // indirect
|
github.com/magiconair/properties v1.8.6 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -204,10 +204,6 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
|
||||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
|
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
|
||||||
github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22 h1:jKUP9TQ0c7X3w6+IPyMit07RE42MtTWNd77sN2cHngQ=
|
|
||||||
github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22/go.mod h1:u0Jo4f2dNlTJeeOywkM6bLwxq6gC3pZ9rEFHn3AhTdk=
|
|
||||||
github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07 h1:+kBG/8rjCa6vxJZbUjAiE4MQmBEBYc8nLEb51frnvBY=
|
|
||||||
github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07/go.mod h1:j1kV/8f3jowErEq4XyeypkCdvg5EeHkf0YCKCcq5Ybo=
|
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
|
29
pkg/container/cgroup_id.go
Normal file
29
pkg/container/cgroup_id.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/containrrr/watchtower/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dockerContainerPattern = regexp.MustCompile(`[0-9]+:.*:/docker/([a-f|0-9]{64})`)
|
||||||
|
|
||||||
|
// GetRunningContainerID tries to resolve the current container ID from the current process cgroup information
|
||||||
|
func GetRunningContainerID() (cid types.ContainerID, err error) {
|
||||||
|
file, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", os.Getpid()))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return getRunningContainerIDFromString(string(file)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRunningContainerIDFromString(s string) types.ContainerID {
|
||||||
|
matches := dockerContainerPattern.FindStringSubmatch(s)
|
||||||
|
if len(matches) < 2 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return types.ContainerID(matches[1])
|
||||||
|
}
|
40
pkg/container/cgroup_id_test.go
Normal file
40
pkg/container/cgroup_id_test.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("GetRunningContainerID", func() {
|
||||||
|
When("a matching container ID is found", func() {
|
||||||
|
It("should return that container ID", func() {
|
||||||
|
cid := getRunningContainerIDFromString(`
|
||||||
|
15:name=systemd:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
14:misc:/
|
||||||
|
13:rdma:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
12:pids:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
11:hugetlb:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
10:net_prio:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
9:perf_event:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
8:net_cls:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
7:freezer:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
6:devices:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
5:blkio:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
4:cpuacct:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
3:cpu:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
2:cpuset:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
1:memory:/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
0::/docker/991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377
|
||||||
|
`)
|
||||||
|
Expect(cid).To(BeEquivalentTo(`991b6b42691449d3ce90192ff9f006863dcdafc6195e227aeefa298235004377`))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
When("no matching container ID could be found", func() {
|
||||||
|
It("should return that container ID", func() {
|
||||||
|
cid := getRunningContainerIDFromString(`14:misc:/`)
|
||||||
|
Expect(cid).To(BeEmpty())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
//
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Package container contains code related to dealing with docker containers
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -15,17 +15,16 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type emailTypeNotifier struct {
|
type emailTypeNotifier struct {
|
||||||
From, To string
|
From, To string
|
||||||
Server, User, Password, SubjectTag string
|
Server, User, Password string
|
||||||
Port int
|
Port int
|
||||||
tlsSkipVerify bool
|
tlsSkipVerify bool
|
||||||
entries []*log.Entry
|
entries []*log.Entry
|
||||||
logLevels []log.Level
|
delay time.Duration
|
||||||
delay time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
|
func newEmailNotifier(c *cobra.Command) t.ConvertibleNotifier {
|
||||||
flags := c.PersistentFlags()
|
flags := c.Flags()
|
||||||
|
|
||||||
from, _ := flags.GetString("notification-email-from")
|
from, _ := flags.GetString("notification-email-from")
|
||||||
to, _ := flags.GetString("notification-email-to")
|
to, _ := flags.GetString("notification-email-to")
|
||||||
|
@ -35,7 +34,6 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
|
||||||
port, _ := flags.GetInt("notification-email-server-port")
|
port, _ := flags.GetInt("notification-email-server-port")
|
||||||
tlsSkipVerify, _ := flags.GetBool("notification-email-server-tls-skip-verify")
|
tlsSkipVerify, _ := flags.GetBool("notification-email-server-tls-skip-verify")
|
||||||
delay, _ := flags.GetInt("notification-email-delay")
|
delay, _ := flags.GetInt("notification-email-delay")
|
||||||
subjecttag, _ := flags.GetString("notification-email-subjecttag")
|
|
||||||
|
|
||||||
n := &emailTypeNotifier{
|
n := &emailTypeNotifier{
|
||||||
entries: []*log.Entry{},
|
entries: []*log.Entry{},
|
||||||
|
@ -46,22 +44,19 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
|
||||||
Password: password,
|
Password: password,
|
||||||
Port: port,
|
Port: port,
|
||||||
tlsSkipVerify: tlsSkipVerify,
|
tlsSkipVerify: tlsSkipVerify,
|
||||||
logLevels: acceptedLogLevels,
|
|
||||||
delay: time.Duration(delay) * time.Second,
|
delay: time.Duration(delay) * time.Second,
|
||||||
SubjectTag: subjecttag,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emailTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
|
func (e *emailTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
conf := &shoutrrrSmtp.Config{
|
conf := &shoutrrrSmtp.Config{
|
||||||
FromAddress: e.From,
|
FromAddress: e.From,
|
||||||
FromName: "Watchtower",
|
FromName: "Watchtower",
|
||||||
ToAddresses: []string{e.To},
|
ToAddresses: []string{e.To},
|
||||||
Port: uint16(e.Port),
|
Port: uint16(e.Port),
|
||||||
Host: e.Server,
|
Host: e.Server,
|
||||||
Subject: e.getSubject(c, title),
|
|
||||||
Username: e.User,
|
Username: e.User,
|
||||||
Password: e.Password,
|
Password: e.Password,
|
||||||
UseStartTLS: !e.tlsSkipVerify,
|
UseStartTLS: !e.tlsSkipVerify,
|
||||||
|
@ -84,11 +79,3 @@ func (e *emailTypeNotifier) GetURL(c *cobra.Command, title string) (string, erro
|
||||||
func (e *emailTypeNotifier) GetDelay() time.Duration {
|
func (e *emailTypeNotifier) GetDelay() time.Duration {
|
||||||
return e.delay
|
return e.delay
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emailTypeNotifier) getSubject(_ *cobra.Command, title string) string {
|
|
||||||
if e.SubjectTag != "" {
|
|
||||||
return e.SubjectTag + " " + title
|
|
||||||
}
|
|
||||||
|
|
||||||
return title
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,11 +19,10 @@ type gotifyTypeNotifier struct {
|
||||||
gotifyURL string
|
gotifyURL string
|
||||||
gotifyAppToken string
|
gotifyAppToken string
|
||||||
gotifyInsecureSkipVerify bool
|
gotifyInsecureSkipVerify bool
|
||||||
logLevels []log.Level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGotifyNotifier(c *cobra.Command, levels []log.Level) t.ConvertibleNotifier {
|
func newGotifyNotifier(c *cobra.Command) t.ConvertibleNotifier {
|
||||||
flags := c.PersistentFlags()
|
flags := c.Flags()
|
||||||
|
|
||||||
apiURL := getGotifyURL(flags)
|
apiURL := getGotifyURL(flags)
|
||||||
token := getGotifyToken(flags)
|
token := getGotifyToken(flags)
|
||||||
|
@ -34,7 +33,6 @@ func newGotifyNotifier(c *cobra.Command, levels []log.Level) t.ConvertibleNotifi
|
||||||
gotifyURL: apiURL,
|
gotifyURL: apiURL,
|
||||||
gotifyAppToken: token,
|
gotifyAppToken: token,
|
||||||
gotifyInsecureSkipVerify: skipVerify,
|
gotifyInsecureSkipVerify: skipVerify,
|
||||||
logLevels: levels,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return n
|
return n
|
||||||
|
@ -62,7 +60,7 @@ func getGotifyURL(flags *pflag.FlagSet) string {
|
||||||
return gotifyURL
|
return gotifyURL
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *gotifyTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
|
func (n *gotifyTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
apiURL, err := url.Parse(n.gotifyURL)
|
apiURL, err := url.Parse(n.gotifyURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -72,7 +70,6 @@ func (n *gotifyTypeNotifier) GetURL(c *cobra.Command, title string) (string, err
|
||||||
Host: apiURL.Host,
|
Host: apiURL.Host,
|
||||||
Path: apiURL.Path,
|
Path: apiURL.Path,
|
||||||
DisableTLS: apiURL.Scheme == "http",
|
DisableTLS: apiURL.Scheme == "http",
|
||||||
Title: title,
|
|
||||||
Token: n.gotifyAppToken,
|
Token: n.gotifyAppToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,12 @@ const (
|
||||||
|
|
||||||
type msTeamsTypeNotifier struct {
|
type msTeamsTypeNotifier struct {
|
||||||
webHookURL string
|
webHookURL string
|
||||||
levels []log.Level
|
|
||||||
data bool
|
data bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
|
func newMsTeamsNotifier(cmd *cobra.Command) t.ConvertibleNotifier {
|
||||||
|
|
||||||
flags := cmd.PersistentFlags()
|
flags := cmd.Flags()
|
||||||
|
|
||||||
webHookURL, _ := flags.GetString("notification-msteams-hook")
|
webHookURL, _ := flags.GetString("notification-msteams-hook")
|
||||||
if len(webHookURL) <= 0 {
|
if len(webHookURL) <= 0 {
|
||||||
|
@ -30,7 +29,6 @@ func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Con
|
||||||
|
|
||||||
withData, _ := flags.GetBool("notification-msteams-data")
|
withData, _ := flags.GetBool("notification-msteams-data")
|
||||||
n := &msTeamsTypeNotifier{
|
n := &msTeamsTypeNotifier{
|
||||||
levels: acceptedLogLevels,
|
|
||||||
webHookURL: webHookURL,
|
webHookURL: webHookURL,
|
||||||
data: withData,
|
data: withData,
|
||||||
}
|
}
|
||||||
|
@ -38,7 +36,7 @@ func newMsTeamsNotifier(cmd *cobra.Command, acceptedLogLevels []log.Level) t.Con
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
|
func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
webhookURL, err := url.Parse(n.webHookURL)
|
webhookURL, err := url.Parse(n.webHookURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -50,7 +48,6 @@ func (n *msTeamsTypeNotifier) GetURL(c *cobra.Command, title string) (string, er
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Color = ColorHex
|
config.Color = ColorHex
|
||||||
config.Title = title
|
|
||||||
|
|
||||||
return config.GetURL().String(), nil
|
return config.GetURL().String(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ty "github.com/containrrr/watchtower/pkg/types"
|
ty "github.com/containrrr/watchtower/pkg/types"
|
||||||
"github.com/johntdyer/slackrus"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewNotifier creates and returns a new Notifier, using global configuration.
|
// NewNotifier creates and returns a new Notifier, using global configuration.
|
||||||
func NewNotifier(c *cobra.Command) ty.Notifier {
|
func NewNotifier(c *cobra.Command) ty.Notifier {
|
||||||
f := c.PersistentFlags()
|
f := c.Flags()
|
||||||
|
|
||||||
level, _ := f.GetString("notifications-level")
|
level, _ := f.GetString("notifications-level")
|
||||||
logLevel, err := log.ParseLevel(level)
|
logLevel, err := log.ParseLevel(level)
|
||||||
|
@ -21,25 +20,19 @@ func NewNotifier(c *cobra.Command) ty.Notifier {
|
||||||
log.Fatalf("Notifications invalid log level: %s", err.Error())
|
log.Fatalf("Notifications invalid log level: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
levels := slackrus.LevelThreshold(logLevel)
|
|
||||||
// slackrus does not allow log level TRACE, even though it's an accepted log level for logrus
|
|
||||||
if len(levels) == 0 {
|
|
||||||
log.Fatalf("Unsupported notification log level provided: %s", level)
|
|
||||||
}
|
|
||||||
|
|
||||||
reportTemplate, _ := f.GetBool("notification-report")
|
reportTemplate, _ := f.GetBool("notification-report")
|
||||||
stdout, _ := f.GetBool("notification-log-stdout")
|
stdout, _ := f.GetBool("notification-log-stdout")
|
||||||
tplString, _ := f.GetString("notification-template")
|
tplString, _ := f.GetString("notification-template")
|
||||||
urls, _ := f.GetStringArray("notification-url")
|
urls, _ := f.GetStringArray("notification-url")
|
||||||
|
|
||||||
data := GetTemplateData(c)
|
data := GetTemplateData(c)
|
||||||
urls, delay := AppendLegacyUrls(urls, c, data.Title)
|
urls, delay := AppendLegacyUrls(urls, c)
|
||||||
|
|
||||||
return newShoutrrrNotifier(tplString, levels, !reportTemplate, data, delay, stdout, urls...)
|
return createNotifier(urls, logLevel, tplString, !reportTemplate, data, stdout, delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendLegacyUrls creates shoutrrr equivalent URLs from legacy notification flags
|
// AppendLegacyUrls creates shoutrrr equivalent URLs from legacy notification flags
|
||||||
func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string, time.Duration) {
|
func AppendLegacyUrls(urls []string, cmd *cobra.Command) ([]string, time.Duration) {
|
||||||
|
|
||||||
// Parse types and create notifiers.
|
// Parse types and create notifiers.
|
||||||
types, err := cmd.Flags().GetStringSlice("notifications")
|
types, err := cmd.Flags().GetStringSlice("notifications")
|
||||||
|
@ -56,13 +49,13 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case emailType:
|
case emailType:
|
||||||
legacyNotifier = newEmailNotifier(cmd, []log.Level{})
|
legacyNotifier = newEmailNotifier(cmd)
|
||||||
case slackType:
|
case slackType:
|
||||||
legacyNotifier = newSlackNotifier(cmd, []log.Level{})
|
legacyNotifier = newSlackNotifier(cmd)
|
||||||
case msTeamsType:
|
case msTeamsType:
|
||||||
legacyNotifier = newMsTeamsNotifier(cmd, []log.Level{})
|
legacyNotifier = newMsTeamsNotifier(cmd)
|
||||||
case gotifyType:
|
case gotifyType:
|
||||||
legacyNotifier = newGotifyNotifier(cmd, []log.Level{})
|
legacyNotifier = newGotifyNotifier(cmd)
|
||||||
case shoutrrrType:
|
case shoutrrrType:
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
|
@ -71,7 +64,7 @@ func AppendLegacyUrls(urls []string, cmd *cobra.Command, title string) ([]string
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
shoutrrrURL, err := legacyNotifier.GetURL(cmd, title)
|
shoutrrrURL, err := legacyNotifier.GetURL(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("failed to create notification config: ", err)
|
log.Fatal("failed to create notification config: ", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package notifications_test
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containrrr/watchtower/cmd"
|
"github.com/containrrr/watchtower/cmd"
|
||||||
|
@ -147,11 +146,9 @@ var _ = Describe("notifications", func() {
|
||||||
channel := "123456789"
|
channel := "123456789"
|
||||||
token := "abvsihdbau"
|
token := "abvsihdbau"
|
||||||
color := notifications.ColorInt
|
color := notifications.ColorInt
|
||||||
data := notifications.GetTemplateData(command)
|
|
||||||
title := url.QueryEscape(data.Title)
|
|
||||||
username := "containrrrbot"
|
username := "containrrrbot"
|
||||||
iconURL := "https://containrrr.dev/watchtower-sq180.png"
|
iconURL := "https://containrrr.dev/watchtower-sq180.png"
|
||||||
expected := fmt.Sprintf("discord://%s@%s?color=0x%x&colordebug=0x0&colorerror=0x0&colorinfo=0x0&colorwarn=0x0&title=%s&username=watchtower", token, channel, color, title)
|
expected := fmt.Sprintf("discord://%s@%s?color=0x%x&colordebug=0x0&colorerror=0x0&colorinfo=0x0&colorwarn=0x0&username=watchtower", token, channel, color)
|
||||||
buildArgs := func(url string) []string {
|
buildArgs := func(url string) []string {
|
||||||
return []string{
|
return []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
|
@ -172,7 +169,7 @@ var _ = Describe("notifications", func() {
|
||||||
When("icon URL and username are specified", func() {
|
When("icon URL and username are specified", func() {
|
||||||
It("should return the expected URL", func() {
|
It("should return the expected URL", func() {
|
||||||
hookURL := fmt.Sprintf("https://%s/api/webhooks/%s/%s/slack", "discord.com", channel, token)
|
hookURL := fmt.Sprintf("https://%s/api/webhooks/%s/%s/slack", "discord.com", channel, token)
|
||||||
expectedOutput := fmt.Sprintf("discord://%s@%s?avatar=%s&color=0x%x&colordebug=0x0&colorerror=0x0&colorinfo=0x0&colorwarn=0x0&title=%s&username=%s", token, channel, url.QueryEscape(iconURL), color, title, username)
|
expectedOutput := fmt.Sprintf("discord://%s@%s?avatar=%s&color=0x%x&colordebug=0x0&colorerror=0x0&colorinfo=0x0&colorwarn=0x0&username=%s", token, channel, url.QueryEscape(iconURL), color, username)
|
||||||
expectedDelay := time.Duration(7) * time.Second
|
expectedDelay := time.Duration(7) * time.Second
|
||||||
args := []string{
|
args := []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
|
@ -199,8 +196,6 @@ var _ = Describe("notifications", func() {
|
||||||
tokenB := "BBBBBBBBB"
|
tokenB := "BBBBBBBBB"
|
||||||
tokenC := "123456789123456789123456"
|
tokenC := "123456789123456789123456"
|
||||||
color := url.QueryEscape(notifications.ColorHex)
|
color := url.QueryEscape(notifications.ColorHex)
|
||||||
data := notifications.GetTemplateData(command)
|
|
||||||
title := url.QueryEscape(data.Title)
|
|
||||||
iconURL := "https://containrrr.dev/watchtower-sq180.png"
|
iconURL := "https://containrrr.dev/watchtower-sq180.png"
|
||||||
iconEmoji := "whale"
|
iconEmoji := "whale"
|
||||||
|
|
||||||
|
@ -208,7 +203,7 @@ var _ = Describe("notifications", func() {
|
||||||
It("should return the expected URL", func() {
|
It("should return the expected URL", func() {
|
||||||
|
|
||||||
hookURL := fmt.Sprintf("https://hooks.slack.com/services/%s/%s/%s", tokenA, tokenB, tokenC)
|
hookURL := fmt.Sprintf("https://hooks.slack.com/services/%s/%s/%s", tokenA, tokenB, tokenC)
|
||||||
expectedOutput := fmt.Sprintf("slack://hook:%s-%s-%s@webhook?botname=%s&color=%s&icon=%s&title=%s", tokenA, tokenB, tokenC, username, color, url.QueryEscape(iconURL), title)
|
expectedOutput := fmt.Sprintf("slack://hook:%s-%s-%s@webhook?botname=%s&color=%s&icon=%s", tokenA, tokenB, tokenC, username, color, url.QueryEscape(iconURL))
|
||||||
expectedDelay := time.Duration(7) * time.Second
|
expectedDelay := time.Duration(7) * time.Second
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
|
@ -231,7 +226,7 @@ var _ = Describe("notifications", func() {
|
||||||
When("icon emoji is specified", func() {
|
When("icon emoji is specified", func() {
|
||||||
It("should return the expected URL", func() {
|
It("should return the expected URL", func() {
|
||||||
hookURL := fmt.Sprintf("https://hooks.slack.com/services/%s/%s/%s", tokenA, tokenB, tokenC)
|
hookURL := fmt.Sprintf("https://hooks.slack.com/services/%s/%s/%s", tokenA, tokenB, tokenC)
|
||||||
expectedOutput := fmt.Sprintf("slack://hook:%s-%s-%s@webhook?botname=%s&color=%s&icon=%s&title=%s", tokenA, tokenB, tokenC, username, color, iconEmoji, title)
|
expectedOutput := fmt.Sprintf("slack://hook:%s-%s-%s@webhook?botname=%s&color=%s&icon=%s", tokenA, tokenB, tokenC, username, color, iconEmoji)
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
|
@ -258,10 +253,8 @@ var _ = Describe("notifications", func() {
|
||||||
|
|
||||||
token := "aaa"
|
token := "aaa"
|
||||||
host := "shoutrrr.local"
|
host := "shoutrrr.local"
|
||||||
data := notifications.GetTemplateData(command)
|
|
||||||
title := url.QueryEscape(data.Title)
|
|
||||||
|
|
||||||
expectedOutput := fmt.Sprintf("gotify://%s/%s?title=%s", host, token, title)
|
expectedOutput := fmt.Sprintf("gotify://%s/%s?title=", host, token)
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
|
@ -287,11 +280,9 @@ var _ = Describe("notifications", func() {
|
||||||
tokenB := "33333333012222222222333333333344"
|
tokenB := "33333333012222222222333333333344"
|
||||||
tokenC := "44444444-4444-4444-8444-cccccccccccc"
|
tokenC := "44444444-4444-4444-8444-cccccccccccc"
|
||||||
color := url.QueryEscape(notifications.ColorHex)
|
color := url.QueryEscape(notifications.ColorHex)
|
||||||
data := notifications.GetTemplateData(command)
|
|
||||||
title := url.QueryEscape(data.Title)
|
|
||||||
|
|
||||||
hookURL := fmt.Sprintf("https://outlook.office.com/webhook/%s/IncomingWebhook/%s/%s", tokenA, tokenB, tokenC)
|
hookURL := fmt.Sprintf("https://outlook.office.com/webhook/%s/IncomingWebhook/%s/%s", tokenA, tokenB, tokenC)
|
||||||
expectedOutput := fmt.Sprintf("teams://%s/%s/%s?color=%s&title=%s", tokenA, tokenB, tokenC, color, title)
|
expectedOutput := fmt.Sprintf("teams://%s/%s/%s?color=%s", tokenA, tokenB, tokenC, color)
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--notifications",
|
"--notifications",
|
||||||
|
@ -362,18 +353,12 @@ var _ = Describe("notifications", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
func buildExpectedURL(username string, password string, host string, port int, from string, to string, auth string) string {
|
func buildExpectedURL(username string, password string, host string, port int, from string, to string, auth string) string {
|
||||||
hostname, err := os.Hostname()
|
var template = "smtp://%s:%s@%s:%d/?auth=%s&fromaddress=%s&fromname=Watchtower&subject=&toaddresses=%s"
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
subject := fmt.Sprintf("Watchtower updates on %s", hostname)
|
|
||||||
|
|
||||||
var template = "smtp://%s:%s@%s:%d/?auth=%s&fromaddress=%s&fromname=Watchtower&subject=%s&toaddresses=%s"
|
|
||||||
return fmt.Sprintf(template,
|
return fmt.Sprintf(template,
|
||||||
url.QueryEscape(username),
|
url.QueryEscape(username),
|
||||||
url.QueryEscape(password),
|
url.QueryEscape(password),
|
||||||
host, port, auth,
|
host, port, auth,
|
||||||
url.QueryEscape(from),
|
url.QueryEscape(from),
|
||||||
url.QueryEscape(subject),
|
|
||||||
url.QueryEscape(to))
|
url.QueryEscape(to))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,8 +370,7 @@ func testURL(args []string, expectedURL string, expectedDelay time.Duration) {
|
||||||
|
|
||||||
Expect(command.ParseFlags(args)).To(Succeed())
|
Expect(command.ParseFlags(args)).To(Succeed())
|
||||||
|
|
||||||
data := notifications.GetTemplateData(command)
|
urls, delay := notifications.AppendLegacyUrls([]string{}, command)
|
||||||
urls, delay := notifications.AppendLegacyUrls([]string{}, command, data.Title)
|
|
||||||
|
|
||||||
Expect(urls).To(ContainElement(expectedURL))
|
Expect(urls).To(ContainElement(expectedURL))
|
||||||
Expect(delay).To(Equal(expectedDelay))
|
Expect(delay).To(Equal(expectedDelay))
|
||||||
|
|
|
@ -32,13 +32,15 @@ type shoutrrrTypeNotifier struct {
|
||||||
Urls []string
|
Urls []string
|
||||||
Router router
|
Router router
|
||||||
entries []*log.Entry
|
entries []*log.Entry
|
||||||
logLevels []log.Level
|
logLevel log.Level
|
||||||
template *template.Template
|
template *template.Template
|
||||||
messages chan string
|
messages chan string
|
||||||
done chan bool
|
done chan bool
|
||||||
legacyTemplate bool
|
legacyTemplate bool
|
||||||
params *types.Params
|
params *types.Params
|
||||||
data StaticData
|
data StaticData
|
||||||
|
receiving bool
|
||||||
|
delay time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScheme returns the scheme part of a Shoutrrr URL
|
// GetScheme returns the scheme part of a Shoutrrr URL
|
||||||
|
@ -59,18 +61,24 @@ func (n *shoutrrrTypeNotifier) GetNames() []string {
|
||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
func newShoutrrrNotifier(tplString string, levels []log.Level, legacy bool, data StaticData, delay time.Duration, stdout bool, urls ...string) t.Notifier {
|
// GetNames returns a list of URLs for notification services that has been added
|
||||||
|
func (n *shoutrrrTypeNotifier) GetURLs() []string {
|
||||||
notifier := createNotifier(urls, levels, tplString, legacy, data, stdout)
|
return n.Urls
|
||||||
log.AddHook(notifier)
|
|
||||||
|
|
||||||
// Do the sending in a separate goroutine so we don't block the main process.
|
|
||||||
go sendNotifications(notifier, delay)
|
|
||||||
|
|
||||||
return notifier
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNotifier(urls []string, levels []log.Level, tplString string, legacy bool, data StaticData, stdout bool) *shoutrrrTypeNotifier {
|
// AddLogHook adds the notifier as a receiver of log messages and starts a go func for processing them
|
||||||
|
func (n *shoutrrrTypeNotifier) AddLogHook() {
|
||||||
|
if n.receiving {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n.receiving = true
|
||||||
|
log.AddHook(n)
|
||||||
|
|
||||||
|
// Do the sending in a separate goroutine so we don't block the main process.
|
||||||
|
go sendNotifications(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNotifier(urls []string, level log.Level, tplString string, legacy bool, data StaticData, stdout bool, delay time.Duration) *shoutrrrTypeNotifier {
|
||||||
tpl, err := getShoutrrrTemplate(tplString, legacy)
|
tpl, err := getShoutrrrTemplate(tplString, legacy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not use configured notification template: %s. Using default template", err)
|
log.Errorf("Could not use configured notification template: %s. Using default template", err)
|
||||||
|
@ -97,7 +105,7 @@ func createNotifier(urls []string, levels []log.Level, tplString string, legacy
|
||||||
Router: r,
|
Router: r,
|
||||||
messages: make(chan string, 1),
|
messages: make(chan string, 1),
|
||||||
done: make(chan bool),
|
done: make(chan bool),
|
||||||
logLevels: levels,
|
logLevel: level,
|
||||||
template: tpl,
|
template: tpl,
|
||||||
legacyTemplate: legacy,
|
legacyTemplate: legacy,
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -105,9 +113,9 @@ func createNotifier(urls []string, levels []log.Level, tplString string, legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendNotifications(n *shoutrrrTypeNotifier, delay time.Duration) {
|
func sendNotifications(n *shoutrrrTypeNotifier) {
|
||||||
for msg := range n.messages {
|
for msg := range n.messages {
|
||||||
time.Sleep(delay)
|
time.Sleep(n.delay)
|
||||||
errs := n.Router.Send(msg, n.params)
|
errs := n.Router.Send(msg, n.params)
|
||||||
|
|
||||||
for i, err := range errs {
|
for i, err := range errs {
|
||||||
|
@ -180,7 +188,7 @@ func (n *shoutrrrTypeNotifier) Close() {
|
||||||
|
|
||||||
// Levels return what log levels trigger notifications
|
// Levels return what log levels trigger notifications
|
||||||
func (n *shoutrrrTypeNotifier) Levels() []log.Level {
|
func (n *shoutrrrTypeNotifier) Levels() []log.Level {
|
||||||
return n.logLevels
|
return log.AllLevels[:n.logLevel+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire is the hook that logrus calls on a new log message
|
// Fire is the hook that logrus calls on a new log message
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var allButTrace = logrus.AllLevels[0:logrus.TraceLevel]
|
var allButTrace = logrus.DebugLevel
|
||||||
|
|
||||||
var legacyMockData = Data{
|
var legacyMockData = Data{
|
||||||
Entries: []*logrus.Entry{
|
Entries: []*logrus.Entry{
|
||||||
|
@ -83,6 +83,30 @@ updt1 (mock/updt1:latest): Updated
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
When("adding a log hook", func() {
|
||||||
|
When("it has not been added before", func() {
|
||||||
|
It("should be added to the logrus hooks", func() {
|
||||||
|
level := logrus.TraceLevel
|
||||||
|
hooksBefore := len(logrus.StandardLogger().Hooks[level])
|
||||||
|
shoutrrr := createNotifier([]string{}, level, "", true, StaticData{}, false, time.Second)
|
||||||
|
shoutrrr.AddLogHook()
|
||||||
|
hooksAfter := len(logrus.StandardLogger().Hooks[level])
|
||||||
|
Expect(hooksAfter).To(BeNumerically(">", hooksBefore))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
When("it is being added a second time", func() {
|
||||||
|
It("should not be added to the logrus hooks", func() {
|
||||||
|
level := logrus.TraceLevel
|
||||||
|
shoutrrr := createNotifier([]string{}, level, "", true, StaticData{}, false, time.Second)
|
||||||
|
shoutrrr.AddLogHook()
|
||||||
|
hooksBefore := len(logrus.StandardLogger().Hooks[level])
|
||||||
|
shoutrrr.AddLogHook()
|
||||||
|
hooksAfter := len(logrus.StandardLogger().Hooks[level])
|
||||||
|
Expect(hooksAfter).To(Equal(hooksBefore))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
When("using legacy templates", func() {
|
When("using legacy templates", func() {
|
||||||
|
|
||||||
When("no custom template is provided", func() {
|
When("no custom template is provided", func() {
|
||||||
|
@ -90,7 +114,7 @@ updt1 (mock/updt1:latest): Updated
|
||||||
cmd := new(cobra.Command)
|
cmd := new(cobra.Command)
|
||||||
flags.RegisterNotificationFlags(cmd)
|
flags.RegisterNotificationFlags(cmd)
|
||||||
|
|
||||||
shoutrrr := createNotifier([]string{}, logrus.AllLevels, "", true, StaticData{}, false)
|
shoutrrr := createNotifier([]string{}, logrus.TraceLevel, "", true, StaticData{}, false, time.Second)
|
||||||
|
|
||||||
entries := []*logrus.Entry{
|
entries := []*logrus.Entry{
|
||||||
{
|
{
|
||||||
|
@ -245,7 +269,7 @@ Turns out everything is on fire
|
||||||
When("batching notifications", func() {
|
When("batching notifications", func() {
|
||||||
When("no messages are queued", func() {
|
When("no messages are queued", func() {
|
||||||
It("should not send any notification", func() {
|
It("should not send any notification", func() {
|
||||||
shoutrrr := newShoutrrrNotifier("", allButTrace, true, StaticData{}, time.Duration(0), false, "logger://")
|
shoutrrr := createNotifier([]string{"logger://"}, allButTrace, "", true, StaticData{}, false, time.Duration(0))
|
||||||
shoutrrr.StartNotification()
|
shoutrrr.StartNotification()
|
||||||
shoutrrr.SendNotification(nil)
|
shoutrrr.SendNotification(nil)
|
||||||
Consistently(logBuffer).ShouldNot(gbytes.Say(`Shoutrrr:`))
|
Consistently(logBuffer).ShouldNot(gbytes.Say(`Shoutrrr:`))
|
||||||
|
@ -253,7 +277,8 @@ Turns out everything is on fire
|
||||||
})
|
})
|
||||||
When("at least one message is queued", func() {
|
When("at least one message is queued", func() {
|
||||||
It("should send a notification", func() {
|
It("should send a notification", func() {
|
||||||
shoutrrr := newShoutrrrNotifier("", allButTrace, true, StaticData{}, time.Duration(0), false, "logger://")
|
shoutrrr := createNotifier([]string{"logger://"}, allButTrace, "", true, StaticData{}, false, time.Duration(0))
|
||||||
|
shoutrrr.AddLogHook()
|
||||||
shoutrrr.StartNotification()
|
shoutrrr.StartNotification()
|
||||||
logrus.Info("This log message is sponsored by ContainrrrVPN")
|
logrus.Info("This log message is sponsored by ContainrrrVPN")
|
||||||
shoutrrr.SendNotification(nil)
|
shoutrrr.SendNotification(nil)
|
||||||
|
@ -267,7 +292,7 @@ Turns out everything is on fire
|
||||||
shoutrrr := createNotifier([]string{"logger://"}, allButTrace, "", true, StaticData{
|
shoutrrr := createNotifier([]string{"logger://"}, allButTrace, "", true, StaticData{
|
||||||
Host: "test.host",
|
Host: "test.host",
|
||||||
Title: "",
|
Title: "",
|
||||||
}, false)
|
}, false, time.Second)
|
||||||
_, found := shoutrrr.params.Title()
|
_, found := shoutrrr.params.Title()
|
||||||
Expect(found).ToNot(BeTrue())
|
Expect(found).ToNot(BeTrue())
|
||||||
})
|
})
|
||||||
|
@ -321,13 +346,14 @@ func sendNotificationsWithBlockingRouter(legacy bool) (*shoutrrrTypeNotifier, *b
|
||||||
Router: router,
|
Router: router,
|
||||||
legacyTemplate: legacy,
|
legacyTemplate: legacy,
|
||||||
params: &types.Params{},
|
params: &types.Params{},
|
||||||
|
delay: time.Duration(0),
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := &logrus.Entry{
|
entry := &logrus.Entry{
|
||||||
Message: "foo bar",
|
Message: "foo bar",
|
||||||
}
|
}
|
||||||
|
|
||||||
go sendNotifications(shoutrrr, time.Duration(0))
|
go sendNotifications(shoutrrr)
|
||||||
|
|
||||||
shoutrrr.StartNotification()
|
shoutrrr.StartNotification()
|
||||||
_ = shoutrrr.Fire(entry)
|
_ = shoutrrr.Fire(entry)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
shoutrrrDisco "github.com/containrrr/shoutrrr/pkg/services/discord"
|
shoutrrrDisco "github.com/containrrr/shoutrrr/pkg/services/discord"
|
||||||
shoutrrrSlack "github.com/containrrr/shoutrrr/pkg/services/slack"
|
shoutrrrSlack "github.com/containrrr/shoutrrr/pkg/services/slack"
|
||||||
t "github.com/containrrr/watchtower/pkg/types"
|
t "github.com/containrrr/watchtower/pkg/types"
|
||||||
"github.com/johntdyer/slackrus"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
@ -16,11 +15,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type slackTypeNotifier struct {
|
type slackTypeNotifier struct {
|
||||||
slackrus.SlackrusHook
|
HookURL string
|
||||||
|
Username string
|
||||||
|
Channel string
|
||||||
|
IconEmoji string
|
||||||
|
IconURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.ConvertibleNotifier {
|
func newSlackNotifier(c *cobra.Command) t.ConvertibleNotifier {
|
||||||
flags := c.PersistentFlags()
|
flags := c.Flags()
|
||||||
|
|
||||||
hookURL, _ := flags.GetString("notification-slack-hook-url")
|
hookURL, _ := flags.GetString("notification-slack-hook-url")
|
||||||
userName, _ := flags.GetString("notification-slack-identifier")
|
userName, _ := flags.GetString("notification-slack-identifier")
|
||||||
|
@ -29,19 +32,16 @@ func newSlackNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Convert
|
||||||
iconURL, _ := flags.GetString("notification-slack-icon-url")
|
iconURL, _ := flags.GetString("notification-slack-icon-url")
|
||||||
|
|
||||||
n := &slackTypeNotifier{
|
n := &slackTypeNotifier{
|
||||||
SlackrusHook: slackrus.SlackrusHook{
|
HookURL: hookURL,
|
||||||
HookURL: hookURL,
|
Username: userName,
|
||||||
Username: userName,
|
Channel: channel,
|
||||||
Channel: channel,
|
IconEmoji: emoji,
|
||||||
IconEmoji: emoji,
|
IconURL: iconURL,
|
||||||
IconURL: iconURL,
|
|
||||||
AcceptedLevels: acceptedLogLevels,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *slackTypeNotifier) GetURL(c *cobra.Command, title string) (string, error) {
|
func (s *slackTypeNotifier) GetURL(c *cobra.Command) (string, error) {
|
||||||
trimmedURL := strings.TrimRight(s.HookURL, "/")
|
trimmedURL := strings.TrimRight(s.HookURL, "/")
|
||||||
trimmedURL = strings.TrimPrefix(trimmedURL, "https://")
|
trimmedURL = strings.TrimPrefix(trimmedURL, "https://")
|
||||||
parts := strings.Split(trimmedURL, "/")
|
parts := strings.Split(trimmedURL, "/")
|
||||||
|
@ -52,7 +52,6 @@ func (s *slackTypeNotifier) GetURL(c *cobra.Command, title string) (string, erro
|
||||||
WebhookID: parts[len(parts)-3],
|
WebhookID: parts[len(parts)-3],
|
||||||
Token: parts[len(parts)-2],
|
Token: parts[len(parts)-2],
|
||||||
Color: ColorInt,
|
Color: ColorInt,
|
||||||
Title: title,
|
|
||||||
SplitLines: true,
|
SplitLines: true,
|
||||||
Username: s.Username,
|
Username: s.Username,
|
||||||
}
|
}
|
||||||
|
@ -70,7 +69,6 @@ func (s *slackTypeNotifier) GetURL(c *cobra.Command, title string) (string, erro
|
||||||
BotName: s.Username,
|
BotName: s.Username,
|
||||||
Color: ColorHex,
|
Color: ColorHex,
|
||||||
Channel: "webhook",
|
Channel: "webhook",
|
||||||
Title: title,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.IconURL != "" {
|
if s.IconURL != "" {
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertibleNotifier is a notifier capable of creating a shoutrrr URL
|
// ConvertibleNotifier is a notifier capable of creating a shoutrrr URL
|
||||||
type ConvertibleNotifier interface {
|
type ConvertibleNotifier interface {
|
||||||
GetURL(c *cobra.Command, title string) (string, error)
|
GetURL(c *cobra.Command) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelayNotifier is a notifier that might need to be delayed before sending notifications
|
// DelayNotifier is a notifier that might need to be delayed before sending notifications
|
||||||
type DelayNotifier interface {
|
type DelayNotifier interface {
|
||||||
GetDelay() time.Duration
|
GetDelay() time.Duration
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ package types
|
||||||
type Notifier interface {
|
type Notifier interface {
|
||||||
StartNotification()
|
StartNotification()
|
||||||
SendNotification(Report)
|
SendNotification(Report)
|
||||||
|
AddLogHook()
|
||||||
GetNames() []string
|
GetNames() []string
|
||||||
|
GetURLs() []string
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue