github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/metrics/spool/manifold.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package spool contains the implementation of a
     5  // worker that extracts the spool directory path from the agent
     6  // config and enables other workers to write and read
     7  // metrics to and from a the spool directory using a writer
     8  // and a reader.
     9  package spool
    10  
    11  import (
    12  	"time"
    13  
    14  	"github.com/juju/errors"
    15  	corecharm "gopkg.in/juju/charm.v6"
    16  	"gopkg.in/juju/worker.v1"
    17  	"gopkg.in/juju/worker.v1/dependency"
    18  	"gopkg.in/tomb.v2"
    19  
    20  	"github.com/juju/juju/agent"
    21  	"github.com/juju/juju/cmd/jujud/agent/engine"
    22  )
    23  
    24  // MetricRecorder records metrics to a spool directory.
    25  type MetricRecorder interface {
    26  	// AddMetric records a metric with the specified key, value, create time
    27  	// and labels to a spool directory.
    28  	AddMetric(key, value string, created time.Time, labels map[string]string) error
    29  	// Close implements io.Closer.
    30  	Close() error
    31  	// IsDeclaredMetrics returns true if the metric recorder
    32  	// is permitted to store metrics with the specified key.
    33  	IsDeclaredMetric(key string) bool
    34  }
    35  
    36  // MetricReader reads metrics from a spool directory.
    37  type MetricReader interface {
    38  	// Read returns all metric batches stored in the spool directory.
    39  	Read() ([]MetricBatch, error)
    40  	// Remove removes the metric batch with the specified uuid
    41  	// from the spool directory.
    42  	Remove(uuid string) error
    43  	// Close implements io.Closer.
    44  	Close() error
    45  }
    46  
    47  // MetricFactory contains the metrics reader and recorder factories.
    48  type MetricFactory interface {
    49  	// Recorder returns a new MetricRecorder.
    50  	Recorder(metrics map[string]corecharm.Metric, charmURL, unitTag string) (MetricRecorder, error)
    51  
    52  	// Reader returns a new MetricReader.
    53  	Reader() (MetricReader, error)
    54  }
    55  
    56  type factory struct {
    57  	spoolDir string
    58  }
    59  
    60  // Reader implements the MetricFactory interface.
    61  func (f *factory) Reader() (MetricReader, error) {
    62  	return NewJSONMetricReader(f.spoolDir)
    63  }
    64  
    65  // Recorder implements the MetricFactory interface.
    66  func (f *factory) Recorder(declaredMetrics map[string]corecharm.Metric, charmURL, unitTag string) (MetricRecorder, error) {
    67  	return NewJSONMetricRecorder(MetricRecorderConfig{
    68  		SpoolDir: f.spoolDir,
    69  		Metrics:  declaredMetrics,
    70  		CharmURL: charmURL,
    71  		UnitTag:  unitTag,
    72  	})
    73  }
    74  
    75  var newFactory = func(spoolDir string) MetricFactory {
    76  	return &factory{spoolDir: spoolDir}
    77  }
    78  
    79  // ManifoldConfig specifies names a spooldirectory manifold should use to
    80  // address its dependencies.
    81  type ManifoldConfig engine.AgentManifoldConfig
    82  
    83  // Manifold returns a dependency.Manifold that extracts the metrics
    84  // spool directory path from the agent.
    85  func Manifold(config ManifoldConfig) dependency.Manifold {
    86  	manifold := engine.AgentManifold(engine.AgentManifoldConfig(config), newWorker)
    87  	manifold.Output = outputFunc
    88  	return manifold
    89  }
    90  
    91  // newWorker creates a degenerate worker that provides access to the metrics
    92  // spool directory path.
    93  func newWorker(a agent.Agent) (worker.Worker, error) {
    94  	metricsSpoolDir := a.CurrentConfig().MetricsSpoolDir()
    95  	err := checkSpoolDir(metricsSpoolDir)
    96  	if err != nil {
    97  		return nil, errors.Annotatef(err, "error checking spool directory %q", metricsSpoolDir)
    98  	}
    99  	w := &spoolWorker{factory: newFactory(metricsSpoolDir)}
   100  	w.tomb.Go(func() error {
   101  		<-w.tomb.Dying()
   102  		return nil
   103  	})
   104  	return w, nil
   105  }
   106  
   107  // outputFunc extracts the metrics spool directory path from a *metricsSpoolDirWorker.
   108  func outputFunc(in worker.Worker, out interface{}) error {
   109  	inWorker, _ := in.(*spoolWorker)
   110  	outPointer, _ := out.(*MetricFactory)
   111  	if inWorker == nil || outPointer == nil {
   112  		return errors.Errorf("expected %T->%T; got %T->%T", inWorker, outPointer, in, out)
   113  	}
   114  	*outPointer = inWorker.factory
   115  	return nil
   116  }
   117  
   118  // spoolWorker is a worker that provides a MetricFactory.
   119  type spoolWorker struct {
   120  	tomb    tomb.Tomb
   121  	factory MetricFactory
   122  }
   123  
   124  // Kill is part of the worker.Worker interface.
   125  func (w *spoolWorker) Kill() {
   126  	w.tomb.Kill(nil)
   127  }
   128  
   129  // Wait is part of the worker.Worker interface.
   130  func (w *spoolWorker) Wait() error {
   131  	return w.tomb.Wait()
   132  }