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 }