diff --git a/cmd/service-prober/debug.go b/cmd/service-prober/debug.go index a76f7e2a982e1b020f2bacb53de7c1108b6d63ad..1991454d6ae305108ecc19551694fcedd8c79e10 100644 --- a/cmd/service-prober/debug.go +++ b/cmd/service-prober/debug.go @@ -91,9 +91,9 @@ func (d *debugServer) handleDebugHome(w http.ResponseWriter, req *http.Request) } errsOnly := (req.FormValue("errs") == "y") if errsOnly { - d.store.DoErrs(appendResult) + d.store.EachErrs(appendResult) } else { - d.store.DoOk(appendResult) + d.store.Each(appendResult) } debugHomeTpl.Execute(w, map[string]interface{}{ @@ -129,6 +129,6 @@ func (s *dumpResultStore) Push(r *probes.Result) { } } -func (s *dumpResultStore) DoOk(func(*probes.Result)) {} -func (s *dumpResultStore) DoErrs(func(*probes.Result)) {} -func (s *dumpResultStore) Find(string) *probes.Result { return nil } +func (s *dumpResultStore) Each(func(*probes.Result)) {} +func (s *dumpResultStore) EachErrs(func(*probes.Result)) {} +func (s *dumpResultStore) Find(string) *probes.Result { return nil } diff --git a/probes/probe.go b/probes/probe.go index d7a977f348f44b7fa128d0ac73789212dd96c9cc..6856e629c6fbe8258cb7209d1ae5afe22ad20dbc 100644 --- a/probes/probe.go +++ b/probes/probe.go @@ -19,6 +19,7 @@ var ( defaultTimeout = 5 * time.Minute ) +// SpecCommon holds the parameters common to all probe specifications. type SpecCommon struct { Type string `json:"type"` Name string `json:"name"` @@ -42,10 +43,13 @@ type config struct { Probes []*rawSpec `json:"probes"` } +// ProbeImpl is the probe-specific implementation interface. type ProbeImpl interface { RunProbe(context.Context, *log.Logger) error } +// Spec is the generic interface for probe implementation factory +// functions. type Spec interface { Build(map[string]interface{}) (ProbeImpl, error) } @@ -58,6 +62,26 @@ func RegisterProbeType(ptype string, pfunc probeParserFunc) { probeRegistry[ptype] = pfunc } +// ParseConfig parses a JSON-encoded configuration and returns a list of +// Probe objects that can be executed or sent to the scheduler. +// +// Configuration parsing has a few phases: decoding the configuration +// itself is a relatively complex task, as we are implementing +// polymorphysm on probe definitions (via their "params" +// attribute).This requires a multi-level approach in Go, where JSON +// parsing is split into generic / specific stages, with the latter +// delegated to a type-specific parser accessed via a global +// registry. The result of this process is a "template" probe called a +// Spec, which has to be an interface type because the actual struct +// definition is private to the specific probe implementation. +// +// Then, parameterization is applied, potentially generating multiple +// Probes from each Spec. Variables are expanded, and all necessary +// information is bound together in the runtime-ready Probe object. +// The variable expansion process is reflect-heavy but is only +// performed once at the start of the process. The resulting Probe +// objects are minimal and lean. +// func ParseConfig(data []byte, resultStore ResultStore) ([]*Probe, error) { var cfg config if err := json.Unmarshal(data, &cfg); err != nil { @@ -148,6 +172,9 @@ func checkUniqueness(probes []*Probe) error { return nil } +// A Probe holds all the runtime information necessary to run a single +// probe, combining the SpecCommon parameters with a ProbeImpl to +// actually execute the probe. type Probe struct { spec SpecCommon impl ProbeImpl @@ -157,6 +184,7 @@ type Probe struct { vars map[string]interface{} } +// Name is a convenience function to return the probe name. func (p *Probe) Name() string { return p.spec.Name } // Interval satisfies the scheduler.PeriodicEvent interface. diff --git a/probes/results.go b/probes/results.go index 80fb426f0643b04018ad1019d418eb8e37a50b1f..32be2f321732d9f9dcdd490fd167242e4c353d58 100644 --- a/probes/results.go +++ b/probes/results.go @@ -6,6 +6,7 @@ import ( "time" ) +// Result of the execution of a probe. type Result struct { ID string Spec SpecCommon @@ -21,10 +22,17 @@ func (r *Result) String() string { return fmt.Sprintf("%s(%s)", r.Spec.Name, r.ID) } +// ResultStore keeps the most recent probe results in storage, and +// allows access to them (mostly for debugging purposes, as this part +// isn't required by the prober functionality). The results buffer has +// a fixed size, so older results will be dropped as more recent ones +// come in. Failures are kept in a separate buffer, with the hope of +// keeping errors around for longer for inspection purposes, even +// after they've recovered. type ResultStore interface { Push(*Result) - DoOk(func(*Result)) - DoErrs(func(*Result)) + Each(func(*Result)) + EachErrs(func(*Result)) Find(string) *Result } @@ -84,13 +92,13 @@ func (r *memResultStore) Push(result *Result) { r.mx.Unlock() } -func (r *memResultStore) DoOk(f func(*Result)) { +func (r *memResultStore) Each(f func(*Result)) { r.mx.Lock() r.last.Each(f) r.mx.Unlock() } -func (r *memResultStore) DoErrs(f func(*Result)) { +func (r *memResultStore) EachErrs(f func(*Result)) { r.mx.Lock() r.errs.Each(f) r.mx.Unlock()