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 }