config: stop startup on an invalid DNS or TLS interval (closes #177)
check / check (push) Successful in 1m20s
check / check (push) Successful in 1m20s
DNSWATCHER_DNS_INTERVAL and DNSWATCHER_TLS_INTERVAL were parsed with time.ParseDuration and silently replaced by the default when that failed, so a value like 5 or 1d gave hourly checks with no hint why, and zero or negative values were accepted. Both now go through parseInterval, which returns an error naming the variable and the value, and startup stops the same way it does for invalid targets. An unset or empty variable still gets its default from setupViper. The three tests that pinned the old fallback are replaced. The README says what a valid value looks like. Model: opus-5-5
This commit is contained in:
@@ -28,6 +28,13 @@ var ErrNoTargets = errors.New(
|
||||
"no monitoring targets configured: set DNSWATCHER_TARGETS environment variable",
|
||||
)
|
||||
|
||||
// ErrInvalidInterval is returned when DNSWATCHER_DNS_INTERVAL or
|
||||
// DNSWATCHER_TLS_INTERVAL is set but is not a positive duration. An empty
|
||||
// value counts as unset and means the default.
|
||||
var ErrInvalidInterval = errors.New(
|
||||
"interval must be a positive duration such as 30m or 1h",
|
||||
)
|
||||
|
||||
// Params contains dependencies for Config.
|
||||
type Params struct {
|
||||
fx.In
|
||||
@@ -125,18 +132,14 @@ func buildConfig(
|
||||
}
|
||||
}
|
||||
|
||||
dnsInterval, err := time.ParseDuration(
|
||||
viper.GetString("DNS_INTERVAL"),
|
||||
)
|
||||
dnsInterval, err := parseInterval("DNS_INTERVAL")
|
||||
if err != nil {
|
||||
dnsInterval = defaultDNSInterval
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsInterval, err := time.ParseDuration(
|
||||
viper.GetString("TLS_INTERVAL"),
|
||||
)
|
||||
tlsInterval, err := parseInterval("TLS_INTERVAL")
|
||||
if err != nil {
|
||||
tlsInterval = defaultTLSInterval
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains, hostnames, err := parseAndValidateTargets()
|
||||
@@ -168,6 +171,22 @@ func buildConfig(
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// parseInterval reads the DNSWATCHER_-prefixed setting key as a duration. A
|
||||
// value that does not parse, or is zero or negative, is an error naming the
|
||||
// variable and the value; an unset variable has its default from setupViper.
|
||||
func parseInterval(key string) (time.Duration, error) {
|
||||
value := viper.GetString(key)
|
||||
|
||||
interval, err := time.ParseDuration(value)
|
||||
if err != nil || interval <= 0 {
|
||||
return 0, fmt.Errorf(
|
||||
"invalid DNSWATCHER_%s %q: %w", key, value, ErrInvalidInterval,
|
||||
)
|
||||
}
|
||||
|
||||
return interval, nil
|
||||
}
|
||||
|
||||
func parseAndValidateTargets() ([]string, []string, error) {
|
||||
domains, hostnames, err := ClassifyTargets(
|
||||
parseCSV(viper.GetString("TARGETS")),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -113,33 +114,40 @@ func TestNew_OnlyEmptyCSVSegments(t *testing.T) {
|
||||
assert.ErrorIs(t, err, config.ErrNoTargets)
|
||||
}
|
||||
|
||||
func TestNew_InvalidDNSInterval_FallsBackToDefault(t *testing.T) {
|
||||
viper.Reset()
|
||||
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
||||
t.Setenv("DNSWATCHER_DNS_INTERVAL", "banana")
|
||||
// TestNew_InvalidIntervalStopsStartup checks values that must stop startup;
|
||||
// TestNew_DefaultValues and TestNew_EmptyIntervalMeansDefault check that an
|
||||
// unset or empty interval means the default.
|
||||
func TestNew_InvalidIntervalStopsStartup(t *testing.T) {
|
||||
variables := []string{"DNSWATCHER_DNS_INTERVAL", "DNSWATCHER_TLS_INTERVAL"}
|
||||
values := []string{
|
||||
"banana", // not a duration
|
||||
"5", // no unit
|
||||
"1d", // days are not a unit time.ParseDuration knows
|
||||
"0", // zero
|
||||
"-1h", // negative
|
||||
}
|
||||
|
||||
cfg, err := config.New(nil, newTestParams(t))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, time.Hour, cfg.DNSInterval,
|
||||
"invalid DNS interval should fall back to 1h default")
|
||||
for _, variable := range variables {
|
||||
for _, value := range values {
|
||||
t.Run(variable+"="+value, func(t *testing.T) {
|
||||
viper.Reset()
|
||||
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
||||
t.Setenv(variable, value)
|
||||
|
||||
_, err := config.New(nil, newTestParams(t))
|
||||
require.ErrorIs(t, err, config.ErrInvalidInterval)
|
||||
require.ErrorContains(t, err, variable)
|
||||
require.ErrorContains(t, err, strconv.Quote(value))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew_InvalidTLSInterval_FallsBackToDefault(t *testing.T) {
|
||||
func TestNew_EmptyIntervalMeansDefault(t *testing.T) {
|
||||
viper.Reset()
|
||||
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
||||
t.Setenv("DNSWATCHER_TLS_INTERVAL", "notaduration")
|
||||
|
||||
cfg, err := config.New(nil, newTestParams(t))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 12*time.Hour, cfg.TLSInterval,
|
||||
"invalid TLS interval should fall back to 12h default")
|
||||
}
|
||||
|
||||
func TestNew_BothIntervalsInvalid(t *testing.T) {
|
||||
viper.Reset()
|
||||
t.Setenv("DNSWATCHER_TARGETS", "example.com")
|
||||
t.Setenv("DNSWATCHER_DNS_INTERVAL", "xyz")
|
||||
t.Setenv("DNSWATCHER_TLS_INTERVAL", "abc")
|
||||
t.Setenv("DNSWATCHER_DNS_INTERVAL", "")
|
||||
t.Setenv("DNSWATCHER_TLS_INTERVAL", "")
|
||||
|
||||
cfg, err := config.New(nil, newTestParams(t))
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user