From f6575edcc34adc64a784bbbdad1e38d7d876304a Mon Sep 17 00:00:00 2001 From: beautifulentropy Date: Wed, 3 Mar 2021 19:29:04 -0800 Subject: [PATCH] Squashing Fixes --- cmd/boulder-observer/README.md | 147 ++++++++++++++++--------- cmd/boulder-observer/main.go | 15 ++- observer/mon_conf.go | 58 ++++++---- observer/monitor.go | 54 ++++----- observer/obs_conf.go | 76 ++++++++----- observer/observer.go | 68 ++++++------ observer/plugins/build.sh | 59 ---------- observer/plugins/dns/dns.go | 43 -------- observer/plugins/dns/dns_conf.go | 102 ----------------- observer/plugins/http/http.go | 31 ------ observer/plugins/http/http_conf.go | 59 ---------- observer/plugins/plugins.go | 55 --------- observer/probes/dns/dns.go | 48 ++++++++ observer/probes/dns/dns_conf.go | 139 +++++++++++++++++++++++ observer/probes/dns/dns_conf_test.go | 106 ++++++++++++++++++ observer/probes/http/http.go | 40 +++++++ observer/probes/http/http_conf.go | 63 +++++++++++ observer/probes/http/http_conf_test.go | 34 ++++++ observer/probes/prober.go | 50 +++++++++ test/config-next/observer.yaml | 54 --------- test/config-next/observer.yml | 90 +++++++++++++++ test/prometheus/prometheus.yml | 2 +- 22 files changed, 813 insertions(+), 580 deletions(-) delete mode 100755 observer/plugins/build.sh delete mode 100644 observer/plugins/dns/dns.go delete mode 100644 observer/plugins/dns/dns_conf.go delete mode 100644 observer/plugins/http/http.go delete mode 100644 observer/plugins/http/http_conf.go delete mode 100644 observer/plugins/plugins.go create mode 100644 observer/probes/dns/dns.go create mode 100644 observer/probes/dns/dns_conf.go create mode 100644 observer/probes/dns/dns_conf_test.go create mode 100644 observer/probes/http/http.go create mode 100644 observer/probes/http/http_conf.go create mode 100644 observer/probes/http/http_conf_test.go create mode 100644 observer/probes/prober.go delete mode 100644 test/config-next/observer.yaml create mode 100644 test/config-next/observer.yml diff --git a/cmd/boulder-observer/README.md b/cmd/boulder-observer/README.md index 252dc0d70d9..873ae5c9ad9 100644 --- a/cmd/boulder-observer/README.md +++ b/cmd/boulder-observer/README.md @@ -1,86 +1,127 @@ # boulder-observer -A modular config driven approach to black box monitoring with Prometheus +A modular config driven approach to black box monitoring with +Prometheus. +## Metrics +Observer provides the following metrics. + +### obs_monitors +Count of configured monitors. + +**Labels:** + +`name`: name of the monitor + +`type`: type of prober the monitor is configured to use + +`valid`: whether the monitor configuration was valid + +### obs_observations +Time taken, in seconds, for a monitor to perform a request/ query. + +**Labels:** + +`name`: name of the monitor + +`type`: type of prober the monitor is configured to use + +`result`: whether the query/ request was successful + +**Buckets:** + +`.1, .25, .5, 1, 2.5, 5, 7.5, 10, 15, 30, 45` ## Usage -### Starting the `observer` daemon -```shell -$ ./observer/plugins/build.sh && go run ./cmd/boulder-observer/main.go -config test/config-next/observer.yaml -Building plugins: -⚙️ observer/plugins/dns.so -✅dns.so -⚙️ observer/plugins/http.so -✅http.so -OK -I191418 main ksKu7w4 Versions: main=(Unspecified Unspecified) Golang=(go1.15.7) BuildHost=(Unspecified) -I191418 main o9me0QI Initializing boulder-observer daemon from config: test/config-next/observer.yaml -I191420 main wv7tug0 HTTP monitor "https://letsencrypt.org-200" succeeded while taking:=120.900665ms -I191422 main ss-hzQ8 HTTP monitor "https://letsencrypt.org-200" succeeded while taking:=23.051998ms -I191424 main -fD46gg HTTP monitor "https://letsencrypt.org-200" succeeded while taking:=23.419121ms -I191426 main urmy8AM HTTP monitor "https://letsencrypt.org-200" succeeded while taking:=23.875478ms -I191428 main qaGe0Qc DNS monitor "udp-8.8.8.8:53-google.com-A" succeeded while taking:=5.088261ms -I191428 main i677rw0 DNS monitor "tcp-8.8.8.8:53-google.com-A" succeeded while taking:=5.156114ms -I191428 main ooyq_Qo DNS monitor "udp-owen.ns.cloudflare.com:53-letsencrypt.org-A" succeeded while taking:=15.858563ms -``` -### Help ```shell $ go run ./cmd/boulder-observer/main.go -help main: -config string - Path to boulder-observer configuration file (default "config.yaml") + Path to boulder-observer configuration file (default "config.yml") +``` + +### Starting the boulder-observer daemon +```shell +$ go run ./cmd/boulder-observer/main.go -config test/config-next/observer.yml +I142601 main ksKu7w4 Versions: main=(Unspecified Unspecified) Golang=(go1.15.7) BuildHost=(Unspecified) +I142601 main q_D84gk Initializing boulder-observer daemon from config: test/config-next/observer.yml +I142603 main o4Cp-Q0 type=[HTTP] result=[true] duration=[0.123472] name=[http://letsencrypt.org-200] +I142603 main n4iSrAM type=[HTTP] result=[true] duration=[0.123751] name=[https://letsencrypt.org-200] +I142605 main qe3Gugc type=[HTTP] result=[true] duration=[0.023499] name=[https://letsencrypt.org-200] +I142605 main _J2k0wo type=[HTTP] result=[true] duration=[0.044429] name=[http://letsencrypt.org-200] +I142606 main zomKjwc type=[DNS] result=[false] duration=[0.000017] name=[udp-2606:4700:4700::1111:53-google.com-A] +I142606 main 6parpwM type=[DNS] result=[false] duration=[0.000014] name=[tcp-2606:4700:4700::1111:53-google.com-A] +I142606 main pJqFmAs type=[DNS] result=[true] duration=[0.004667] name=[udp-1.1.1.1:53-google.com-A] +I142606 main 9f7d2AM type=[DNS] result=[true] duration=[0.008965] name=[tcp-1.1.1.1:53-google.com-A] +I142606 main 962rkgM type=[DNS] result=[true] duration=[0.013107] name=[udp-owen.ns.cloudflare.com:53-letsencrypt.org-A] +I142606 main l-r29gc type=[DNS] result=[true] duration=[0.016294] name=[tcp-owen.ns.cloudflare.com:53-letsencrypt.org-A] +I142607 main t_vrtAQ type=[HTTP] result=[true] duration=[0.022378] name=[https://letsencrypt.org-200] +I142607 main v7SjtQM type=[HTTP] result=[true] duration=[0.043780] name=[http://letsencrypt.org-200] +I142609 main ptjWkQM type=[HTTP] result=[true] duration=[0.021068] name=[https://letsencrypt.org-200] +I142609 main jPzToww type=[HTTP] result=[true] duration=[0.042141] name=[http://letsencrypt.org-200] +I142611 main 5IygqAI type=[DNS] result=[false] duration=[0.000019] name=[udp-2606:4700:4700::1111:53-google.com-A] +I142611 main zqe61Qk type=[DNS] result=[false] duration=[0.000012] name=[tcp-2606:4700:4700::1111:53-google.com-A] +I142611 main k9Xh1AU type=[DNS] result=[true] duration=[0.008134] name=[udp-8.8.8.8:53-google.com-A] +I142611 main trL2mwU type=[DNS] result=[true] duration=[0.008801] name=[udp-1.1.1.1:53-google.com-A] +I142611 main _qLDgwk type=[DNS] result=[true] duration=[0.011323] name=[tcp-8.8.8.8:53-google.com-A] +I142611 main rJDj2AI type=[DNS] result=[true] duration=[0.012559] name=[tcp-1.1.1.1:53-google.com-A] +I142611 main teWD6Qs type=[DNS] result=[true] duration=[0.015299] name=[udp-owen.ns.cloudflare.com:53-letsencrypt.org-A] +I142611 main kPrnlg4 type=[DNS] result=[true] duration=[0.019022] name=[tcp-owen.ns.cloudflare.com:53-letsencrypt.org-A] +I142611 main xb_w9gs type=[HTTP] result=[true] duration=[0.025506] name=[https://letsencrypt.org-200] +I142611 main oKi2ggk type=[HTTP] result=[true] duration=[0.074734] name=[http://letsencrypt.org-200] +I142613 main wPqP-gg type=[HTTP] result=[true] duration=[0.021814] name=[https://letsencrypt.org-200] +I142613 main 4IrYoQY type=[HTTP] result=[true] duration=[0.041857] name=[http://letsencrypt.org-200] ``` + ## Configuration + +### Observer ```yaml -debugAddr: 8040 -syslog: +debugAddr: :8040 +syslog: stdoutlevel: 6 sysloglevel: 6 -timeout: 5 -monitors: [] +monitors: + - + ... ``` ### Monitors -#### Using the DNS plugin +#### Configuring a DNS monitor ```yaml monitors: - - enabled: true - period: 1 - plugin: - name: DNS - path: "./cmd/boulder-observer/observer/plugins/dns.so" + period: 10s + type: DNS settings: - qproto: udp - qrecurse: false - qname: letsencrypt.org - qtype: A - qserver: "owen.ns.cloudflare.com:53" + protocol: tcp + server: 8.8.8.8:53 + recurse: true + query_name: google.com + query_type: A ``` -#### Using the HTTP plugin +#### Configuring an HTTP monitor ```yaml monitors: - - enabled: true - period: 1 - plugin: - name: HTTP - path: "./cmd/boulder-observer/observer/plugins/http.so" + period: 2s + type: HTTP settings: url: https://letsencrypt.org rcode: 200 ``` -### Plugins -**Building plugins** +## Development + +### Starting Prometheus locally +Please note, this requires a local prometheus binary. ```shell -$ ./observer/plugins/build.sh -Building plugins: -⚙️ observer/plugins/dns.so -✅dns.so -⚙️ observer/plugins/http.so -✅http.so -OK -``` \ No newline at end of file +prometheus --config.file=boulder/test/prometheus/prometheus.yml +``` + +### Viewing metrics locally +When developing with a local prometheus instance, you can use this link +to view metrics: +[link](http://0.0.0.0:9090/graph?g0.expr=sum%20by(name)%20(%0Arate(obs_observations_bucket%7Bresult%3D%22true%22%7D%5B1m%5D)%0A)&g0.tab=0&g0.stacked=0&g0.range_input=1h&g1.expr=sum%20by(name)%20(%0Arate(obs_observations_bucket%7Bresult%3D%22false%22%7D%5B1m%5D)%0A)&g1.tab=0&g1.stacked=0&g1.range_input=1h&g2.expr=count%20by(valid)%20(%0Aobs_monitors%7Bvalid%3D%22true%22%7D%0A)&g2.tab=0&g2.stacked=0&g2.range_input=1h) \ No newline at end of file diff --git a/cmd/boulder-observer/main.go b/cmd/boulder-observer/main.go index 6543ec14a94..5b90bcb32ab 100644 --- a/cmd/boulder-observer/main.go +++ b/cmd/boulder-observer/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "io/ioutil" @@ -21,13 +22,11 @@ func main() { var config observer.ObsConf err = yaml.Unmarshal(configYAML, &config) if err != nil { - cmd.FailOnError(err, "failed to parse YAML config") + cmd.FailOnError(err, "failed to parse yaml config") } - // validate config - err = config.Validate() - if err != nil { - cmd.FailOnError(err, "YAML config failed validation") + if config.DebugAddr == "" { + cmd.FailOnError(errors.New(""), "debugaddr is not defined") } // start monitoring and logging @@ -35,6 +34,12 @@ func main() { defer logger.AuditPanic() logger.Info(cmd.VersionString()) + // validate config + err = config.Validate(logger) + if err != nil { + cmd.FailOnError(err, "config failed validation") + } + // start daemon logger.Infof("Initializing boulder-observer daemon from config: %s", *configPath) logger.Debugf("Using config: %+v", config) diff --git a/observer/mon_conf.go b/observer/mon_conf.go index ef69faf847a..f6f5db7b888 100644 --- a/observer/mon_conf.go +++ b/observer/mon_conf.go @@ -1,44 +1,60 @@ package observer import ( - "errors" "fmt" "strings" - "github.com/letsencrypt/boulder/observer/plugins" + "github.com/letsencrypt/boulder/cmd" + p "github.com/letsencrypt/boulder/observer/probes" + "gopkg.in/yaml.v2" ) -var ( - errNewMonEmpty = errors.New("monitor config is empty") - errNewMonInvalid = errors.New("monitor config is invalid") -) +type settings map[string]interface{} -// MonConf is exported to receive the supplied monitor config +// MonConf is exported to receive yaml configuration type MonConf struct { - Enabled bool `yaml:"enabled"` - Period int `yaml:"period"` - Timeout int `yaml:"timeout"` - Plugin plugins.Info `yaml:"plugin"` - Settings map[string]interface{} `yaml:"settings"` + Valid bool + Period cmd.ConfigDuration `yaml:"period"` + Timeout int `yaml:"timeout"` + Kind string `yaml:"type"` + Settings settings `yaml:"settings"` } func (c MonConf) normalize() { - c.Plugin.Name = strings.ToLower(c.Plugin.Name) - c.Plugin.Path = strings.ToLower(c.Plugin.Path) + c.Kind = strings.ToLower(c.Kind) +} + +func (c MonConf) unmashalProbeSettings() (p.Configurer, error) { + probeConf, err := p.GetProbeConf(c.Kind, c.Settings) + if err != nil { + return nil, err + } + s, _ := yaml.Marshal(c.Settings) + probeConf, err = probeConf.UnmarshalSettings(s) + if err != nil { + return nil, err + } + return probeConf, nil } // validate normalizes and validates the received monitor config -func (c MonConf) validate() error { +func (c *MonConf) validate() error { c.normalize() - pluginConf, err := plugins.GetPluginConf(c.Settings, c.Plugin.Path, c.Plugin.Name) + probeConf, err := c.unmashalProbeSettings() if err != nil { - if err != nil { - return fmt.Errorf("failed to get plugin: %w", err) - } + return err } - err = pluginConf.Validate() + err = probeConf.Validate() if err != nil { - return fmt.Errorf("failed to validate plugin settings: %w", err) + return fmt.Errorf( + "failed to validate: %s prober with settings: %+v due to: %w", + c.Kind, probeConf, err) } + c.Valid = true return nil } + +func (c MonConf) getProber() p.Prober { + probeConf, _ := c.unmashalProbeSettings() + return probeConf.AsProbe() +} diff --git a/observer/monitor.go b/observer/monitor.go index f69df2002b3..a515c74c45e 100644 --- a/observer/monitor.go +++ b/observer/monitor.go @@ -1,54 +1,44 @@ package observer import ( + "strconv" "time" blog "github.com/letsencrypt/boulder/log" - "github.com/letsencrypt/boulder/observer/plugins" + p "github.com/letsencrypt/boulder/observer/probes" "github.com/prometheus/client_golang/prometheus" ) +// monitor contains the parsed, normalized, and validated configuration +// describing a given oberver monitor type monitor struct { - name string - period time.Duration - timeout time.Duration - pluginIs string - probe plugins.Plugin - logger blog.Logger - metric prometheus.Registerer + valid bool + period time.Duration + prober p.Prober + logger blog.Logger + metric prometheus.Registerer } +// start creates a ticker channel then spins off a prober goroutine for +// each period specified in the monitor config and a timeout inferred +// from that period. This is not perfect, it means that the effective +// deadline for a prober goroutine will be TTL + time-to-schedule, but +// it's close enough for our purposes func (m monitor) start() *time.Ticker { ticker := time.NewTicker(m.period) go func() { for { select { - case tick := <-ticker.C: - success, took := m.probe.Do(tick, m.timeout) - statTotalObservations.WithLabelValues(m.pluginIs, m.name).Add(1) - if !success { - statTotalErrors.WithLabelValues(m.pluginIs, m.name).Add(1) - m.logger.Infof("%s monitor %q failed while taking:=%s", m.pluginIs, m.name, took.String()) - return - } - m.logger.Infof("%s monitor %q succeeded while taking:=%s", m.pluginIs, m.name, took.String()) + case <-ticker.C: + result, dur := m.prober.Do(m.period) + statObservations.WithLabelValues( + m.prober.Name(), m.prober.Type(), strconv.FormatBool(result)). + Observe(dur.Seconds()) + m.logger.Infof( + "type=[%s] result=[%v] duration=[%f] name=[%s]", + m.prober.Type(), result, dur.Seconds(), m.prober.Name()) } } }() return ticker } - -func (m monitor) New(c MonConf, log blog.Logger, prom prometheus.Registerer, t int) *monitor { - if c.Timeout == 0 { - c.Timeout = t - } - plugin, _ := plugins.GetPluginConf(c.Settings, c.Plugin.Path, c.Plugin.Name) - m.name = plugin.GetMonitorName() - m.period = time.Duration(c.Period * 1000000000) - m.timeout = time.Duration(c.Timeout * 1000000000) - m.pluginIs = c.Plugin.Name - m.probe = plugin.AsProbe() - m.logger = log - m.metric = prom - return &m -} diff --git a/observer/obs_conf.go b/observer/obs_conf.go index a6bf7dfa065..d3a024a4fc1 100644 --- a/observer/obs_conf.go +++ b/observer/obs_conf.go @@ -2,57 +2,75 @@ package observer import ( "errors" + "fmt" "github.com/letsencrypt/boulder/cmd" - "github.com/letsencrypt/boulder/observer/plugins" + blog "github.com/letsencrypt/boulder/log" + p "github.com/letsencrypt/boulder/observer/probes" ) var ( - errNewObsNoMons = errors.New("observer config is invalid, 0 monitors configured") - errNewObsEmpty = errors.New("observer config is empty") - errNewObsInvalid = errors.New("observer config is invalid") + errNewObsNoMons = errors.New("observer config is invalid, 0 monitors configured") + errNewObsEmpty = errors.New("observer config is empty") ) -// ObsConf is exported to receive the supplied observer config +// ObsConf is exported to receive yaml configuration type ObsConf struct { Syslog cmd.SyslogConfig `yaml:"syslog"` - DebugAddr string `yaml:"debugAddr"` - Modules []plugins.Conf `yaml:"modules"` - Timeout int `yaml:"timeout"` - NewMons []MonConf `yaml:"monitors"` + DebugAddr string `yaml:"debugaddr"` + Modules []p.Configurer `yaml:"modules"` + MonConfs []*MonConf `yaml:"monitors"` } -func (n *ObsConf) validateMonConfs() error { - i := 0 - for _, m := range n.NewMons { - if !m.Enabled { - continue - } +func (n *ObsConf) validateMonConfs() ([]error, bool) { + var validationErrs []error + for _, m := range n.MonConfs { err := m.validate() if err != nil { - return err + validationErrs = append(validationErrs, err) } - n.NewMons[i] = m - i++ } - n.NewMons = n.NewMons[:i] - return nil + + // all configured monitors are invalid, cannot continue + if len(n.MonConfs) == len(validationErrs) { + return validationErrs, false + } + return validationErrs, true } -// Validate normalizes and validates the received monitor config -func (n *ObsConf) Validate() error { +// Validate normalizes and validates the observer config as well as each +// monitor config. If no valid monitor configs remain, Validate will +// return an error indicating that observer cannot be started. In all +// instances the the rationale for invalidating a monitor will logged to +// stderr +func (n *ObsConf) Validate(log blog.Logger) error { if n == nil { return errNewObsEmpty } - if n.DebugAddr == "" { - return errNewObsInvalid + + if len(n.MonConfs) == 0 { + return errNewObsNoMons } - err := n.validateMonConfs() - if err != nil { - return err + + logErrs := func(errs []error, lenMons int) { + log.Errf("%d of %d monitors failed validation", len(errs), lenMons) + for _, err := range errs { + log.Errf("invalid monitor: %s", err) + } } - if len(n.NewMons) == 0 { - return errNewObsNoMons + + errs, ok := n.validateMonConfs() + + // if no valid mons remain, log validation errors, and return in + // error + if len(errs) != 0 && !ok { + logErrs(errs, len(n.MonConfs)) + return fmt.Errorf("no valid mons, cannot continue") + } + + // if at least 1 valid monitor remains, only log validation errors + if len(errs) != 0 && ok { + logErrs(errs, len(n.MonConfs)) } return nil } diff --git a/observer/observer.go b/observer/observer.go index 66d950df44b..4bcaa323348 100644 --- a/observer/observer.go +++ b/observer/observer.go @@ -1,72 +1,68 @@ package observer import ( - "time" + "strconv" blog "github.com/letsencrypt/boulder/log" + "github.com/letsencrypt/boulder/metrics" + + // _ are probes imported to trigger init func + _ "github.com/letsencrypt/boulder/observer/probes/dns" + _ "github.com/letsencrypt/boulder/observer/probes/http" "github.com/prometheus/client_golang/prometheus" ) var ( - statTotalMonitors = prometheus.NewCounterVec( + statMonitors = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "obs_monitors", Help: "count of configured monitors", }, - []string{"plugin", "name"}, + []string{"name", "type", "valid"}, ) - statTotalErrors = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "obs_errors", - Help: "count of errors encountered by all monitors", + statObservations = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "obs_observations", + Help: "time taken for a monitor to perform a request/query", + Buckets: metrics.InternetFacingBuckets, }, - []string{"plugin", "name"}, - ) - statTotalObservations = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "obs_oberservations", - Help: "count of observations performed by all monitors", - }, - []string{"plugin", "name"}, + []string{"name", "type", "result"}, ) ) -// Observer acts as the +// Observer contains the parsed, normalized, and validated configuration +// describing a collection of monitors and the metrics to be collected type Observer struct { - Timeout time.Duration Logger blog.Logger Metric prometheus.Registerer Monitors []*monitor } -// Start acts as the supervisor for all monitor goroutines +// Start registers global metrics and spins off a goroutine for each of +// the configured monitors func (o Observer) Start() { - runningChan := make(chan bool) - // register metrics - o.Metric.MustRegister(statTotalErrors) - o.Metric.MustRegister(statTotalMonitors) - o.Metric.MustRegister(statTotalObservations) + o.Metric.MustRegister(statMonitors) + o.Metric.MustRegister(statObservations) // start each monitor for _, mon := range o.Monitors { - statTotalMonitors.WithLabelValues(mon.pluginIs, mon.name).Inc() - go mon.start() + if mon.valid { + // TODO(@beautifulentropy): track and restart unhealthy goroutines + go mon.start() + } + statMonitors.WithLabelValues( + mon.prober.Name(), mon.prober.Type(), strconv.FormatBool(mon.valid)).Inc() } - // run forever - <-runningChan + select {} } -// New initializes new Observer objects +// New creates new observer and it's corresponding monitor objects func New(c ObsConf, l blog.Logger, p prometheus.Registerer) *Observer { - var o Observer - o.Timeout = time.Duration(c.Timeout * 1000000000) - o.Logger = l - o.Metric = p - for _, monConf := range c.NewMons { - var mon monitor - o.Monitors = append(o.Monitors, mon.New(monConf, l, p, c.Timeout)) + var monitors []*monitor + for _, c := range c.MonConfs { + monitors = append(monitors, &monitor{c.Valid, c.Period.Duration, c.getProber(), l, p}) } - return &o + return &Observer{l, p, monitors} } diff --git a/observer/plugins/build.sh b/observer/plugins/build.sh deleted file mode 100755 index bbe45944ce7..00000000000 --- a/observer/plugins/build.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash - -set -eu -STATUS="ERROR" -PLUGINS_PATH="observer/plugins/" -esc=$'\033' -aesc="${esc}[" # posix compliant escape sequence - - -function print_outcome() { - if [ "$STATUS" == OK ] - then - echo -e "${aesc}0;32m""$STATUS""${aesc}0m" - else - echo -e "${aesc}0;31m""$STATUS""${aesc}0m" - fi -} -function print_heading() { - echo -e "${aesc}0;34m"$1"${aesc}0m" -} - -function print_entry() { - echo -e "${aesc}0;36m"$1"${aesc}0m" -} - -# -# Detect exit and print outcome -# -trap "print_outcome" EXIT - -# -# Look for plugins to compile -# -plugins=( $(find $PLUGINS_PATH -mindepth 1 -maxdepth 1 -not -path '*/\.*' -type d 2> /dev/null | sort) ) -if [ -z "${plugins[@]+x}" ] -then - echo "There are no plugins at path: "\"$PLUGINS_PATH\""" - exit 1 -fi - -# -# Clean and compile observer plugins -# -print_heading "Building plugins:" -for plugin in "${plugins[@]}" -do - # for each , attempt to build a .so - print_entry "⚙️ "${plugin}".so" - if [ -f "${plugin}".so ] - then - # force remove because previously compiled .so files are protected - rm -f "${plugin}".so - fi - $(go build -buildmode=plugin -o "${plugin}".so "${PLUGINS_PATH}"/$(basename -- "${plugin}")/*.go) - print_entry "✅$(basename -- "${plugin}").so" -done - -# assume success if we got here -STATUS="OK" diff --git a/observer/plugins/dns/dns.go b/observer/plugins/dns/dns.go deleted file mode 100644 index 0bf96c2676e..00000000000 --- a/observer/plugins/dns/dns.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "fmt" - "time" - - odns "github.com/miekg/dns" -) - -// Probe is the exported handler object for monitors configured to use -// DNS probes -type Probe struct { - QProto string - QRecurse bool - QName string - QServer string - QType uint16 -} - -// Do performs the DNS query provided by the monitor settings -func (p Probe) Do(tick time.Time, timeout time.Duration) (bool, time.Duration) { - m := new(odns.Msg) - m.SetQuestion(odns.Fqdn(p.QName), p.QType) - m.RecursionDesired = p.QRecurse - c := odns.Client{Timeout: timeout} - r, _, err := c.Exchange(m, p.QServer) - if err != nil { - return false, time.Since(tick) - } - if r == nil { - fmt.Println("response was nil") - return false, time.Since(tick) - } - if r.Rcode != odns.RcodeSuccess { - fmt.Println("response code failed") - return false, time.Since(tick) - } - return true, time.Since(tick) -} - -func main() { - return -} diff --git a/observer/plugins/dns/dns_conf.go b/observer/plugins/dns/dns_conf.go deleted file mode 100644 index 7ad4018ddaa..00000000000 --- a/observer/plugins/dns/dns_conf.go +++ /dev/null @@ -1,102 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "net" - "strings" - - "github.com/letsencrypt/boulder/observer/plugins" - "github.com/miekg/dns" - "gopkg.in/yaml.v2" -) - -var ( - errNewMonDNSEmpty = errors.New("monitor DNS config is empty") - errNewMonDNSInvalid = errors.New("monitor DNS config is invalid") - - validQprotos = []string{"udp", "tcp"} - validQtypes = map[string]uint16{"A": 1, "TXT": 16, "AAAA": 28, "CAA": 257} -) - -// DNS is the exported name of the DNS probe plugin -var DNS Conf - -// Conf is exported to receive the supplied probe config -type Conf struct { - QProto string `yaml:"qproto"` - QRecurse bool `yaml:"qrecurse"` - QName string `yaml:"qname"` - QServer string `yaml:"qserver"` - QType string `yaml:"qtype"` -} - -func (c Conf) normalize() { - c.QProto = strings.ToLower(c.QProto) - c.QName = strings.ToLower(c.QName) - c.QServer = strings.ToLower(c.QServer) - c.QType = strings.ToLower(c.QType) -} - -// Validate normalizes and validates the received probe config -func (c Conf) Validate() error { - c.normalize() - qprotoValid := func() bool { - for _, i := range validQprotos { - if c.QProto == i { - return true - } - } - return false - }() - if !qprotoValid { - return fmt.Errorf("Invalid qproto: %w", errNewMonDNSInvalid) - } - if !dns.IsFqdn(dns.Fqdn(c.QName)) { - return fmt.Errorf("Invalid qname: %w", errNewMonDNSInvalid) - } - if net.ParseIP(c.QServer) == nil { - if !dns.IsFqdn(dns.Fqdn(c.QServer)) { - return fmt.Errorf("Invalid qserver: %w", errNewMonDNSInvalid) - } - } - qtypeValid := func() bool { - for i := range validQtypes { - if c.QType == i { - return true - } - } - return false - }() - if !qtypeValid { - return fmt.Errorf("Invalid qtype: %w", errNewMonDNSInvalid) - } - - return nil -} - -// GetMonitorName returns a name that uniquely identifies the monitor -func (c Conf) GetMonitorName() string { - return fmt.Sprintf("%s-%s-%s-%s", c.QProto, c.QServer, c.QName, c.QType) -} - -// FromSettings returns the supplied settings as an `Observer.PluginConf` -func (c Conf) FromSettings(settings []byte) (plugins.Conf, error) { - var conf Conf - err := yaml.Unmarshal(settings, &conf) - if err != nil { - return nil, fmt.Errorf("couldn't unmarshal settings for dns plugin: %w", err) - } - return conf, nil -} - -// AsProbe returns the `Conf` object as a DNS probe -func (c Conf) AsProbe() plugins.Plugin { - return Probe{ - QProto: c.QProto, - QRecurse: c.QRecurse, - QName: c.QName, - QServer: c.QServer, - QType: validQtypes[c.QType], - } -} diff --git a/observer/plugins/http/http.go b/observer/plugins/http/http.go deleted file mode 100644 index 5b48b7ae32d..00000000000 --- a/observer/plugins/http/http.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "net/http" - "net/url" - "time" -) - -// Probe is the exported handler object for monitors configured to use -// HTTP probes -type Probe struct { - URL url.URL - RCode int -} - -// Do performs the HTTP request provided by the monitor settings -func (p Probe) Do(tick time.Time, timeout time.Duration) (bool, time.Duration) { - client := http.Client{Timeout: timeout} - resp, err := client.Get(p.URL.String()) - if err != nil { - return false, time.Since(tick) - } - if resp.StatusCode == p.RCode { - return true, time.Since(tick) - } - return false, time.Since(tick) -} - -func main() { - return -} diff --git a/observer/plugins/http/http_conf.go b/observer/plugins/http/http_conf.go deleted file mode 100644 index 772ebf3f078..00000000000 --- a/observer/plugins/http/http_conf.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "net/url" - "strings" - - "github.com/letsencrypt/boulder/observer/plugins" - "gopkg.in/yaml.v2" -) - -var ( - errNewMonHTTPEmpty = errors.New("monitor HTTP config is empty") - errNewMonHTTPInvalid = errors.New("monitor HTTP config is invalid") -) - -// HTTP is the exported name of the HTTP probe -var HTTP Conf - -// Conf is exported to receive the supplied probe config -type Conf struct { - URL string `yaml:"url"` - RCode int `yaml:"rcode"` -} - -func (c Conf) normalize() { - c.URL = strings.ToLower(c.URL) -} - -// Validate normalizes and validates the received probe config -func (c Conf) Validate() error { - c.normalize() - _, err := url.ParseRequestURI(c.URL) - if err != nil { - return errNewMonHTTPInvalid - } - return nil -} - -// GetMonitorName returns a name that uniquely identifies the monitor -func (c Conf) GetMonitorName() string { - return fmt.Sprintf("%s-%d", c.URL, c.RCode) -} - -// FromSettings returns the supplied settings as an `Observer.PluginConf` -func (c Conf) FromSettings(settings []byte) (plugins.Conf, error) { - err := yaml.Unmarshal(settings, &c) - if err != nil { - return nil, fmt.Errorf("couldn't unmarshal settings for http plugin: %w", err) - } - return c, nil -} - -// AsProbe returns the NewHTTP object as an HTTP probe -func (c Conf) AsProbe() plugins.Plugin { - url, _ := url.Parse(c.URL) - return Probe{URL: *url, RCode: c.RCode} -} diff --git a/observer/plugins/plugins.go b/observer/plugins/plugins.go deleted file mode 100644 index d8283dc0ef8..00000000000 --- a/observer/plugins/plugins.go +++ /dev/null @@ -1,55 +0,0 @@ -package plugins - -import ( - "fmt" - "plugin" - "time" - - "gopkg.in/yaml.v2" -) - -// Plugin is the expected interface for probe plugins -type Plugin interface { - Do(time.Time, time.Duration) (bool, time.Duration) -} - -// Conf is the expected interface for plugin config data -type Conf interface { - FromSettings([]byte) (Conf, error) - GetMonitorName() string - Validate() error - AsProbe() Plugin -} - -// Info contains the name of the plugin and the path where it can be -// loaded from -type Info struct { - Name string `yaml:"name"` - Path string `yaml:"path"` -} - -// GetPluginConf performs a lookup for the probe plugin specified in the -// `MonConf` and returns the specified `ProbeConf` by calling it's -// `FromSettings` method -func GetPluginConf(s map[string]interface{}, path, name string) (Conf, error) { - settings, _ := yaml.Marshal(s) - plugin, err := plugin.Open(path) - if err != nil { - return nil, fmt.Errorf("couldn't load plugin file: %q due to error: %w", path, err) - } - - symProbeConf, err := plugin.Lookup(name) - if err != nil { - return nil, fmt.Errorf( - "plugin: %q not in file: %q due to error: %w", name, path, err) - } - - var probeConf Conf - probeConf, ok := symProbeConf.(Conf) - if !ok { - return nil, fmt.Errorf( - "plugin: %q in file: %q does not satisfy the PluginConf interface", - name, path) - } - return probeConf.FromSettings(settings) -} diff --git a/observer/probes/dns/dns.go b/observer/probes/dns/dns.go new file mode 100644 index 00000000000..4d38e6f6691 --- /dev/null +++ b/observer/probes/dns/dns.go @@ -0,0 +1,48 @@ +package observer + +import ( + "fmt" + "time" + + "github.com/miekg/dns" +) + +// DNSProbe is the exported handler object for monitors configured to +// perform DNS queries +type DNSProbe struct { + Proto string + Server string + Recurse bool + QName string + QType uint16 +} + +// Name returns a name that uniquely identifies the monitor +func (p DNSProbe) Name() string { + return fmt.Sprintf("%s-%s-%s-%s", p.Proto, p.Server, p.QName, dns.TypeToString[p.QType]) +} + +// Type returns a name that uniquely identifies the monitor +func (p DNSProbe) Type() string { + return "DNS" +} + +// Do is the query handler for HTTP probes +func (p DNSProbe) Do(timeout time.Duration) (bool, time.Duration) { + m := new(dns.Msg) + m.SetQuestion(dns.Fqdn(p.QName), p.QType) + m.RecursionDesired = p.Recurse + start := time.Now() + c := dns.Client{Timeout: timeout, Net: p.Proto} + r, _, err := c.Exchange(m, p.Server) + if err != nil { + return false, time.Since(start) + } + if r == nil { + return false, time.Since(start) + } + if r.Rcode != dns.RcodeSuccess { + return false, time.Since(start) + } + return true, time.Since(start) +} diff --git a/observer/probes/dns/dns_conf.go b/observer/probes/dns/dns_conf.go new file mode 100644 index 00000000000..f82b730cd1d --- /dev/null +++ b/observer/probes/dns/dns_conf.go @@ -0,0 +1,139 @@ +package observer + +import ( + "fmt" + "net" + "regexp" + "strings" + + p "github.com/letsencrypt/boulder/observer/probes" + "github.com/miekg/dns" + "gopkg.in/yaml.v2" +) + +var ( + validProtos = []string{"udp", "tcp"} + validQTypes = map[string]uint16{"A": 1, "TXT": 16, "AAAA": 28, "CAA": 257} +) + +// DNSConf is exported to receive the supplied probe config +type DNSConf struct { + Proto string `yaml:"protocol"` + Server string `yaml:"server"` + Recurse bool `yaml:"recurse"` + QName string `yaml:"query_name"` + QType string `yaml:"query_type"` +} + +// UnmarshalSettings takes yaml as bytes and unmarshals it to the +// to a DNSConf object +func (c DNSConf) UnmarshalSettings(settings []byte) (p.Configurer, error) { + var conf DNSConf + err := yaml.Unmarshal(settings, &conf) + if err != nil { + return nil, err + } + return conf, nil +} + +func (c DNSConf) normalize() { + c.Proto = strings.Trim(strings.ToLower(c.Proto), " ") + c.QName = strings.Trim(strings.ToLower(c.QName), " ") + c.Server = strings.Trim(strings.ToLower(c.Server), " ") + c.QType = strings.Trim(strings.ToLower(c.QType), " ") +} + +func (c DNSConf) validateServer() error { + // ensure `server` does not contain scheme + schemeExp := regexp.MustCompile("^([[:alnum:]]+://)(.*)+$") + if schemeExp.MatchString(c.Server) { + return fmt.Errorf( + "invalid server, %q, remove %q", c.Server, + strings.SplitAfter(c.Server, "://")[0]) + } + + // ensure `server` contains a port + serverExp := regexp.MustCompile("^(.*)+([[:alnum:]])+(:)([[:digit:]]{1,5})$") + if !serverExp.MatchString(c.Server) { + return fmt.Errorf( + "invalid server, %q, is missing a port", c.Server) + } + + // ensure `server` is a valid fqdn, ipv4, or ipv6 address + host := serverExp.FindAllStringSubmatch(c.Server, -1)[0][1] + ipv6 := net.ParseIP(host).To16() + ipv4 := net.ParseIP(host).To4() + fqdn := dns.IsFqdn(dns.Fqdn(host)) + if ipv6 == nil && ipv4 == nil && fqdn != true { + return fmt.Errorf( + "invalid server, %q, is not an fqdn or ipv4/6 address", c.Server) + } + return nil +} + +func (c DNSConf) validateProto() error { + for _, i := range validProtos { + if c.Proto == i { + return nil + } + } + return fmt.Errorf( + "invalid protocol, got: %q, expected one in: %s", c.Proto, validProtos) +} + +func (c DNSConf) validateQType() error { + q := make([]string, 0, len(validQTypes)) + for i := range validQTypes { + q = append(q, i) + if c.QType == i { + return nil + } + } + return fmt.Errorf( + "invalid query_type, got: %q, expected one in %s", c.QType, q) +} + +// Validate normalizes and validates the received probe config +func (c DNSConf) Validate() error { + c.normalize() + + // validate `query_name` + if !dns.IsFqdn(dns.Fqdn(c.QName)) { + return fmt.Errorf("invalid query_name, %q is not an fqdn", c.QName) + } + + // validate `server` + err := c.validateServer() + if err != nil { + return err + } + + // validate `protocol` + err = c.validateProto() + if err != nil { + return err + } + + // validate `query_type` + err = c.validateQType() + if err != nil { + return err + } + return nil +} + +// AsProbe returns the `Conf` object as a DNS probe +func (c DNSConf) AsProbe() p.Prober { + return DNSProbe{ + Proto: c.Proto, + Recurse: c.Recurse, + QName: c.QName, + Server: c.Server, + QType: validQTypes[c.QType], + } +} + +// init is called on observer start and registers DNS as a probe type +func init() { + p.Register("DNS", DNSConf{}) +} diff --git a/observer/probes/dns/dns_conf_test.go b/observer/probes/dns/dns_conf_test.go new file mode 100644 index 00000000000..b45d6ccffc2 --- /dev/null +++ b/observer/probes/dns/dns_conf_test.go @@ -0,0 +1,106 @@ +package observer + +import ( + "testing" +) + +func TestDNSConf_validateServer(t *testing.T) { + type fields struct { + Server string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + // ipv4 cases + {"ipv4 with port", fields{"1.1.1.1:53"}, false}, + {"ipv4 without port", fields{"1.1.1.1"}, true}, + {"ipv4 port num missing", fields{"1.1.1.1:"}, true}, + {"ipv4 string for port", fields{"1.1.1.1:foo"}, true}, + + // ipv6 cases + {"ipv6 with port", fields{"2606:4700:4700::1111:53"}, false}, + {"ipv6 without port", fields{"2606:4700:4700::1111"}, true}, + {"ipv6 port num missing", fields{"2606:4700:4700::1111:"}, true}, + {"ipv6 string for port", fields{"2606:4700:4700:foo"}, true}, + + // hostname cases + {"hostname with port", fields{"foo:53"}, false}, + {"hostname without port", fields{"foo"}, true}, + {"hostname port num missing", fields{"foo:"}, true}, + {"hostname string for port", fields{"foo:bar"}, true}, + + // fqdn cases + {"hostname with port", fields{"foo.baz:53"}, false}, + {"hostname without port", fields{"foo.baz"}, true}, + {"hostname port num missing", fields{"foo.baz:"}, true}, + {"hostname string for port", fields{"foo.baz:bar"}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := DNSConf{ + Server: tt.fields.Server, + } + if err := c.validateServer(); (err != nil) != tt.wantErr { + t.Errorf("DNSConf.validateServer() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDNSConf_validateQType(t *testing.T) { + type fields struct { + QType string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + // valid + {"A", fields{"A"}, false}, + {"AAAA", fields{"AAAA"}, false}, + {"TXT", fields{"TXT"}, false}, + // invalid + {"AAA", fields{"AAA"}, true}, + {"TXTT", fields{"TXTT"}, true}, + {"D", fields{"D"}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := DNSConf{ + QType: tt.fields.QType, + } + if err := c.validateQType(); (err != nil) != tt.wantErr { + t.Errorf("DNSConf.validateQType() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDNSConf_validateProto(t *testing.T) { + type fields struct { + Proto string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + {"tcp", fields{"tcp"}, false}, + {"udp", fields{"udp"}, false}, + // invalid + {"foo", fields{"foo"}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := DNSConf{ + Proto: tt.fields.Proto, + } + if err := c.validateProto(); (err != nil) != tt.wantErr { + t.Errorf("DNSConf.validateProto() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/observer/probes/http/http.go b/observer/probes/http/http.go new file mode 100644 index 00000000000..481f6267987 --- /dev/null +++ b/observer/probes/http/http.go @@ -0,0 +1,40 @@ +package observer + +import ( + "fmt" + "net/http" + "net/url" + "time" +) + +// HTTPProbe is the exported handler object for monitors configured to +// perform HTTP requests +type HTTPProbe struct { + URL url.URL + RCode int +} + +// Name returns a name that uniquely identifies the monitor +func (p HTTPProbe) Name() string { + return fmt.Sprintf("%s-%d", p.URL.String(), p.RCode) +} + +// Type returns the type of prober as a string +func (p HTTPProbe) Type() string { + return "HTTP" +} + +// Do is the request handler for HTTP probes +func (p HTTPProbe) Do(timeout time.Duration) (bool, time.Duration) { + client := http.Client{Timeout: timeout} + start := time.Now() + // TODO(@beautifulentropy): add support for more than HTTP GET + resp, err := client.Get(p.URL.String()) + if err != nil { + return false, time.Since(start) + } + if resp.StatusCode == p.RCode { + return true, time.Since(start) + } + return false, time.Since(start) +} diff --git a/observer/probes/http/http_conf.go b/observer/probes/http/http_conf.go new file mode 100644 index 00000000000..296290d0e1b --- /dev/null +++ b/observer/probes/http/http_conf.go @@ -0,0 +1,63 @@ +package observer + +import ( + "fmt" + "net/url" + "strings" + + p "github.com/letsencrypt/boulder/observer/probes" + "gopkg.in/yaml.v2" +) + +// HTTPConf is exported to receive the supplied probe config +type HTTPConf struct { + URL string `yaml:"url"` + RCode int `yaml:"rcode"` +} + +// UnmarshalSettings takes YAML as bytes and unmarshals it to the +// to a HTTPConf object +func (c HTTPConf) UnmarshalSettings(settings []byte) (p.Configurer, error) { + var conf HTTPConf + err := yaml.Unmarshal(settings, &conf) + if err != nil { + return nil, err + } + return conf, nil +} + +func (c HTTPConf) normalize() { + c.URL = strings.Trim(strings.ToLower(c.URL), " ") +} + +// Validate normalizes and validates the received probe config +func (c HTTPConf) Validate() error { + c.normalize() + + // validate `url` + url, err := url.Parse(c.URL) + if err != nil { + return fmt.Errorf( + "invalid url, got: %q, expected a valid url", c.URL) + } + if url.Scheme == "" { + return fmt.Errorf( + "invalid url, got: %q, missing scheme", c.URL) + } + if c.RCode == 0 { + return fmt.Errorf( + "invalid rcode, got: %q, please specify a response code", c.RCode) + } + return nil +} + +// AsProbe returns the NewHTTP object as an HTTP probe +func (c HTTPConf) AsProbe() p.Prober { + url, _ := url.Parse(c.URL) + return HTTPProbe{URL: *url, RCode: c.RCode} +} + +// init is called on observer start and registers HTTP as a probe type +func init() { + p.Register("HTTP", HTTPConf{}) +} diff --git a/observer/probes/http/http_conf_test.go b/observer/probes/http/http_conf_test.go new file mode 100644 index 00000000000..c7c6bd8affe --- /dev/null +++ b/observer/probes/http/http_conf_test.go @@ -0,0 +1,34 @@ +package observer + +import "testing" + +func TestHTTPConf_Validate(t *testing.T) { + type fields struct { + URL string + RCode int + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + // valid + {"valid fqdn valid rcode", fields{"http://example.com", 200}, false}, + {"valid hostname valid rcode", fields{"example", 200}, true}, + // invalid + {"valid fqdn bad rcode", fields{"http://example.com", 0}, true}, + {"bad fqdn good rcode", fields{":::::", 200}, true}, + {"missing scheme", fields{"example.com", 200}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := HTTPConf{ + URL: tt.fields.URL, + RCode: tt.fields.RCode, + } + if err := c.Validate(); (err != nil) != tt.wantErr { + t.Errorf("HTTPConf.Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/observer/probes/prober.go b/observer/probes/prober.go new file mode 100644 index 00000000000..8c600ffeb82 --- /dev/null +++ b/observer/probes/prober.go @@ -0,0 +1,50 @@ +package observer + +import ( + "fmt" + "time" + + "github.com/letsencrypt/boulder/cmd" +) + +const ( + initProb = "Error while initializing probes" +) + +var ( + // Registry is the central registry for prober Configurer types + Registry = make(map[string]Configurer) +) + +// Prober is the expected interface for all Prober types +type Prober interface { + Name() string + Type() string + Do(time.Duration) (bool, time.Duration) +} + +// Configurer is the expected interface for Prober Configurer types +type Configurer interface { + UnmarshalSettings([]byte) (Configurer, error) + Validate() error + AsProbe() Prober +} + +// GetProbeConf returns the probe configurer specified by name from +// `observer.Registry` +func GetProbeConf(kind string, s map[string]interface{}) (Configurer, error) { + if _, ok := Registry[kind]; ok { + return Registry[kind], nil + } + return nil, fmt.Errorf("%s is not a registered probe type", kind) +} + +// Register is called in every prober's `init()` function and adds the +// probe configurer to `observer.Registry` +func Register(kind string, c Configurer) { + if _, ok := Registry[kind]; ok { + cmd.FailOnError( + fmt.Errorf("configurer: %s has already been added", kind), initProb) + } + Registry[kind] = c +} diff --git a/test/config-next/observer.yaml b/test/config-next/observer.yaml deleted file mode 100644 index 3ae44888d25..00000000000 --- a/test/config-next/observer.yaml +++ /dev/null @@ -1,54 +0,0 @@ ---- -debugAddr: :8040 -syslog: - stdoutlevel: 6 - sysloglevel: 6 -timeout: 10 -monitors: - - - enabled: true - period: 10 - plugin: - name: DNS - path: "./observer/plugins/dns.so" - settings: - qproto: udp - qrecurse: false - qname: letsencrypt.org - qtype: A - qserver: "owen.ns.cloudflare.com:53" - - - - enabled: true - period: 10 - plugin: - name: DNS - path: "./observer/plugins/dns.so" - settings: - qproto: udp - qrecurse: true - qname: google.com - qtype: A - qserver: "8.8.8.8:53" - - - - enabled: true - period: 10 - plugin: - name: DNS - path: "./observer/plugins/dns.so" - settings: - qproto: tcp - qrecurse: true - qname: google.com - qtype: A - qserver: "8.8.8.8:53" - - - enabled: true - period: 2 - plugin: - name: HTTP - path: "./observer/plugins/http.so" - settings: - url: https://letsencrypt.org - rcode: 200 diff --git a/test/config-next/observer.yml b/test/config-next/observer.yml new file mode 100644 index 00000000000..422f100fe44 --- /dev/null +++ b/test/config-next/observer.yml @@ -0,0 +1,90 @@ +--- +debugaddr: :8040 +syslog: + stdoutlevel: 6 + sysloglevel: 6 +monitors: + - + period: 5s + type: DNS + settings: + protocol: udp + server: owen.ns.cloudflare.com:53 + recurse: false + query_name: letsencrypt.org + query_type: A + - + period: 5s + type: DNS + settings: + protocol: udp + server: 1.1.1.1:53 + recurse: true + query_name: google.com + query_type: A + - + period: 10s + type: DNS + settings: + protocol: tcp + server: 8.8.8.8:53 + recurse: true + query_name: google.com + query_type: A + - + period: 2s + type: HTTP + settings: + url: https://letsencrypt.org + rcode: 200 + - + period: 5s + type: DNS + settings: + protocol: tcp + server: owen.ns.cloudflare.com:53 + recurse: false + query_name: letsencrypt.org + query_type: A + - + period: 5s + type: DNS + settings: + protocol: tcp + server: 1.1.1.1:53 + recurse: true + query_name: google.com + query_type: A + - + period: 10s + type: DNS + settings: + protocol: udp + server: 8.8.8.8:53 + recurse: true + query_name: google.com + query_type: A + - + period: 5s + type: DNS + settings: + protocol: tcp + server: 2606:4700:4700::1111:53 + recurse: true + query_name: google.com + query_type: A + - + period: 5s + type: DNS + settings: + protocol: udp + server: 2606:4700:4700::1111:53 + recurse: true + query_name: google.com + query_type: A + - + period: 2s + type: HTTP + settings: + url: http://letsencrypt.org + rcode: 200 \ No newline at end of file diff --git a/test/prometheus/prometheus.yml b/test/prometheus/prometheus.yml index 6cecd6f9817..488d0aa5bae 100644 --- a/test/prometheus/prometheus.yml +++ b/test/prometheus/prometheus.yml @@ -16,4 +16,4 @@ scrape_configs: - boulder:8008 - boulder:8009 - boulder:8010 - - localhost:8040 + - boulder:8040