github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/metrics/sender/sender.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package sender contains the implementation of the metric
     5  // sender manifold.
     6  package sender
     7  
     8  import (
     9  	"fmt"
    10  	"net"
    11  	"path"
    12  	"runtime"
    13  	"time"
    14  
    15  	"github.com/juju/errors"
    16  
    17  	"github.com/juju/juju/api/metricsadder"
    18  	"github.com/juju/juju/apiserver/params"
    19  	"github.com/juju/juju/worker/metrics/spool"
    20  )
    21  
    22  const (
    23  	defaultSocketName = "metrics-send.socket"
    24  )
    25  
    26  type stopper interface {
    27  	Stop() error
    28  }
    29  
    30  type sender struct {
    31  	client   metricsadder.MetricsAdderClient
    32  	factory  spool.MetricFactory
    33  	listener stopper
    34  }
    35  
    36  // Do sends metrics from the metric spool to the
    37  // controller via an api call.
    38  func (s *sender) Do(stop <-chan struct{}) error {
    39  	reader, err := s.factory.Reader()
    40  	if err != nil {
    41  		return errors.Trace(err)
    42  	}
    43  	defer reader.Close()
    44  	return s.sendMetrics(reader)
    45  }
    46  
    47  func (s *sender) sendMetrics(reader spool.MetricReader) error {
    48  	batches, err := reader.Read()
    49  	if err != nil {
    50  		return errors.Annotate(err, "failed to open the metric reader")
    51  	}
    52  	var sendBatches []params.MetricBatchParam
    53  	for _, batch := range batches {
    54  		sendBatches = append(sendBatches, spool.APIMetricBatch(batch))
    55  	}
    56  	results, err := s.client.AddMetricBatches(sendBatches)
    57  	if err != nil {
    58  		return errors.Annotate(err, "could not send metrics")
    59  	}
    60  	for batchUUID, resultErr := range results {
    61  		// if we fail to send any metric batch we log a warning with the assumption that
    62  		// the unsent metric batches remain in the spool directory and will be sent to the
    63  		// controller when the network partition is restored.
    64  		if _, ok := resultErr.(*params.Error); ok || params.IsCodeAlreadyExists(resultErr) {
    65  			err := reader.Remove(batchUUID)
    66  			if err != nil {
    67  				logger.Errorf("could not remove batch %q from spool: %v", batchUUID, err)
    68  			}
    69  		} else {
    70  			logger.Errorf("failed to send batch %q: %v", batchUUID, resultErr)
    71  		}
    72  	}
    73  	return nil
    74  }
    75  
    76  // Handle sends metrics from the spool directory to the
    77  // controller.
    78  func (s *sender) Handle(c net.Conn, _ <-chan struct{}) (err error) {
    79  	defer func() {
    80  		if err != nil {
    81  			fmt.Fprintf(c, "%v\n", err)
    82  		} else {
    83  			fmt.Fprintf(c, "ok\n")
    84  		}
    85  		c.Close()
    86  	}()
    87  	// TODO(fwereade): 2016-03-17 lp:1558657
    88  	if err := c.SetDeadline(time.Now().Add(spool.DefaultTimeout)); err != nil {
    89  		return errors.Annotate(err, "failed to set the deadline")
    90  	}
    91  	reader, err := s.factory.Reader()
    92  	if err != nil {
    93  		return errors.Trace(err)
    94  	}
    95  	defer reader.Close()
    96  	return s.sendMetrics(reader)
    97  }
    98  
    99  func (s *sender) stop() {
   100  	if s.listener != nil {
   101  		s.listener.Stop()
   102  	}
   103  }
   104  
   105  var socketName = func(baseDir, unitTag string) string {
   106  	switch runtime.GOOS {
   107  	case "windows":
   108  		return fmt.Sprintf(`\\.\pipe\send-metrics-%s`, unitTag)
   109  	default:
   110  		return path.Join(baseDir, defaultSocketName)
   111  	}
   112  }
   113  
   114  func newSender(client metricsadder.MetricsAdderClient, factory spool.MetricFactory, baseDir, unitTag string) (*sender, error) {
   115  	s := &sender{
   116  		client:  client,
   117  		factory: factory,
   118  	}
   119  	listener, err := spool.NewSocketListener(socketName(baseDir, unitTag), s)
   120  	if err != nil {
   121  		return nil, errors.Trace(err)
   122  	}
   123  	s.listener = listener
   124  	return s, nil
   125  }