|
| 1 | +/* |
| 2 | +Copyright 2015 The Kubernetes Authors All rights reserved. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +package main |
| 18 | + |
| 19 | +import ( |
| 20 | + "bufio" |
| 21 | + "bytes" |
| 22 | + "encoding/json" |
| 23 | + "fmt" |
| 24 | + "strings" |
| 25 | + |
| 26 | + // TODO: move this somewhere central |
| 27 | + "k8s.io/contrib/submit-queue/jenkins" |
| 28 | + "k8s.io/kubernetes/pkg/util/sets" |
| 29 | +) |
| 30 | + |
| 31 | +// LatencyData represents the latency data for a set of RESTful API calls |
| 32 | +type LatencyData struct { |
| 33 | + APICalls []APICallLatency `json:"apicalls"` |
| 34 | +} |
| 35 | + |
| 36 | +// APICallLatency represents the latency data for a (resource, verb) tuple |
| 37 | +type APICallLatency struct { |
| 38 | + Latency Histogram `json:"latency"` |
| 39 | + Resource string `json:"resource"` |
| 40 | + Verb string `json:"verb"` |
| 41 | +} |
| 42 | + |
| 43 | +// Histogram is a map from bucket to latency (e.g. "Perc90" -> 23.5) |
| 44 | +type Histogram map[string]float64 |
| 45 | + |
| 46 | +type resourceToHistogram map[string][]APICallLatency |
| 47 | + |
| 48 | +var buildLatency = map[int]resourceToHistogram{} |
| 49 | + |
| 50 | +func main() { |
| 51 | + job := "kubernetes-e2e-gce-scalability" |
| 52 | + client := jenkins.JenkinsClient{ |
| 53 | + Host: "http://kubekins.dls.corp.google.com", |
| 54 | + } |
| 55 | + |
| 56 | + queue, err := client.GetJob(job) |
| 57 | + if err != nil { |
| 58 | + fmt.Printf("%v\n", err) |
| 59 | + return |
| 60 | + } |
| 61 | + resources := sets.NewString() |
| 62 | + methods := sets.NewString() |
| 63 | + for ix := range queue.Builds { |
| 64 | + build := queue.Builds[ix] |
| 65 | + reader, err := client.GetConsoleLog(job, build.Number) |
| 66 | + if err != nil { |
| 67 | + fmt.Printf("error getting logs: %v", err) |
| 68 | + continue |
| 69 | + } |
| 70 | + defer reader.Close() |
| 71 | + scanner := bufio.NewScanner(reader) |
| 72 | + buff := &bytes.Buffer{} |
| 73 | + inLatency := false |
| 74 | + |
| 75 | + hist := resourceToHistogram{} |
| 76 | + found := false |
| 77 | + for scanner.Scan() { |
| 78 | + line := scanner.Text() |
| 79 | + // TODO: This is brittle, we should emit a tail delimiter too |
| 80 | + if strings.Contains(line, "INFO") || strings.Contains(line, "STEP") || strings.Contains(line, "Failure") { |
| 81 | + if inLatency { |
| 82 | + obj := LatencyData{} |
| 83 | + if err := json.Unmarshal(buff.Bytes(), &obj); err != nil { |
| 84 | + fmt.Printf("error parsing JSON in build %d: %v %s\n", build.Number, err, buff.String()) |
| 85 | + // reset state and try again with more input |
| 86 | + inLatency = false |
| 87 | + continue |
| 88 | + } |
| 89 | + |
| 90 | + for _, call := range obj.APICalls { |
| 91 | + list := hist[call.Resource] |
| 92 | + list = append(list, call) |
| 93 | + hist[call.Resource] = list |
| 94 | + resources.Insert(call.Resource) |
| 95 | + methods.Insert(call.Verb) |
| 96 | + } |
| 97 | + |
| 98 | + buff.Reset() |
| 99 | + } |
| 100 | + inLatency = false |
| 101 | + } |
| 102 | + if strings.Contains(line, "API calls latencies") { |
| 103 | + found = true |
| 104 | + inLatency = true |
| 105 | + ix = strings.Index(line, "{") |
| 106 | + line = line[ix:] |
| 107 | + } |
| 108 | + if inLatency { |
| 109 | + buff.WriteString(line + " ") |
| 110 | + } |
| 111 | + } |
| 112 | + if !found { |
| 113 | + continue |
| 114 | + } |
| 115 | + |
| 116 | + buildLatency[build.Number] = hist |
| 117 | + } |
| 118 | + |
| 119 | + header := []string{"build"} |
| 120 | + for _, rsrc := range resources.List() { |
| 121 | + header = append(header, fmt.Sprintf("%s_50", rsrc)) |
| 122 | + header = append(header, fmt.Sprintf("%s_90", rsrc)) |
| 123 | + header = append(header, fmt.Sprintf("%s_99", rsrc)) |
| 124 | + } |
| 125 | + fmt.Println(strings.Join(header, ",")) |
| 126 | + |
| 127 | + for _, method := range methods.List() { |
| 128 | + fmt.Println(method) |
| 129 | + for build, data := range buildLatency { |
| 130 | + line := []string{fmt.Sprintf("%d", build)} |
| 131 | + for _, rsrc := range resources.List() { |
| 132 | + podData := data[rsrc] |
| 133 | + line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc50", podData))) |
| 134 | + line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc90", podData))) |
| 135 | + line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc99", podData))) |
| 136 | + } |
| 137 | + fmt.Println(strings.Join(line, ",")) |
| 138 | + } |
| 139 | + } |
| 140 | +} |
| 141 | + |
| 142 | +func findMethod(method, item string, data []APICallLatency) float64 { |
| 143 | + for _, datum := range data { |
| 144 | + if datum.Verb == method { |
| 145 | + return datum.Latency[item] |
| 146 | + } |
| 147 | + } |
| 148 | + return -1 |
| 149 | +} |
0 commit comments