Skip to content
This repository was archived by the owner on Apr 17, 2019. It is now read-only.

Commit b0ac5dc

Browse files
author
Ryan Hitchman
committed
Add /health.svg endpoint to serve a small queue status badge.
This is inspired by shields.io, but is served directly. Future work: show overall health percentage somehow (pie chart? sparklines?).
1 parent c4221d4 commit b0ac5dc

File tree

5 files changed

+144
-2
lines changed

5 files changed

+144
-2
lines changed

hack/verify-flags/exceptions.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
ingress/controllers/nginx/README.md:Enables which HTTP codes should be passed for processing with the [error_page directive](http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page)
2-
ingress/controllers/nginx/README.md:Setting at least one code this also enables [proxy_intercept_errors](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors) (required to process error_page)
31
ingress/controllers/nginx/nginx.tmpl: require("error_page")
42
ingress/controllers/nginx/nginx.tmpl: error_page {{ $errCode }} = @custom_{{ $errCode }};{{ end }}
53
ingress/controllers/nginx/nginx/config/config.go: // enables which HTTP codes should be passed for processing with the error_page directive
64
mungegithub/mungers/submit-queue.go: sq.e2e = &fake_e2e.FakeE2ETester{
75
mungegithub/mungers/submit-queue.go: fake_e2e "k8s.io/contrib/mungegithub/mungers/e2e/fake"
6+
mungegithub/mungers/submit-queue_test.go: e2e := sq.e2e.(*fake_e2e.FakeE2ETester)
87
mungegithub/mungers/submit-queue_test.go: fake_e2e "k8s.io/contrib/mungegithub/mungers/e2e/fake"
98
mungegithub/mungers/submit-queue_test.go: sq.e2e = &fake_e2e.FakeE2ETester{

mungegithub/mungers/e2e/fake/fake.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
type FakeE2ETester struct {
2626
JobNames []string
2727
WeakStableJobNames []string
28+
NotStableJobNames []string
2829
}
2930

3031
// Flakes returns nil.
@@ -47,5 +48,8 @@ func (e *FakeE2ETester) GetBuildStatus() map[string]e2e.BuildInfo {
4748
for _, name := range e.WeakStableJobNames {
4849
out[name] = e2e.BuildInfo{"Stable", "1"}
4950
}
51+
for _, name := range e.NotStableJobNames {
52+
out[name] = e2e.BuildInfo{"Not Stable", "1"}
53+
}
5054
return out
5155
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
Copyright 2016 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 shield
18+
19+
// shield provides a local version of the shields.io service.
20+
21+
import (
22+
"bytes"
23+
"html/template"
24+
)
25+
26+
var svg = `<svg xmlns="http://www.w3.org/2000/svg" width="{{.Width}}" height="20">
27+
<linearGradient id="a" x2="0" y2="100%">
28+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
29+
<stop offset="1" stop-opacity=".1"/>
30+
</linearGradient>
31+
<rect rx="3" width="100%" height="20" fill="#555"/>
32+
<g fill="{{.Color}}">
33+
<rect rx="3" x="{{.RightStart}}" width="{{.RightWidth}}" height="20"/>
34+
<path d="M{{.RightStart}} 0h4v20h-4z"/>
35+
</g>
36+
<rect rx="3" width="100%" height="20" fill="url(#a)"/>
37+
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
38+
<g fill="#010101" opacity=".3">
39+
<text x="{{.XposLeft}}" y="15">{{.Subject}}</text>
40+
<text x="{{.XposRight}}" y="15">{{.Status}}</text>
41+
</g>
42+
<text x="{{.XposLeft}}" y="14">{{.Subject}}</text>
43+
<text x="{{.XposRight}}" y="14">{{.Status}}</text>
44+
</g>
45+
</svg>`
46+
47+
var svgTemplate = template.Must(template.New("svg").Parse(svg))
48+
49+
// Make a small SVG badge that looks like `[subject | status]`, with the status
50+
// text in the given color.
51+
func Make(subject, status, color string) []byte {
52+
// TODO(rmmh): Use better font-size metrics for prettier badges-- estimating
53+
// character widths as 6px isn't very accurate.
54+
// See also: https://github.com/badges/shields/blob/master/measure-text.js
55+
p := struct {
56+
Width, RightStart, RightWidth int
57+
XposLeft, XposRight float64
58+
Subject, Status string
59+
Color string
60+
}{
61+
Subject: subject,
62+
Status: status,
63+
RightStart: 13 + 6*len(subject),
64+
RightWidth: 13 + 6*len(status),
65+
}
66+
p.Width = p.RightStart + p.RightWidth
67+
p.XposLeft = float64(p.RightStart) * 0.5
68+
p.XposRight = float64(p.RightStart) + float64(p.RightWidth-2)*0.5
69+
switch color {
70+
case "brightgreen":
71+
p.Color = "#4c1"
72+
case "red":
73+
p.Color = "#e05d44"
74+
default:
75+
panic("Invalid color " + color)
76+
}
77+
var buf bytes.Buffer
78+
svgTemplate.Execute(&buf, p)
79+
return buf.Bytes()
80+
}

mungegithub/mungers/submit-queue.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"k8s.io/contrib/mungegithub/github"
3737
"k8s.io/contrib/mungegithub/mungers/e2e"
3838
fake_e2e "k8s.io/contrib/mungegithub/mungers/e2e/fake"
39+
"k8s.io/contrib/mungegithub/mungers/shield"
3940
"k8s.io/contrib/test-utils/utils"
4041

4142
"github.com/NYTimes/gziphandler"
@@ -393,6 +394,7 @@ func (sq *SubmitQueue) internalInitialize(config *github.Config, features *featu
393394
http.Handle("/merge-info", gziphandler.GzipHandler(http.HandlerFunc(sq.serveMergeInfo)))
394395
http.Handle("/priority-info", gziphandler.GzipHandler(http.HandlerFunc(sq.servePriorityInfo)))
395396
http.Handle("/health", gziphandler.GzipHandler(http.HandlerFunc(sq.serveHealth)))
397+
http.Handle("/health.svg", gziphandler.GzipHandler(http.HandlerFunc(sq.serveHealthSVG)))
396398
http.Handle("/sq-stats", gziphandler.GzipHandler(http.HandlerFunc(sq.serveSQStats)))
397399
http.Handle("/flakes", gziphandler.GzipHandler(http.HandlerFunc(sq.serveFlakes)))
398400
config.ServeDebugStats("/stats")
@@ -1269,6 +1271,40 @@ func (sq *SubmitQueue) servePriorityInfo(res http.ResponseWriter, req *http.Requ
12691271
</ol> `))
12701272
}
12711273

1274+
func (sq *SubmitQueue) getHealthSVG() []byte {
1275+
sq.Lock()
1276+
defer sq.Unlock()
1277+
blocked := false
1278+
blockingJobs := make([]string, 0)
1279+
blocked = !sq.health.MergePossibleNow
1280+
status := "running"
1281+
color := "brightgreen"
1282+
if blocked {
1283+
status = "blocked"
1284+
color = "red"
1285+
for job, status := range sq.e2e.GetBuildStatus() {
1286+
if status.Status == "Not Stable" {
1287+
job = strings.Replace(job, "kubernetes-", "", -1)
1288+
blockingJobs = append(blockingJobs, job)
1289+
}
1290+
}
1291+
sort.Strings(blockingJobs)
1292+
if len(blockingJobs) > 3 {
1293+
blockingJobs = append(blockingJobs[:3], "...")
1294+
}
1295+
if len(blockingJobs) > 0 {
1296+
status += " by " + strings.Join(blockingJobs, ", ")
1297+
}
1298+
}
1299+
return shield.Make("queue", status, color)
1300+
}
1301+
1302+
func (sq *SubmitQueue) serveHealthSVG(res http.ResponseWriter, req *http.Request) {
1303+
res.Header().Set("Content-type", "image/svg+xml")
1304+
res.WriteHeader(http.StatusOK)
1305+
res.Write(sq.getHealthSVG())
1306+
}
1307+
12721308
func (sq *SubmitQueue) isStaleComment(obj *github.MungeObject, comment githubapi.IssueComment) bool {
12731309
if !mergeBotComment(comment) {
12741310
return false

mungegithub/mungers/submit-queue_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,3 +1276,26 @@ func TestHealth(t *testing.T) {
12761276
t.Errorf("updateHealth didn't truncate old entries: %v", sq.healthHistory)
12771277
}
12781278
}
1279+
1280+
func TestHealthSVG(t *testing.T) {
1281+
sq := getTestSQ(false, nil, nil)
1282+
e2e := sq.e2e.(*fake_e2e.FakeE2ETester)
1283+
1284+
for _, state := range []struct {
1285+
mergePossible bool
1286+
expected string
1287+
notStable []string
1288+
}{
1289+
{true, "running", nil},
1290+
{false, "blocked</text>", nil},
1291+
{false, "blocked by kubemark-500", []string{"kubernetes-kubemark-500"}},
1292+
{false, "blocked by a, b, c, ...", []string{"a", "b", "c", "d"}},
1293+
} {
1294+
sq.health.MergePossibleNow = state.mergePossible
1295+
e2e.NotStableJobNames = state.notStable
1296+
res := string(sq.getHealthSVG())
1297+
if !strings.Contains(res, state.expected) {
1298+
t.Errorf("SVG doesn't contain `%s`: %v", state.expected, res)
1299+
}
1300+
}
1301+
}

0 commit comments

Comments
 (0)