138 lines
3.6 KiB
Go
138 lines
3.6 KiB
Go
package observer
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
|
|
"github.com/letsencrypt/boulder/cmd"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
var (
|
|
countMonitors = prometheus.NewCounterVec(
|
|
prometheus.CounterOpts{
|
|
Name: "obs_monitors",
|
|
Help: "details of each configured monitor",
|
|
},
|
|
[]string{"kind", "valid"},
|
|
)
|
|
histObservations *prometheus.HistogramVec
|
|
)
|
|
|
|
// ObsConf is exported to receive YAML configuration.
|
|
type ObsConf struct {
|
|
DebugAddr string `yaml:"debugaddr"`
|
|
Buckets []float64 `yaml:"buckets"`
|
|
Syslog cmd.SyslogConfig `yaml:"syslog"`
|
|
MonConfs []*MonConf `yaml:"monitors"`
|
|
}
|
|
|
|
// validateSyslog ensures the 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() ([]*monitor, []error, error) {
|
|
var errs []error
|
|
var monitors []*monitor
|
|
for e, m := range c.MonConfs {
|
|
entry := strconv.Itoa(e + 1)
|
|
monitor, err := m.makeMonitor()
|
|
if err != nil {
|
|
// append validation error to errs
|
|
errs = append(
|
|
errs, fmt.Errorf(
|
|
"'monitors' entry #%s couldn't be validated: %v", entry, err))
|
|
|
|
// increment metrics
|
|
countMonitors.WithLabelValues(m.Kind, "false").Inc()
|
|
} else {
|
|
// append monitor to monitors
|
|
monitors = append(monitors, monitor)
|
|
|
|
// increment metrics
|
|
countMonitors.WithLabelValues(m.Kind, "true").Inc()
|
|
}
|
|
}
|
|
if len(c.MonConfs) == len(errs) {
|
|
return nil, errs, errors.New("no valid monitors, cannot continue")
|
|
}
|
|
return monitors, errs, nil
|
|
}
|
|
|
|
// MakeObserver constructs an `Observer` object from the contents of the
|
|
// 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 := cmd.StatsAndLogging(c.Syslog, c.DebugAddr)
|
|
histObservations = prometheus.NewHistogramVec(
|
|
prometheus.HistogramOpts{
|
|
Name: "obs_observations",
|
|
Help: "details of each probe attempt",
|
|
Buckets: c.Buckets,
|
|
}, []string{"name", "kind", "success"})
|
|
metrics.MustRegister(countMonitors)
|
|
metrics.MustRegister(histObservations)
|
|
defer logger.AuditPanic()
|
|
logger.Info(cmd.VersionString())
|
|
logger.Infof("Initializing boulder-observer daemon")
|
|
logger.Debugf("Using config: %+v", c)
|
|
|
|
monitors, errs, err := c.makeMonitors()
|
|
if len(errs) != 0 {
|
|
logger.Errf("%d of %d monitors failed validation", len(errs), len(c.MonConfs))
|
|
for _, err := range errs {
|
|
logger.Errf("%s", err)
|
|
}
|
|
} else {
|
|
logger.Info("all monitors passed validation")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Observer{logger, monitors}, nil
|
|
}
|