@@ -3,17 +3,27 @@ package multitenant
3
3
// Collect reports from probes per-tenant, and supply them to queriers on demand
4
4
5
5
import (
6
+ "fmt"
7
+ "io"
8
+ "net"
9
+ "net/http"
10
+ "strconv"
6
11
"sync"
7
12
8
13
"context"
9
14
15
+ "github.com/opentracing-contrib/go-stdlib/nethttp"
16
+ opentracing "github.com/opentracing/opentracing-go"
17
+ log "github.com/sirupsen/logrus"
18
+ "github.com/weaveworks/common/user"
10
19
"github.com/weaveworks/scope/report"
11
20
)
12
21
13
22
// if StoreInterval is set, reports are merged into here and held until flushed to store
14
23
type pendingEntry struct {
15
24
sync.Mutex
16
25
report * report.Report
26
+ older []* report.Report
17
27
}
18
28
19
29
// We are building up a report in memory; merge into that and it will be saved shortly
@@ -32,3 +42,93 @@ func (c *awsCollector) addToLive(ctx context.Context, userid string, rep report.
32
42
entry .Unlock ()
33
43
}
34
44
45
+ func (c * awsCollector ) reportsFromLive (ctx context.Context , userid string ) ([]report.Report , error ) {
46
+ span , ctx := opentracing .StartSpanFromContext (ctx , "reportsFromLive" )
47
+ defer span .Finish ()
48
+ if c .cfg .StoreInterval != 0 {
49
+ // We are a collector
50
+ e , found := c .pending .Load (userid )
51
+ if ! found {
52
+ return nil , nil
53
+ }
54
+ entry := e .(* pendingEntry )
55
+ entry .Lock ()
56
+ ret := make ([]report.Report , 0 , len (entry .older )+ 1 )
57
+ if entry .report != nil {
58
+ ret = append (ret , entry .report .Copy ()) // Copy contents because this report is being unsafe-merged to
59
+ }
60
+ for _ , v := range entry .older {
61
+ if v != nil {
62
+ ret = append (ret , * v ) // no copy because older reports are immutable
63
+ }
64
+ }
65
+ entry .Unlock ()
66
+ return ret , nil
67
+ }
68
+
69
+ // We are a querier: fetch the most up-to-date reports from collectors
70
+ // TODO: resolve c.collectorAddress periodically instead of every time we make a call
71
+ addrs := resolve (c .cfg .CollectorAddr )
72
+ ret := make ([]report.Report , 0 , len (addrs ))
73
+ // make a call to each collector and fetch its data for this userid
74
+ // TODO: do them in parallel
75
+ for _ , addr := range addrs {
76
+ body , err := oneCall (ctx , addr , "/api/report" , userid )
77
+ if err != nil {
78
+ log .Warnf ("error calling '%s': %v" , addr , err )
79
+ continue
80
+ }
81
+ rpt , err := report .MakeFromBinary (ctx , body , false , true )
82
+ body .Close ()
83
+ if err != nil {
84
+ log .Warnf ("error decoding: %v" , err )
85
+ continue
86
+ }
87
+ ret = append (ret , * rpt )
88
+ }
89
+
90
+ return ret , nil
91
+ }
92
+
93
+ func resolve (name string ) []string {
94
+ _ , addrs , err := net .LookupSRV ("" , "" , name )
95
+ if err != nil {
96
+ log .Warnf ("Cannot resolve '%s': %v" , name , err )
97
+ return []string {}
98
+ }
99
+ endpoints := make ([]string , 0 , len (addrs ))
100
+ for _ , addr := range addrs {
101
+ port := strconv .Itoa (int (addr .Port ))
102
+ endpoints = append (endpoints , net .JoinHostPort (addr .Target , port ))
103
+ }
104
+ return endpoints
105
+ }
106
+
107
+ func oneCall (ctx context.Context , endpoint , path , userid string ) (io.ReadCloser , error ) {
108
+ fullPath := "http://" + endpoint + path
109
+ req , err := http .NewRequest ("GET" , fullPath , nil )
110
+ if err != nil {
111
+ return nil , fmt .Errorf ("error making request %s: %w" , fullPath , err )
112
+ }
113
+ req = req .WithContext (ctx )
114
+ req .Header .Set (user .OrgIDHeaderName , userid )
115
+ req .Header .Set ("Accept" , "application/msgpack" )
116
+ req .Header .Set ("Accept-Encoding" , "identity" ) // disable compression
117
+ if parentSpan := opentracing .SpanFromContext (ctx ); parentSpan != nil {
118
+ var ht * nethttp.Tracer
119
+ req , ht = nethttp .TraceRequest (parentSpan .Tracer (), req , nethttp .OperationName ("Collector Fetch" ))
120
+ defer ht .Finish ()
121
+ }
122
+ client := & http.Client {Transport : & nethttp.Transport {}}
123
+ res , err := client .Do (req )
124
+ if err != nil {
125
+ return nil , fmt .Errorf ("error getting %s: %w" , fullPath , err )
126
+ }
127
+ if res .StatusCode != http .StatusOK {
128
+ content , _ := io .ReadAll (res .Body )
129
+ res .Body .Close ()
130
+ return nil , fmt .Errorf ("error from collector: %s (%s)" , res .Status , string (content ))
131
+ }
132
+
133
+ return res .Body , nil
134
+ }
0 commit comments