github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 DefaultMetricsSendSocketName = "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{}) (err error) { 39 defer func() { 40 // See bug https://pad/lv/1733469 41 // If this function which is run by a PeriodicWorker 42 // exits with an error, we need to call stop() to 43 // ensure the sender socket is closed. 44 if err != nil { 45 s.stop() 46 } 47 }() 48 49 reader, err := s.factory.Reader() 50 if err != nil { 51 return errors.Trace(err) 52 } 53 defer reader.Close() 54 err = s.sendMetrics(reader) 55 if spool.IsMetricsDataError(err) { 56 logger.Debugf("cannot send metrics: %v", err) 57 return nil 58 } 59 return err 60 } 61 62 func (s *sender) sendMetrics(reader spool.MetricReader) error { 63 batches, err := reader.Read() 64 if err != nil { 65 return errors.Annotate(err, "failed to open the metric reader") 66 } 67 var sendBatches []params.MetricBatchParam 68 for _, batch := range batches { 69 sendBatches = append(sendBatches, spool.APIMetricBatch(batch)) 70 } 71 results, err := s.client.AddMetricBatches(sendBatches) 72 if err != nil { 73 return errors.Annotate(err, "could not send metrics") 74 } 75 for batchUUID, resultErr := range results { 76 // if we fail to send any metric batch we log a warning with the assumption that 77 // the unsent metric batches remain in the spool directory and will be sent to the 78 // controller when the network partition is restored. 79 if _, ok := resultErr.(*params.Error); ok || params.IsCodeAlreadyExists(resultErr) { 80 err := reader.Remove(batchUUID) 81 if err != nil { 82 logger.Errorf("could not remove batch %q from spool: %v", batchUUID, err) 83 } 84 } else { 85 logger.Errorf("failed to send batch %q: %v", batchUUID, resultErr) 86 } 87 } 88 return nil 89 } 90 91 // Handle sends metrics from the spool directory to the 92 // controller. 93 func (s *sender) Handle(c net.Conn, _ <-chan struct{}) (err error) { 94 defer func() { 95 if err != nil { 96 fmt.Fprintf(c, "%v\n", err) 97 } else { 98 fmt.Fprintf(c, "ok\n") 99 } 100 c.Close() 101 }() 102 // TODO(fwereade): 2016-03-17 lp:1558657 103 if err := c.SetDeadline(time.Now().Add(spool.DefaultTimeout)); err != nil { 104 return errors.Annotate(err, "failed to set the deadline") 105 } 106 reader, err := s.factory.Reader() 107 if err != nil { 108 return errors.Trace(err) 109 } 110 defer reader.Close() 111 return s.sendMetrics(reader) 112 } 113 114 func (s *sender) stop() { 115 if s.listener != nil { 116 s.listener.Stop() 117 } 118 } 119 120 var socketName = func(baseDir, unitTag string) string { 121 switch runtime.GOOS { 122 case "windows": 123 return fmt.Sprintf(`\\.\pipe\send-metrics-%s`, unitTag) 124 default: 125 return path.Join(baseDir, DefaultMetricsSendSocketName) 126 } 127 } 128 129 func newSender(client metricsadder.MetricsAdderClient, factory spool.MetricFactory, baseDir, unitTag string) (*sender, error) { 130 s := &sender{ 131 client: client, 132 factory: factory, 133 } 134 listener, err := newListener(s, baseDir, unitTag) 135 if err != nil { 136 return nil, errors.Trace(err) 137 } 138 s.listener = listener 139 return s, nil 140 } 141 142 var newListener = func(s spool.ConnectionHandler, baseDir, unitTag string) (stopper, error) { 143 listener, err := spool.NewSocketListener(socketName(baseDir, unitTag), s) 144 if err != nil { 145 return nil, errors.Trace(err) 146 } 147 return listener, nil 148 }