github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/prometheus/response.go (about) 1 // Copyright (c) 2020 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package prometheus 22 23 import ( 24 "encoding/json" 25 "fmt" 26 "math" 27 "sort" 28 "strconv" 29 "strings" 30 ) 31 32 // Response represents Prometheus's query response. 33 type Response struct { 34 // Status is the response status. 35 Status string `json:"status"` 36 // Data is the response data. 37 Data data `json:"data"` 38 } 39 40 type data struct { 41 // ResultType is the type of Result (matrix, vector, etc.). 42 ResultType string 43 // Result contains the query result (concrete type depends on ResultType). 44 Result result 45 } 46 47 type result interface { 48 matches(other result) (MatchInformation, error) 49 } 50 51 // MatrixResult contains a list matrixRow. 52 type MatrixResult struct { 53 Result []matrixRow `json:"result"` 54 } 55 56 // VectorResult contains a list of vectorItem. 57 type VectorResult struct { 58 Result []vectorItem `json:"result"` 59 } 60 61 // ScalarResult is the scalar Value for the response. 62 type ScalarResult struct { 63 Result Value `json:"result"` 64 } 65 66 // StringResult is the string Value for the response. 67 type StringResult struct { 68 Result Value `json:"result"` 69 } 70 71 // UnmarshalJSON unmarshals the data struct of query response. 72 func (d *data) UnmarshalJSON(bytes []byte) error { 73 var discriminator struct { 74 ResultType string `json:"resultType"` 75 } 76 if err := json.Unmarshal(bytes, &discriminator); err != nil { 77 return err 78 } 79 *d = data{ResultType: discriminator.ResultType} 80 81 switch discriminator.ResultType { 82 83 case "matrix": 84 d.Result = &MatrixResult{} 85 86 case "vector": 87 d.Result = &VectorResult{} 88 89 case "scalar": 90 d.Result = &ScalarResult{} 91 92 case "string": 93 d.Result = &StringResult{} 94 95 default: 96 return fmt.Errorf("unknown resultType: %s", discriminator.ResultType) 97 } 98 99 return json.Unmarshal(bytes, d.Result) 100 } 101 102 // Len is the number of elements in the collection. 103 func (r MatrixResult) Len() int { return len(r.Result) } 104 105 // Less reports whether the element with 106 // index i should sort before the element with index j. 107 func (r MatrixResult) Less(i, j int) bool { 108 return r.Result[i].id < r.Result[j].id 109 } 110 111 // Swap swaps the elements with indexes i and j. 112 func (r MatrixResult) Swap(i, j int) { r.Result[i], r.Result[j] = r.Result[j], r.Result[i] } 113 114 // Sort sorts the MatrixResult. 115 func (r MatrixResult) Sort() { 116 for i, result := range r.Result { 117 r.Result[i].id = result.Metric.genID() 118 } 119 120 sort.Sort(r) 121 } 122 123 // Len is the number of elements in the vector. 124 func (r VectorResult) Len() int { return len(r.Result) } 125 126 // Less reports whether the element with 127 // index i should sort before the element with index j. 128 func (r VectorResult) Less(i, j int) bool { 129 return r.Result[i].id < r.Result[j].id 130 } 131 132 // Swap swaps the elements with indexes i and j. 133 func (r VectorResult) Swap(i, j int) { r.Result[i], r.Result[j] = r.Result[j], r.Result[i] } 134 135 // Sort sorts the VectorResult. 136 func (r VectorResult) Sort() { 137 for i, result := range r.Result { 138 r.Result[i].id = result.Metric.genID() 139 } 140 141 sort.Sort(r) 142 } 143 144 // matrixRow is a single row of "matrix" Result. 145 type matrixRow struct { 146 // Metric is the tags for the matrixRow. 147 Metric Tags `json:"metric"` 148 // Values is the set of values for the matrixRow. 149 Values Values `json:"values"` 150 id string 151 } 152 153 // vectorItem is a single item of "vector" Result. 154 type vectorItem struct { 155 // Metric is the tags for the vectorItem. 156 Metric Tags `json:"metric"` 157 // Value is the value for the vectorItem. 158 Value Value `json:"value"` 159 id string 160 } 161 162 // Tags is a simple representation of Prometheus tags. 163 type Tags map[string]string 164 165 // Values is a list of values for the Prometheus Result. 166 type Values []Value 167 168 // Value is a single value for Prometheus Result. 169 type Value []interface{} 170 171 func (t *Tags) genID() string { 172 tags := make(sort.StringSlice, len(*t)) 173 for k, v := range *t { 174 tags = append(tags, fmt.Sprintf("%s:%s,", k, v)) 175 } 176 177 sort.Sort(tags) 178 var sb strings.Builder 179 // NB: this may clash but exact tag values are also checked, and this is a 180 // validation endpoint so there's less concern over correctness. 181 for _, t := range tags { 182 sb.WriteString(t) 183 } 184 185 return sb.String() 186 } 187 188 // MatchInformation describes how well two responses match. 189 type MatchInformation struct { 190 // FullMatch indicates a full match. 191 FullMatch bool 192 // NoMatch indicates that the responses do not match sufficiently. 193 NoMatch bool 194 } 195 196 // Matches compares two responses and determines how closely they match. 197 func (r Response) Matches(other Response) (MatchInformation, error) { 198 if r.Status != other.Status { 199 err := fmt.Errorf("status %s does not match other status %s", 200 r.Status, other.Status) 201 return MatchInformation{ 202 NoMatch: true, 203 }, err 204 } 205 206 if r.Status == "error" { 207 return MatchInformation{ 208 FullMatch: true, 209 }, nil 210 } 211 212 return r.Data.matches(other.Data) 213 } 214 215 func (d data) matches(other data) (MatchInformation, error) { 216 if d.ResultType != other.ResultType { 217 err := fmt.Errorf("result type %s does not match other result type %s", 218 d.ResultType, other.ResultType) 219 return MatchInformation{ 220 NoMatch: true, 221 }, err 222 } 223 224 return d.Result.matches(other.Result) 225 } 226 227 func (r MatrixResult) matches(other result) (MatchInformation, error) { 228 otherMatrix, ok := other.(*MatrixResult) 229 if !ok { 230 err := fmt.Errorf("incorrect type for matching, expected MatrixResult, %v", other) 231 return MatchInformation{ 232 NoMatch: true, 233 }, err 234 } 235 236 if len(r.Result) != len(otherMatrix.Result) { 237 err := fmt.Errorf("result length %d does not match other result length %d", 238 len(r.Result), len(otherMatrix.Result)) 239 return MatchInformation{ 240 NoMatch: true, 241 }, err 242 } 243 244 r.Sort() 245 otherMatrix.Sort() 246 for i, result := range r.Result { 247 if err := result.matches(otherMatrix.Result[i]); err != nil { 248 return MatchInformation{ 249 NoMatch: true, 250 }, err 251 } 252 } 253 254 return MatchInformation{FullMatch: true}, nil 255 } 256 257 func (r VectorResult) matches(other result) (MatchInformation, error) { 258 otherVector, ok := other.(*VectorResult) 259 if !ok { 260 err := fmt.Errorf("incorrect type for matching, expected VectorResult") 261 return MatchInformation{ 262 NoMatch: true, 263 }, err 264 } 265 266 if len(r.Result) != len(otherVector.Result) { 267 err := fmt.Errorf("result length %d does not match other result length %d", 268 len(r.Result), len(otherVector.Result)) 269 return MatchInformation{ 270 NoMatch: true, 271 }, err 272 } 273 274 r.Sort() 275 otherVector.Sort() 276 for i, result := range r.Result { 277 if err := result.matches(otherVector.Result[i]); err != nil { 278 return MatchInformation{ 279 NoMatch: true, 280 }, err 281 } 282 } 283 284 return MatchInformation{FullMatch: true}, nil 285 } 286 287 func (r ScalarResult) matches(other result) (MatchInformation, error) { 288 otherScalar, ok := other.(*ScalarResult) 289 if !ok { 290 err := fmt.Errorf("incorrect type for matching, expected ScalarResult") 291 return MatchInformation{ 292 NoMatch: true, 293 }, err 294 } 295 296 if err := r.Result.matches(otherScalar.Result); err != nil { 297 return MatchInformation{ 298 NoMatch: true, 299 }, err 300 } 301 302 return MatchInformation{FullMatch: true}, nil 303 } 304 305 func (r StringResult) matches(other result) (MatchInformation, error) { 306 otherString, ok := other.(*StringResult) 307 if !ok { 308 err := fmt.Errorf("incorrect type for matching, expected StringResult") 309 return MatchInformation{ 310 NoMatch: true, 311 }, err 312 } 313 314 if err := r.Result.matches(otherString.Result); err != nil { 315 return MatchInformation{ 316 NoMatch: true, 317 }, err 318 } 319 320 return MatchInformation{FullMatch: true}, nil 321 } 322 323 func (r matrixRow) matches(other matrixRow) error { 324 // NB: tags should match by here so this is more of a sanity check. 325 if err := r.Metric.matches(other.Metric); err != nil { 326 return err 327 } 328 329 return r.Values.matches(other.Values) 330 } 331 332 func (r vectorItem) matches(other vectorItem) error { 333 // NB: tags should match by here so this is more of a sanity check. 334 if err := r.Metric.matches(other.Metric); err != nil { 335 return err 336 } 337 338 return r.Value.matches(other.Value) 339 } 340 341 func (t Tags) matches(other Tags) error { 342 if len(t) != len(other) { 343 return fmt.Errorf("tag length %d does not match other tag length %d", 344 len(t), len(other)) 345 } 346 347 for k, v := range t { 348 if vv, ok := other[k]; ok { 349 if v != vv { 350 return fmt.Errorf("tag %s value %s does not match other tag value %s", k, v, vv) 351 } 352 } else { 353 return fmt.Errorf("tag %s not found in other tagset", v) 354 } 355 } 356 357 return nil 358 } 359 360 func (v Values) matches(other Values) error { 361 if len(v) != len(other) { 362 return fmt.Errorf("values length %d does not match other values length %d", 363 len(v), len(other)) 364 } 365 366 for i, val := range v { 367 if err := val.matches(other[i]); err != nil { 368 return err 369 } 370 } 371 372 return nil 373 } 374 375 func (v Value) matches(other Value) error { 376 if len(v) != 2 { 377 return fmt.Errorf("value length %d must be 2", len(v)) 378 } 379 380 if len(other) != 2 { 381 return fmt.Errorf("other value length %d must be 2", len(other)) 382 } 383 384 tsV := fmt.Sprint(v[0]) 385 tsOther := fmt.Sprint(other[0]) 386 if tsV != tsOther { 387 return fmt.Errorf("ts %s does not match other ts %s", tsV, tsOther) 388 } 389 390 valV, err := strconv.ParseFloat(fmt.Sprint(v[1]), 64) 391 if err != nil { 392 return err 393 } 394 395 valOther, err := strconv.ParseFloat(fmt.Sprint(other[1]), 64) 396 if err != nil { 397 return err 398 } 399 400 if math.Abs(valV-valOther) > tolerance { 401 return fmt.Errorf("point %f does not match other point %f", valV, valOther) 402 } 403 404 return nil 405 }