|
|
|
package flags
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestEnvConfig_Defaults(t *testing.T) {
|
|
|
|
// Unset testing environments own variables, since those are not what is under test
|
|
|
|
_ = os.Unsetenv("DOCKER_TLS_VERIFY")
|
|
|
|
_ = os.Unsetenv("DOCKER_HOST")
|
|
|
|
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
|
|
|
|
err := EnvConfig(cmd)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "unix:///var/run/docker.sock", os.Getenv("DOCKER_HOST"))
|
|
|
|
assert.Equal(t, "", os.Getenv("DOCKER_TLS_VERIFY"))
|
|
|
|
// Re-enable this test when we've moved to github actions.
|
|
|
|
// assert.Equal(t, DockerAPIMinVersion, os.Getenv("DOCKER_API_VERSION"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEnvConfig_Custom(t *testing.T) {
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
|
|
|
|
err := cmd.ParseFlags([]string{"--host", "some-custom-docker-host", "--tlsverify", "--api-version", "1.99"})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = EnvConfig(cmd)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "some-custom-docker-host", os.Getenv("DOCKER_HOST"))
|
|
|
|
assert.Equal(t, "1", os.Getenv("DOCKER_TLS_VERIFY"))
|
|
|
|
// Re-enable this test when we've moved to github actions.
|
|
|
|
// assert.Equal(t, "1.99", os.Getenv("DOCKER_API_VERSION"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetSecretsFromFilesWithString(t *testing.T) {
|
|
|
|
value := "supersecretstring"
|
|
|
|
t.Setenv("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD", value)
|
|
|
|
|
|
|
|
testGetSecretsFromFiles(t, "notification-email-server-password", value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetSecretsFromFilesWithFile(t *testing.T) {
|
|
|
|
value := "megasecretstring"
|
|
|
|
|
|
|
|
// Create the temporary file which will contain a secret.
|
|
|
|
file, err := os.CreateTemp(t.TempDir(), "watchtower-")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Write the secret to the temporary file.
|
|
|
|
_, err = file.Write([]byte(value))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, file.Close())
|
|
|
|
|
|
|
|
t.Setenv("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD", file.Name())
|
|
|
|
|
|
|
|
testGetSecretsFromFiles(t, "notification-email-server-password", value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetSliceSecretsFromFiles(t *testing.T) {
|
|
|
|
values := []string{"entry2", "", "entry3"}
|
|
|
|
|
|
|
|
// Create the temporary file which will contain a secret.
|
|
|
|
file, err := os.CreateTemp(t.TempDir(), "watchtower-")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Write the secret to the temporary file.
|
|
|
|
for _, value := range values {
|
|
|
|
_, err = file.WriteString("\n" + value)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
require.NoError(t, 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()
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
RegisterNotificationFlags(cmd)
|
|
|
|
require.NoError(t, cmd.ParseFlags(args))
|
|
|
|
GetSecretsFromFiles(cmd)
|
|
|
|
flag := cmd.PersistentFlags().Lookup(flagName)
|
|
|
|
require.NotNil(t, flag)
|
|
|
|
value := flag.Value.String()
|
|
|
|
|
|
|
|
assert.Equal(t, expected, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHTTPAPIPeriodicPollsFlag(t *testing.T) {
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
|
|
|
|
err := cmd.ParseFlags([]string{"--http-api-periodic-polls"})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
periodicPolls, err := cmd.PersistentFlags().GetBool("http-api-periodic-polls")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, true, periodicPolls)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestIsFile(t *testing.T) {
|
|
|
|
assert.False(t, isFile("https://google.com"), "an URL should never be considered a file")
|
|
|
|
assert.True(t, isFile(os.Args[0]), "the currently running binary path should always be considered a file")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProcessFlagAliases(t *testing.T) {
|
|
|
|
logrus.StandardLogger().ExitFunc = func(_ int) { t.FailNow() }
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
RegisterNotificationFlags(cmd)
|
|
|
|
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{
|
|
|
|
`--porcelain`, `v1`,
|
|
|
|
`--interval`, `10`,
|
|
|
|
`--trace`,
|
|
|
|
}))
|
|
|
|
flags := cmd.Flags()
|
|
|
|
ProcessFlagAliases(flags)
|
|
|
|
|
|
|
|
urls, _ := flags.GetStringArray(`notification-url`)
|
|
|
|
assert.Contains(t, urls, `logger://`)
|
|
|
|
|
|
|
|
logStdout, _ := flags.GetBool(`notification-log-stdout`)
|
|
|
|
assert.True(t, logStdout)
|
|
|
|
|
|
|
|
report, _ := flags.GetBool(`notification-report`)
|
|
|
|
assert.True(t, report)
|
|
|
|
|
|
|
|
template, _ := flags.GetString(`notification-template`)
|
|
|
|
assert.Equal(t, `porcelain.v1.summary-no-log`, template)
|
|
|
|
|
|
|
|
sched, _ := flags.GetString(`schedule`)
|
|
|
|
assert.Equal(t, `@every 10s`, sched)
|
|
|
|
|
|
|
|
logLevel, _ := flags.GetString(`log-level`)
|
|
|
|
assert.Equal(t, `trace`, logLevel)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProcessFlagAliasesLogLevelFromEnvironment(t *testing.T) {
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
t.Setenv("WATCHTOWER_DEBUG", `true`)
|
|
|
|
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
RegisterNotificationFlags(cmd)
|
|
|
|
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{}))
|
|
|
|
flags := cmd.Flags()
|
|
|
|
ProcessFlagAliases(flags)
|
|
|
|
|
|
|
|
logLevel, _ := flags.GetString(`log-level`)
|
|
|
|
assert.Equal(t, `debug`, logLevel)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLogFormatFlag(t *testing.T) {
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
|
|
|
|
// Ensure the default value is Auto
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{}))
|
|
|
|
require.NoError(t, SetupLogging(cmd.Flags()))
|
|
|
|
assert.IsType(t, &logrus.TextFormatter{}, logrus.StandardLogger().Formatter)
|
|
|
|
|
|
|
|
// Test JSON format
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `JSON`}))
|
|
|
|
require.NoError(t, SetupLogging(cmd.Flags()))
|
|
|
|
assert.IsType(t, &logrus.JSONFormatter{}, logrus.StandardLogger().Formatter)
|
|
|
|
|
|
|
|
// Test Pretty format
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `pretty`}))
|
|
|
|
require.NoError(t, SetupLogging(cmd.Flags()))
|
|
|
|
assert.IsType(t, &logrus.TextFormatter{}, logrus.StandardLogger().Formatter)
|
|
|
|
textFormatter, ok := (logrus.StandardLogger().Formatter).(*logrus.TextFormatter)
|
|
|
|
assert.True(t, ok)
|
|
|
|
assert.True(t, textFormatter.ForceColors)
|
|
|
|
assert.False(t, textFormatter.FullTimestamp)
|
|
|
|
|
|
|
|
// Test LogFmt format
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `logfmt`}))
|
|
|
|
require.NoError(t, SetupLogging(cmd.Flags()))
|
|
|
|
textFormatter, ok = (logrus.StandardLogger().Formatter).(*logrus.TextFormatter)
|
|
|
|
assert.True(t, ok)
|
|
|
|
assert.True(t, textFormatter.DisableColors)
|
|
|
|
assert.True(t, textFormatter.FullTimestamp)
|
|
|
|
|
|
|
|
// Test invalid format
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `cowsay`}))
|
|
|
|
require.Error(t, SetupLogging(cmd.Flags()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLogLevelFlag(t *testing.T) {
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
|
|
|
|
// Test invalid format
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{`--log-level`, `gossip`}))
|
|
|
|
require.Error(t, SetupLogging(cmd.Flags()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProcessFlagAliasesSchedAndInterval(t *testing.T) {
|
|
|
|
logrus.StandardLogger().ExitFunc = func(_ int) { panic(`FATAL`) }
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
RegisterNotificationFlags(cmd)
|
|
|
|
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{`--schedule`, `@hourly`, `--interval`, `10`}))
|
|
|
|
flags := cmd.Flags()
|
|
|
|
|
|
|
|
assert.PanicsWithValue(t, `FATAL`, func() {
|
|
|
|
ProcessFlagAliases(flags)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProcessFlagAliasesScheduleFromEnvironment(t *testing.T) {
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
|
|
|
|
t.Setenv("WATCHTOWER_SCHEDULE", `@hourly`)
|
|
|
|
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
RegisterNotificationFlags(cmd)
|
|
|
|
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{}))
|
|
|
|
flags := cmd.Flags()
|
|
|
|
ProcessFlagAliases(flags)
|
|
|
|
|
|
|
|
sched, _ := flags.GetString(`schedule`)
|
|
|
|
assert.Equal(t, `@hourly`, sched)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProcessFlagAliasesInvalidPorcelaineVersion(t *testing.T) {
|
|
|
|
logrus.StandardLogger().ExitFunc = func(_ int) { panic(`FATAL`) }
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
RegisterNotificationFlags(cmd)
|
|
|
|
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{`--porcelain`, `cowboy`}))
|
|
|
|
flags := cmd.Flags()
|
|
|
|
|
|
|
|
assert.PanicsWithValue(t, `FATAL`, func() {
|
|
|
|
ProcessFlagAliases(flags)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFlagsArePrecentInDocumentation(t *testing.T) {
|
|
|
|
|
|
|
|
// Legacy notifcations are ignored, since they are (soft) deprecated
|
|
|
|
ignoredEnvs := map[string]string{
|
|
|
|
"WATCHTOWER_NOTIFICATION_SLACK_ICON_EMOJI": "legacy",
|
|
|
|
"WATCHTOWER_NOTIFICATION_SLACK_ICON_URL": "legacy",
|
|
|
|
}
|
|
|
|
|
|
|
|
ignoredFlags := map[string]string{
|
|
|
|
"notification-gotify-url": "legacy",
|
|
|
|
"notification-slack-icon-emoji": "legacy",
|
|
|
|
"notification-slack-icon-url": "legacy",
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := new(cobra.Command)
|
|
|
|
SetDefaults()
|
|
|
|
RegisterDockerFlags(cmd)
|
|
|
|
RegisterSystemFlags(cmd)
|
|
|
|
RegisterNotificationFlags(cmd)
|
|
|
|
|
|
|
|
flags := cmd.PersistentFlags()
|
|
|
|
|
|
|
|
docFiles := []string{
|
|
|
|
"../../docs/arguments.md",
|
|
|
|
"../../docs/lifecycle-hooks.md",
|
|
|
|
"../../docs/notifications.md",
|
|
|
|
}
|
|
|
|
allDocs := ""
|
|
|
|
for _, f := range docFiles {
|
|
|
|
bytes, err := os.ReadFile(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Could not load docs file %q: %v", f, err)
|
|
|
|
}
|
|
|
|
allDocs += string(bytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
flags.VisitAll(func(f *pflag.Flag) {
|
|
|
|
if !strings.Contains(allDocs, "--"+f.Name) {
|
|
|
|
if _, found := ignoredFlags[f.Name]; !found {
|
|
|
|
t.Logf("Docs does not mention flag long name %q", f.Name)
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !strings.Contains(allDocs, "-"+f.Shorthand) {
|
|
|
|
t.Logf("Docs does not mention flag shorthand %q (%q)", f.Shorthand, f.Name)
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
for _, key := range viper.AllKeys() {
|
|
|
|
envKey := strings.ToUpper(key)
|
|
|
|
if !strings.Contains(allDocs, envKey) {
|
|
|
|
if _, found := ignoredEnvs[envKey]; !found {
|
|
|
|
t.Logf("Docs does not mention environment variable %q", envKey)
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|