Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@ module github.com/hugoh/upd
go 1.25.0

require (
github.com/go-playground/validator/v10 v10.30.3
github.com/stretchr/testify v1.11.1
github.com/urfave/cli/v3 v3.9.0
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/crypto v0.52.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.37.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)

Expand Down
18 changes: 0 additions & 18 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
Expand All @@ -20,8 +10,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand All @@ -32,12 +20,6 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c=
github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
49 changes: 25 additions & 24 deletions internal/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ import (
"strings"
"time"

"github.com/go-playground/validator/v10"
"github.com/hugoh/upd/internal/check"
"github.com/hugoh/upd/internal/logger"
"github.com/hugoh/upd/internal/logic"
Expand All @@ -80,6 +79,11 @@ const (
DefaultConfig = ".upd.yaml"
// DefaultDNSPort is the default DNS port.
DefaultDNSPort = "53"

logLevelTrace = "trace"
logLevelDebug = "debug"
logLevelInfo = "info"
logLevelWarn = "warn"
)

// ConfigFileUsed stores the path of the active configuration file for debugging purposes.
Expand All @@ -91,26 +95,26 @@ var ConfigFileUsed string
type Configuration struct {
Checks struct {
Every struct {
Normal types.Duration `validate:"required,gt=0"`
Down types.Duration `validate:"required,gt=0"`
} `validate:"required"`
Normal types.Duration
Down types.Duration
}
List struct {
Ordered []string `validate:"dive,uri"`
Shuffled []string `validate:"dive,uri"`
} `validate:"required"`
TimeOut types.Duration `validate:"required,gt=0"`
} `validate:"required"`
Ordered []string
Shuffled []string
}
TimeOut types.Duration
}
DownAction struct {
Exec string
Every struct {
After types.Duration `validate:"omitempty,gt=0"`
Repeat types.Duration `validate:"omitempty,gt=0"`
BackoffLimit types.Duration `validate:"omitempty,gte=0" yaml:"expBackoffLimit"`
After types.Duration
Repeat types.Duration
BackoffLimit types.Duration `yaml:"expBackoffLimit"`
}
StopExec string `yaml:"stopExec" validate:"omitempty"` //nolint:tagalign
} `validate:"omitempty" yaml:"downAction"`
Stats status.StatServerConfig `validate:"omitempty"`
LogLevel string `validate:"omitempty,oneof=trace debug info warn" yaml:"logLevel"`
StopExec string `yaml:"stopExec"`
} `yaml:"downAction"`
Stats status.StatServerConfig
LogLevel string `yaml:"logLevel"`
}

