github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"path"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  
    14  	"github.com/juju/juju/api/agent/metricsadder"
    15  	csender "github.com/juju/juju/common/sender"
    16  	"github.com/juju/juju/rpc/params"
    17  	"github.com/juju/juju/worker/metrics/spool"
    18  )
    19  
    20  type stopper interface {
    21  	Stop() error
    22  }
    23  
    24  type sender struct {
    25  	client   metricsadder.MetricsAdderClient
    26  	factory  spool.MetricFactory
    27  	listener stopper
    28  }
    29  
    30  // Do sends metrics from the metric spool to the
    31  // controller via an api call.
    32  func (s *sender) Do(stop <-chan struct{}) (err error) {
    33  	defer func() {
    34  		// See bug https://pad/lv/1733469
    35  		// If this function which is run by a PeriodicWorker
    36  		// exits with an error, we need to call stop() to
    37  		// ensure the sender socket is closed.
    38  		if err != nil {
    39  			s.stop()
    40  		}
    41  	}()
    42  
    43  	reader, err := s.factory.Reader()
    44  	if err != nil {
    45  		return errors.Trace(err)
    46  	}
    47  	defer reader.Close()
    48  	err = s.sendMetrics(reader)
    49  	if spool.IsMetricsDataError(err) {
    50  		logger.Debugf("cannot send metrics: %v", err)
    51  		return nil
    52  	}
    53  	return err
    54  }
    55  
    56  func (s *sender) sendMetrics(reader spool.MetricReader) error {
    57  	batches, err := reader.Read()
    58  	if err != nil {
    59  		return errors.Annotate(err, "failed to open the metric reader")
    60  	}
    61  	var sendBatches []params.MetricBatchParam
    62  	for _, batch := range batches {
    63  		sendBatches = append(sendBatches, spool.APIMetricBatch(batch))
    64  	}
    65  	results, err := s.client.AddMetricBatches(sendBatches)
    66  	if err != nil {
    67  		return errors.Annotate(err, "could not send metrics")
    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  		// controller 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.Errorf("could not remove batch %q from spool: %v", batchUUID, err)
    77  			}
    78  		} else {
    79  			logger.Errorf("failed to send batch %q: %v", batchUUID, resultErr)
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  // Handle sends metrics from the spool directory to the
    86  // controller.
    87  func (s *sender) Handle(c net.Conn, _ <-chan struct{}) (err error) {
    88  	defer func() {
    89  		if err != nil {
    90  			fmt.Fprintf(c, "%v\n", err)
    91  		} else {
    92  			fmt.Fprintf(c, "ok\n")
    93  		}
    94  		c.Close()
    95  	}()
    96  	// TODO(fwereade): 2016-03-17 lp:1558657
    97  	if err := c.SetDeadline(time.Now().Add(spool.DefaultTimeout)); err != nil {
    98  		return errors.Annotate(err, "failed to set the deadline")
    99  	}
   100  	reader, err := s.factory.Reader()
   101  	if err != nil {
   102  		return errors.Trace(err)
   103  	}
   104  	defer reader.Close()
   105  	return s.sendMetrics(reader)
   106  }
   107  
   108  func (s *sender) stop() {
   109  	if s.listener != nil {
   110  		_ = s.listener.Stop()
   111  	}
   112  }
   113  
   114  var socketName = func(baseDir, unitTag string) string {
   115  	return path.Join(baseDir, csender.DefaultMetricsSendSocketName)
   116  }
   117  
   118  func newSender(client metricsadder.MetricsAdderClient, factory spool.MetricFactory, baseDir, unitTag string) (*sender, error) {
   119  	s := &sender{
   120  		client:  client,
   121  		factory: factory,
   122  	}
   123  	listener, err := newListener(s, baseDir, unitTag)
   124  	if err != nil {
   125  		return nil, errors.Trace(err)
   126  	}
   127  	s.listener = listener
   128  	return s, nil
   129  }
   130  
   131  var newListener = func(s spool.ConnectionHandler, baseDir, unitTag string) (stopper, error) {
   132  	listener, err := spool.NewSocketListener(socketName(baseDir, unitTag), s)
   133  	if err != nil {
   134  		return nil, errors.Trace(err)
   135  	}
   136  	return listener, nil
   137  }