github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/e2e/metrics/prometheus.go (about) 1 package metrics 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/url" 9 10 "github.com/hashicorp/nomad/e2e/e2eutil" 11 "github.com/hashicorp/nomad/e2e/framework" 12 "github.com/hashicorp/nomad/helper/uuid" 13 "github.com/prometheus/common/model" 14 ) 15 16 func (tc *MetricsTest) setUpPrometheus(f *framework.F) error { 17 uuid := uuid.Generate() 18 fabioID := "fabio" + uuid[0:8] 19 fabioAllocs := e2eutil.RegisterAndWaitForAllocs(f.T(), tc.Nomad(), 20 "fabio/fabio.nomad", fabioID, "") 21 if len(fabioAllocs) < 1 { 22 return fmt.Errorf("fabio failed to start") 23 } 24 tc.fabioID = fabioID 25 26 // get a fabio IP address so we can query it later 27 nodeDetails, _, err := tc.Nomad().Nodes().Info(fabioAllocs[0].NodeID, nil) 28 if err != nil { 29 return err 30 } 31 32 // TODO(tgross): currently this forces us to run the target on AWS rather 33 // than any other environment. There's a Provider environment in the E2E 34 // framework we're not currently using; we should revisit that. 35 publicIP := nodeDetails.Attributes["unique.platform.aws.public-ipv4"] 36 tc.fabioAddress = fmt.Sprintf("http://%s:9999", publicIP) 37 prometheusID := "prometheus" + uuid[0:8] 38 prometheusAllocs := e2eutil.RegisterAndWaitForAllocs(f.T(), tc.Nomad(), 39 "prometheus/prometheus.nomad", prometheusID, "") 40 if len(prometheusAllocs) < 1 { 41 return fmt.Errorf("prometheus failed to start") 42 } 43 tc.prometheusID = prometheusID 44 return nil 45 } 46 47 func (tc *MetricsTest) tearDownPrometheus(f *framework.F) { 48 tc.Nomad().Jobs().Deregister(tc.prometheusID, true, nil) 49 tc.Nomad().Jobs().Deregister(tc.fabioID, true, nil) 50 tc.Nomad().System().GarbageCollect() 51 } 52 53 // "Wait, why aren't we just using the prometheus golang client?", you ask? 54 // Nomad has vendored an older version of the prometheus exporter library 55 // their HTTP client which only works with a newer version is also is marked 56 // "alpha", and there's API v2 work currently ongoing. Rather than waiting 57 // till 0.11 to ship this test, this just handles the query API and can be 58 // swapped out later. 59 // 60 // TODO(tgross) / COMPAT(0.11): update our prometheus libraries 61 func (tc *MetricsTest) promQuery(query string) (model.Vector, error) { 62 var err error 63 promUrl := tc.fabioAddress + "/api/v1/query" 64 formValues := url.Values{"query": {query}} 65 resp, err := http.PostForm(promUrl, formValues) 66 if err != nil { 67 return nil, err 68 } 69 defer resp.Body.Close() 70 if resp.StatusCode != http.StatusOK { 71 return nil, fmt.Errorf("HTTP status: %v", resp.StatusCode) 72 } 73 body, err := ioutil.ReadAll(resp.Body) 74 if err != nil { 75 return nil, err 76 } 77 apiResp := &apiResponse{} 78 err = json.Unmarshal(body, apiResp) 79 if err != nil { 80 return nil, err 81 } 82 if apiResp.Status == "error" { 83 return nil, fmt.Errorf("API error: %v: %v", apiResp.ErrorType, apiResp.Error) 84 } 85 86 // unpack query 87 var qs queryResult 88 err = json.Unmarshal(apiResp.Data, &qs) 89 if err != nil { 90 return nil, err 91 } 92 val, ok := qs.v.(model.Vector) 93 if !ok || len(val) == 0 { 94 return nil, fmt.Errorf("no metrics data available") 95 } 96 return val, nil 97 } 98 99 type apiResponse struct { 100 Status string `json:"status"` 101 Data json.RawMessage `json:"data"` 102 ErrorType string `json:"errorType"` 103 Error string `json:"error"` 104 Warnings []string `json:"warnings,omitempty"` 105 } 106 107 // queryResult contains result data for a query. 108 type queryResult struct { 109 Type model.ValueType `json:"resultType"` 110 Result interface{} `json:"result"` 111 112 // The decoded value. 113 v model.Value 114 } 115 116 func (qr *queryResult) UnmarshalJSON(b []byte) error { 117 v := struct { 118 Type model.ValueType `json:"resultType"` 119 Result json.RawMessage `json:"result"` 120 }{} 121 122 err := json.Unmarshal(b, &v) 123 if err != nil { 124 return err 125 } 126 127 switch v.Type { 128 case model.ValScalar: 129 var sv model.Scalar 130 err = json.Unmarshal(v.Result, &sv) 131 qr.v = &sv 132 133 case model.ValVector: 134 var vv model.Vector 135 err = json.Unmarshal(v.Result, &vv) 136 qr.v = vv 137 138 case model.ValMatrix: 139 var mv model.Matrix 140 err = json.Unmarshal(v.Result, &mv) 141 qr.v = mv 142 143 default: 144 err = fmt.Errorf("no metrics data available") 145 } 146 return err 147 }