func configError(msg string, path string, err error) (*Configuration, error) {
Expand Down Expand Up @@ -161,10 +165,7 @@ func ReadConf(cfgFile string) (*Configuration, error) {
return configError("Unable to parse the config", cfgFile, err)
}

validate := validator.New()

err = validate.Struct(&conf)
if err != nil {
if err := conf.Validate(); err != nil {
return configError("Missing required attributes", cfgFile, err)
}

Expand Down Expand Up @@ -335,13 +336,13 @@ func (c Configuration) logSetup() {
var level slog.Level

switch c.LogLevel {
case "trace":
case logLevelTrace:
level = slog.LevelDebug - 4 //nolint:mnd // slog doesn't have LevelTrace, use LevelDebug - 4
case "debug":
case logLevelDebug:
level = slog.LevelDebug
case "info":
case logLevelInfo:
level = slog.LevelInfo
case "warn":
case logLevelWarn:
level = slog.LevelWarn
default:
logger.L.Error("[Config] Unknown loglevel", "loglevel", c.LogLevel)
Expand Down
8 changes: 4 additions & 4 deletions internal/conf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,10 @@ func TestLogSetup(t *testing.T) {
name string
logLevel string
}{
{"trace level", "trace"},
{"debug level", "debug"},
{"info level", "info"},
{"warn level", "warn"},
{"trace level", logLevelTrace},
{"debug level", logLevelDebug},
{"info level", logLevelInfo},
{"warn level", logLevelWarn},
{"empty defaults to warn", ""},
}

Expand Down
142 changes: 142 additions & 0 deletions internal/conf_validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package internal

import (
"errors"
"fmt"
"net/url"
"reflect"
"time"

"github.com/hugoh/upd/internal/types"
)

var (
errDurationMustBePositive = errors.New("must be greater than 0")
errInvalidURI = errors.New("must be a valid URI")
errMustNotBeNegative = errors.New("must not be negative")
errPortOutOfRange = errors.New("must be between 1 and 65535")
errInvalidLogLevel = errors.New("must be one of: trace, debug, info, warn")
)

func appendErr(errs []error, key string, err error) []error {
if err != nil {
return append(errs, fmt.Errorf("%s: %w", key, err))
}

return errs
}

// Validate checks that the configuration has all required fields with valid values.
func (c Configuration) Validate() error {
var errs []error

errs = appendErr(errs, "checks", c.validateChecks())

if !reflect.ValueOf(c.DownAction).IsZero() {
errs = appendErr(errs, "downAction", c.validateDownAction())
}

if !reflect.ValueOf(c.Stats).IsZero() {
errs = appendErr(errs, "stats", c.validateStats())
}

if c.LogLevel != "" {
errs = appendErr(errs, "logLevel", validateLogLevel(c.LogLevel))
}

return errors.Join(errs...)
}

func (c Configuration) validateChecks() error {
var errs []error

errs = appendErr(errs, "every.normal", validatePositiveDuration(c.Checks.Every.Normal))
errs = appendErr(errs, "every.down", validatePositiveDuration(c.Checks.Every.Down))
errs = appendErr(errs, "timeout", validatePositiveDuration(c.Checks.TimeOut))
errs = appendErr(errs, "list.ordered", validateURIs(c.Checks.List.Ordered))
errs = appendErr(errs, "list.shuffled", validateURIs(c.Checks.List.Shuffled))

return errors.Join(errs...)
}

func (c Configuration) validateDownAction() error {
var errs []error

errs = appendErr(errs, "every.after", checkPositive(time.Duration(c.DownAction.Every.After)))
errs = appendErr(errs, "every.repeat", checkPositive(time.Duration(c.DownAction.Every.Repeat)))
errs = appendErr(
errs,
"every.expBackoffLimit",
checkNonNegative(time.Duration(c.DownAction.Every.BackoffLimit)),
)

return errors.Join(errs...)
}

func (c Configuration) validateStats() error {
var errs []error

errs = appendErr(errs, "port", validatePort(c.Stats.Port))
errs = appendErr(errs, "readTimeout", checkNonNegative(c.Stats.ReadTimeout))
errs = appendErr(errs, "writeTimeout", checkNonNegative(c.Stats.WriteTimeout))
errs = appendErr(errs, "idleTimeout", checkNonNegative(c.Stats.IdleTimeout))

return errors.Join(errs...)
}

func validatePositiveDuration(d types.Duration) error {
if time.Duration(d) <= 0 {
return errDurationMustBePositive
}

return nil
}

func checkPositive(d time.Duration) error {
if d < 0 {
return errDurationMustBePositive
}

return nil
}

func checkNonNegative(d time.Duration) error {
if d < 0 {
return errMustNotBeNegative
}

return nil
}

func validatePort(port int) error {
if port < 0 || port > 65535 {
return errPortOutOfRange
}

return nil
}

func validateLogLevel(level string) error {
switch level {
case logLevelTrace, logLevelDebug, logLevelInfo, logLevelWarn:
return nil
default:
return errInvalidLogLevel
}
}

func validateURIs(uris []string) error {
if len(uris) == 0 {
return nil
}

var errs []error

for i, s := range uris {
if _, err := url.ParseRequestURI(s); err != nil {
errs = append(errs, fmt.Errorf("[%d]: %w", i, errInvalidURI))
}
}

return errors.Join(errs...)
}
Loading
Loading