github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/uniter/operation/metrics.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/juju/apiserver/params"
    11  	"github.com/juju/juju/worker/uniter/metrics"
    12  )
    13  
    14  // apiMetricSender is used to send metrics to the state server. Its default implementation is
    15  // *uniter.Unit.
    16  type apiMetricSender interface {
    17  	AddMetricBatches(batches []params.MetricBatch) (map[string]error, error)
    18  }
    19  
    20  // metricsReader is used to read metrics batches stored by the metrics recorder
    21  // and remove metrics batches that have been marked as succesfully sent.
    22  type metricsReader interface {
    23  	Read() ([]metrics.MetricBatch, error)
    24  	Remove(uuid string) error
    25  	Close() error
    26  }
    27  
    28  type sendMetrics struct {
    29  	DoesNotRequireMachineLock
    30  	spoolDir string
    31  	sender   apiMetricSender
    32  }
    33  
    34  // String implements the Operation interface.
    35  func (op *sendMetrics) String() string {
    36  	return fmt.Sprintf("sending metrics")
    37  }
    38  
    39  // Prepare implements the Operation interface.
    40  func (op *sendMetrics) Prepare(state State) (*State, error) {
    41  	return &state, nil
    42  }
    43  
    44  // Execute implements the Operation interface.
    45  // Execute will try to read any metric batches stored in the spool directory
    46  // and send them to the state server.
    47  func (op *sendMetrics) Execute(state State) (*State, error) {
    48  	reader, err := metrics.NewJSONMetricReader(op.spoolDir)
    49  	if err != nil {
    50  		logger.Warningf("failed to create a metric reader: %v", err)
    51  		return &state, nil
    52  	}
    53  
    54  	batches, err := reader.Read()
    55  	if err != nil {
    56  		logger.Warningf("failed to open the metric reader: %v", err)
    57  		return &state, nil
    58  	}
    59  	defer reader.Close()
    60  	var sendBatches []params.MetricBatch
    61  	for _, batch := range batches {
    62  		sendBatches = append(sendBatches, metrics.APIMetricBatch(batch))
    63  	}
    64  	results, err := op.sender.AddMetricBatches(sendBatches)
    65  	if err != nil {
    66  		logger.Warningf("could not send metrics: %v", err)
    67  		return &state, nil
    68  	}
    69  	for batchUUID, resultErr := range results {
    70  		// if we fail to send any metric batch we log a warning with the assumption that
    71  		// the unsent metric batches remain in the spool directory and will be sent to the
    72  		// state server when the network partition is restored.
    73  		if _, ok := resultErr.(*params.Error); ok || params.IsCodeAlreadyExists(resultErr) {
    74  			err = reader.Remove(batchUUID)
    75  			if err != nil {
    76  				logger.Warningf("could not remove batch %q from spool: %v", batchUUID, err)
    77  			}
    78  		} else {
    79  			logger.Warningf("failed to send batch %q: %v", batchUUID, resultErr)
    80  		}
    81  	}
    82  	return &state, nil
    83  }
    84  
    85  // Commit implements the Operation interface.
    86  func (op *sendMetrics) Commit(state State) (*State, error) {
    87  	state.SendMetricsTime = time.Now().Unix()
    88  	return &state, nil
    89  }