preparations for soft deprecation of legacy notification args (#1377)
Co-authored-by: Simon Aronsson <simme@arcticbit.se>pull/1451/head
parent
2102a056de
commit
cb555f539d
@ -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...))
|
||||||
|
}
|
@ -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])
|
||||||
|
}
|
@ -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,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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue