1 package observer
2
3 import (
4 "errors"
5 "fmt"
6 "net"
7 "strconv"
8
9 "github.com/prometheus/client_golang/prometheus"
10
11 "github.com/letsencrypt/boulder/cmd"
12 "github.com/letsencrypt/boulder/observer/probers"
13 )
14
15 var (
16 countMonitors = prometheus.NewCounterVec(
17 prometheus.CounterOpts{
18 Name: "obs_monitors",
19 Help: "details of each configured monitor",
20 },
21 []string{"kind", "valid"},
22 )
23 histObservations *prometheus.HistogramVec
24 )
25
26
27 type ObsConf struct {
28 DebugAddr string `yaml:"debugaddr" validate:"required,hostname_port"`
29 Buckets []float64 `yaml:"buckets" validate:"min=1,dive"`
30 Syslog cmd.SyslogConfig `yaml:"syslog"`
31 OpenTelemetry cmd.OpenTelemetryConfig
32 MonConfs []*MonConf `yaml:"monitors" validate:"min=1,dive"`
33 }
34
35
36
37 func (c *ObsConf) validateSyslog() error {
38 syslog, stdout := c.Syslog.SyslogLevel, c.Syslog.StdoutLevel
39 if stdout < 0 || stdout > 7 || syslog < 0 || syslog > 7 {
40 return fmt.Errorf(
41 "invalid 'syslog', '%+v', valid log levels are 0-7", c.Syslog)
42 }
43 return nil
44 }
45
46
47
48 func (c *ObsConf) validateDebugAddr() error {
49 _, p, err := net.SplitHostPort(c.DebugAddr)
50 if err != nil {
51 return fmt.Errorf(
52 "invalid 'debugaddr', %q, not expected format", c.DebugAddr)
53 }
54 port, _ := strconv.Atoi(p)
55 if port <= 0 || port > 65535 {
56 return fmt.Errorf(
57 "invalid 'debugaddr','%d' is not a valid port", port)
58 }
59 return nil
60 }
61
62 func (c *ObsConf) makeMonitors(metrics prometheus.Registerer) ([]*monitor, []error, error) {
63 var errs []error
64 var monitors []*monitor
65 proberSpecificMetrics := make(map[string]map[string]prometheus.Collector)
66 for e, m := range c.MonConfs {
67 entry := strconv.Itoa(e + 1)
68 proberConf, err := probers.GetConfigurer(m.Kind)
69 if err != nil {
70
71 errs = append(errs, fmt.Errorf("'monitors' entry #%s couldn't be validated: %w", entry, err))
72
73 countMonitors.WithLabelValues(m.Kind, "false").Inc()
74
75 continue
76 }
77 kind := proberConf.Kind()
78
79
80 _, exist := proberSpecificMetrics[kind]
81 if !exist {
82
83
84
85 proberSpecificMetrics[kind] = make(map[string]prometheus.Collector)
86 for name, collector := range proberConf.Instrument() {
87
88 metrics.MustRegister(collector)
89
90
91 proberSpecificMetrics[kind][name] = collector
92 }
93 }
94
95 monitor, err := m.makeMonitor(proberSpecificMetrics[kind])
96 if err != nil {
97
98 errs = append(errs, fmt.Errorf("'monitors' entry #%s couldn't be validated: %w", entry, err))
99
100
101 countMonitors.WithLabelValues(kind, "false").Inc()
102 } else {
103
104 monitors = append(monitors, monitor)
105
106
107 countMonitors.WithLabelValues(kind, "true").Inc()
108 }
109 }
110 if len(c.MonConfs) == len(errs) {
111 return nil, errs, errors.New("no valid monitors, cannot continue")
112 }
113 return monitors, errs, nil
114 }
115
116
117
118
119 func (c *ObsConf) MakeObserver() (*Observer, error) {
120 err := c.validateSyslog()
121 if err != nil {
122 return nil, err
123 }
124
125 err = c.validateDebugAddr()
126 if err != nil {
127 return nil, err
128 }
129
130 if len(c.MonConfs) == 0 {
131 return nil, errors.New("no monitors provided")
132 }
133
134 if len(c.Buckets) == 0 {
135 return nil, errors.New("no histogram buckets provided")
136 }
137
138
139 metrics, logger, shutdown := cmd.StatsAndLogging(c.Syslog, c.OpenTelemetry, c.DebugAddr)
140 histObservations = prometheus.NewHistogramVec(
141 prometheus.HistogramOpts{
142 Name: "obs_observations",
143 Help: "details of each probe attempt",
144 Buckets: c.Buckets,
145 }, []string{"name", "kind", "success"})
146 metrics.MustRegister(countMonitors)
147 metrics.MustRegister(histObservations)
148 defer cmd.AuditPanic()
149 logger.Info(cmd.VersionString())
150 logger.Infof("Initializing boulder-observer daemon")
151 logger.Debugf("Using config: %+v", c)
152
153 monitors, errs, err := c.makeMonitors(metrics)
154 if len(errs) != 0 {
155 logger.Errf("%d of %d monitors failed validation", len(errs), len(c.MonConfs))
156 for _, err := range errs {
157 logger.Errf("%s", err)
158 }
159 } else {
160 logger.Info("all monitors passed validation")
161 }
162 if err != nil {
163 return nil, err
164 }
165 return &Observer{logger, monitors, shutdown}, nil
166 }
167
View as plain text