github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/observability/probe/aggregate.go (about)

     1  // Copyright 2022 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package probe
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  )
     9  
    10  // Aggregate is an implementation of the Prober interface that returns success
    11  // if all probes under it's controler are successful or false if one more of
    12  // the probes fail.
    13  // Convenience NewAggregate() exists to initialise the map.
    14  type Aggregate struct {
    15  	// Probes is a map of probes to run as part of this aggregate with the key
    16  	// corresponding to well known name for the probe.
    17  	Probes map[string]Prober
    18  }
    19  
    20  // ProbeResultCallBack is a function signature for receiving the result of a
    21  // probers probe call.
    22  type ProbeResultCallback func(probeKey string, val bool, err error)
    23  
    24  func NewAggregate() *Aggregate {
    25  	return &Aggregate{
    26  		Probes: make(map[string]Prober),
    27  	}
    28  }
    29  
    30  // Probe implements Prober Probe
    31  func (a *Aggregate) Probe() (bool, error) {
    32  	return a.ProbeWithResultCallback(ProbeResultCallback(func(_ string, _ bool, _ error) {}))
    33  }
    34  
    35  // ProbeWithResultCallback functions the same as Probe but for each probe tested
    36  // in the aggregate calls the provided callback with probe name and result.
    37  // Useful for building more details reports of what probes are failing and
    38  // succeeding.
    39  func (a *Aggregate) ProbeWithResultCallback(
    40  	cb ProbeResultCallback,
    41  ) (bool, error) {
    42  	rval := true
    43  	var errVal error
    44  
    45  	for name, p := range a.Probes {
    46  		val, err := p.Probe()
    47  		cb(name, val, err)
    48  		if err != nil && errVal == nil {
    49  			errVal = errors.Annotatef(err, "probe %s", name)
    50  		} else if err != nil {
    51  			errVal = errors.Wrap(errVal, errors.Annotatef(err, "probe %s", name))
    52  		}
    53  
    54  		// only change rval if it's currently true. All probes in the aggregate
    55  		// need to return true to get a true answer.
    56  		rval = rval && val
    57  	}
    58  
    59  	return rval, errVal
    60  }