github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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()
    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  		logger.Warningf("failed to open the metric reader: %v", err)
    51  		return errors.Trace(err)
    52  	}
    53  	var sendBatches []params.MetricBatchParam
    54  	for _, batch := range batches {
    55  		sendBatches = append(sendBatches, spool.APIMetricBatch(batch))
    56  	}
    57  	results, err := s.client.AddMetricBatches(sendBatches)
    58  	if err != nil {
    59  		logger.Warningf("could not send metrics: %v", err)
    60  		return errors.Trace(err)
    61  	}
    62  	for batchUUID, resultErr := range results {
    63  		// if we fail to send any metric batch we log a warning with the assumption that
    64  		// the unsent metric batches remain in the spool directory and will be sent to the
    65  		// controller when the network partition is restored.
    66  		if _, ok := resultErr.(*params.Error); ok || params.IsCodeAlreadyExists(resultErr) {
    67  			err = reader.Remove(batchUUID)
    68  			if err != nil {
    69  				logger.Warningf("could not remove batch %q from spool: %v", batchUUID, err)
    70  			}
    71  		} else {
    72  			logger.Warningf("failed to send batch %q: %v", batchUUID, resultErr)
    73  		}
    74  	}
    75  	return nil
    76  }
    77  
    78  // Handle sends metrics from the spool directory to the
    79  // controller.
    80  func (s *sender) Handle(c net.Conn) (err error) {
    81  	defer func() {
    82  		if err != nil {
    83  			fmt.Fprintf(c, "%v\n", err)
    84  		} else {
    85  			fmt.Fprintf(c, "ok\n")
    86  		}
    87  		c.Close()
    88  	}()
    89  	// TODO(fwereade): 2016-03-17 lp:1558657
    90  	err = c.SetDeadline(time.Now().Add(spool.DefaultTimeout))
    91  	if err != nil {
    92  		return errors.Annotate(err, "failed to set the deadline")
    93  	}
    94  	reader, err := s.factory.Reader()
    95  	if err != nil {
    96  		return errors.Trace(err)
    97  	}
    98  	defer reader.Close()
    99  	return s.sendMetrics(reader)
   100  }
   101  
   102  func (s *sender) stop() {
   103  	if s.listener != nil {
   104  		s.listener.Stop()
   105  	}
   106  }
   107  
   108  var socketName = func(baseDir, unitTag string) string {
   109  	switch runtime.GOOS {
   110  	case "windows":
   111  		return fmt.Sprintf(`\\.\pipe\send-metrics-%s`, unitTag)
   112  	default:
   113  		return path.Join(baseDir, defaultSocketName)
   114  	}
   115  }
   116  
   117  func newSender(client metricsadder.MetricsAdderClient, factory spool.MetricFactory, baseDir, unitTag string) (*sender, error) {
   118  	s := &sender{
   119  		client:  client,
   120  		factory: factory,
   121  	}
   122  	listener, err := spool.NewSocketListener(socketName(baseDir, unitTag), s)
   123  	if err != nil {
   124  		return nil, errors.Trace(err)
   125  	}
   126  	s.listener = listener
   127  	return s, nil
   128  }