diff --git a/cmd/boulder-observer/main.go b/cmd/boulder-observer/main.go index 2964d82aabf..bbce31cafb9 100644 --- a/cmd/boulder-observer/main.go +++ b/cmd/boulder-observer/main.go @@ -4,7 +4,10 @@ import ( "flag" "os" + "github.com/letsencrypt/validator/v10" + "github.com/letsencrypt/boulder/cmd" + "github.com/letsencrypt/boulder/config" "github.com/letsencrypt/boulder/observer" "github.com/letsencrypt/boulder/strictyaml" ) @@ -19,25 +22,33 @@ func main() { cmd.FailOnError(err, "failed to read config file") // Parse the YAML config file. - var config observer.ObsConf - err = strictyaml.Unmarshal(configYAML, &config) + var obsConf observer.ObsConf + err = strictyaml.Unmarshal(configYAML, &obsConf) if *debugAddr != "" { - config.DebugAddr = *debugAddr + obsConf.DebugAddr = *debugAddr } if err != nil { cmd.FailOnError(err, "failed to parse YAML config") } + // Validate config using struct tags. + validate := validator.New() + validate.RegisterCustomTypeFunc(config.DurationCustomTypeFunc, config.Duration{}) + err = validate.Struct(obsConf) + if err != nil { + cmd.FailOnError(err, "config validation failed") + } + // Make an `Observer` object. - observer, err := config.MakeObserver() + obs, err := obsConf.MakeObserver() if err != nil { cmd.FailOnError(err, "config failed validation") } // Start the `Observer` daemon. - observer.Start() + obs.Start() } func init() { diff --git a/observer/obs_conf.go b/observer/obs_conf.go index a761437ba72..409cc1a8e03 100644 --- a/observer/obs_conf.go +++ b/observer/obs_conf.go @@ -3,7 +3,6 @@ package observer import ( "errors" "fmt" - "net" "strconv" "github.com/prometheus/client_golang/prometheus" @@ -32,33 +31,6 @@ type ObsConf struct { MonConfs []*MonConf `yaml:"monitors" validate:"min=1,dive"` } -// validateSyslog ensures the `Syslog` field received by `ObsConf` -// contains valid log levels. -func (c *ObsConf) validateSyslog() error { - syslog, stdout := c.Syslog.SyslogLevel, c.Syslog.StdoutLevel - if stdout < 0 || stdout > 7 || syslog < 0 || syslog > 7 { - return fmt.Errorf( - "invalid 'syslog', '%+v', valid log levels are 0-7", c.Syslog) - } - return nil -} - -// validateDebugAddr ensures the `debugAddr` received by `ObsConf` is -// properly formatted and a valid port. -func (c *ObsConf) validateDebugAddr() error { - _, p, err := net.SplitHostPort(c.DebugAddr) - if err != nil { - return fmt.Errorf( - "invalid 'debugaddr', %q, not expected format", c.DebugAddr) - } - port, _ := strconv.Atoi(p) - if port <= 0 || port > 65535 { - return fmt.Errorf( - "invalid 'debugaddr','%d' is not a valid port", port) - } - return nil -} - func (c *ObsConf) makeMonitors(metrics prometheus.Registerer) ([]*monitor, []error, error) { var errs []error var monitors []*monitor @@ -117,24 +89,6 @@ func (c *ObsConf) makeMonitors(metrics prometheus.Registerer) ([]*monitor, []err // bound `ObsConf`. If the `ObsConf` cannot be validated, an error // appropriate for end-user consumption is returned instead. func (c *ObsConf) MakeObserver() (*Observer, error) { - err := c.validateSyslog() - if err != nil { - return nil, err - } - - err = c.validateDebugAddr() - if err != nil { - return nil, err - } - - if len(c.MonConfs) == 0 { - return nil, errors.New("no monitors provided") - } - - if len(c.Buckets) == 0 { - return nil, errors.New("no histogram buckets provided") - } - // Start monitoring and logging. metrics, logger, shutdown := cmd.StatsAndLogging(c.Syslog, c.OpenTelemetry, c.DebugAddr) histObservations = prometheus.NewHistogramVec( diff --git a/observer/obs_conf_test.go b/observer/obs_conf_test.go index fea4f1628d8..dc2269e2e57 100644 --- a/observer/obs_conf_test.go +++ b/observer/obs_conf_test.go @@ -10,7 +10,6 @@ import ( "github.com/letsencrypt/boulder/metrics" "github.com/letsencrypt/boulder/observer/probers" _ "github.com/letsencrypt/boulder/observer/probers/mock" - "github.com/letsencrypt/boulder/test" ) const ( @@ -71,72 +70,3 @@ func TestObsConf_makeMonitors(t *testing.T) { }) } } - -func TestObsConf_ValidateDebugAddr(t *testing.T) { - type fields struct { - DebugAddr string - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - // valid - {"max len and range", fields{":65535"}, false}, - {"min len and range", fields{":1"}, false}, - {"2 digits", fields{":80"}, false}, - // invalid - {"out of range high", fields{":65536"}, true}, - {"out of range low", fields{":0"}, true}, - {"not even a port", fields{":foo"}, true}, - {"missing :", fields{"foo"}, true}, - {"missing port", fields{"foo:"}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &ObsConf{ - DebugAddr: tt.fields.DebugAddr, - } - err := c.validateDebugAddr() - if tt.wantErr { - test.AssertError(t, err, "ObsConf.ValidateDebugAddr() should have errored") - } else { - test.AssertNotError(t, err, "ObsConf.ValidateDebugAddr() shouldn't have errored") - } - }) - } -} - -func TestObsConf_validateSyslog(t *testing.T) { - type fields struct { - Syslog cmd.SyslogConfig - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - // valid - {"valid", fields{cmd.SyslogConfig{StdoutLevel: 6, SyslogLevel: 6}}, false}, - // invalid - {"both too high", fields{cmd.SyslogConfig{StdoutLevel: 9, SyslogLevel: 9}}, true}, - {"stdout too high", fields{cmd.SyslogConfig{StdoutLevel: 9, SyslogLevel: 6}}, true}, - {"syslog too high", fields{cmd.SyslogConfig{StdoutLevel: 6, SyslogLevel: 9}}, true}, - {"both too low", fields{cmd.SyslogConfig{StdoutLevel: -1, SyslogLevel: -1}}, true}, - {"stdout too low", fields{cmd.SyslogConfig{StdoutLevel: -1, SyslogLevel: 6}}, true}, - {"syslog too low", fields{cmd.SyslogConfig{StdoutLevel: 6, SyslogLevel: -1}}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &ObsConf{ - Syslog: tt.fields.Syslog, - } - err := c.validateSyslog() - if tt.wantErr { - test.AssertError(t, err, "ObsConf.validateSyslog() should have errored") - } else { - test.AssertNotError(t, err, "ObsConf.validateSyslog() shouldn't have errored") - } - }) - } -}