-
Notifications
You must be signed in to change notification settings - Fork 1
/
webserver.go
132 lines (110 loc) · 3.18 KB
/
webserver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"bytes"
"database/sql"
"fmt"
_ "github.com/lib/pq"
"net/http"
"os"
"strings"
"text/template"
)
type Concourse struct {
PgUsername, PgPassword, PgHost string
}
func (c Concourse) PipelineStatus(team, pipeline string) (string, error) {
var status string
connStr := fmt.Sprintf("postgres://%s:%s@%s/concourse?sslmode=disable", c.PgUsername, c.PgPassword, c.PgHost)
db, err := sql.Open("postgres", connStr)
if err != nil {
return status, err
}
defer db.Close()
cql := fmt.Sprintf(`SELECT b.status
FROM builds b
INNER JOIN pipelines p ON p.id = b.pipeline_id
INNER JOIN teams t ON t.id = b.team_id
WHERE t.name = '%s' AND p.name = '%s' AND b.completed = TRUE AND b.name != 'check'
ORDER BY end_time DESC NULLS LAST
LIMIT 1`, team, pipeline)
err = db.QueryRow(cql).Scan(&status)
return status, err
}
func main() {
port := os.Getenv("PORT")
concourse := Concourse{
PgUsername: os.Getenv("POSTGRES_USERNAME"),
PgPassword: os.Getenv("POSTGRES_PASSWORD"),
PgHost: os.Getenv("POSTGRES_HOST"),
}
handler := func(w http.ResponseWriter, r *http.Request) {
pathSegments := strings.Split(r.URL.Path, "/")
if len(pathSegments) != 3 {
w.WriteHeader(404)
return
}
status, err := concourse.PipelineStatus(pathSegments[1], pathSegments[2])
if err != nil {
status = "unknown"
}
w.Header().Set("Content-type", "image/svg+xml")
w.Header().Set("Cache-Control", "max-age=60")
fmt.Fprint(w, badge(status))
}
http.HandleFunc("/", handler)
fmt.Println("Running on http://localhost:" + port)
http.ListenAndServe(":"+port, nil)
}
func badge(status string) string {
var color string
switch status {
case "succeeded":
color = `#44cc11`
status = "passed"
case "failed":
color = `#e05d44`
case "aborted":
color = `#8f4b2d`
case "errored":
color = `#fe7d37`
default:
color = `#9f9f9f`
status = "unknown"
}
const svg = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="88" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="a">
<rect width="88" height="20" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#a)">
<path fill="#555" d="M0 0h37v20H0z"/>
<path fill="{{ .FillColor }}" d="M37 0h51v20H37z"/>
<path fill="url(#b)" d="M0 0h88v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
<text x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">build</text>
<text x="195" y="140" transform="scale(.1)" textLength="270">build</text>
<text x="615" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">{{ .Status }}</text>
<text x="615" y="140" transform="scale(.1)" textLength="410">{{ .Status }}</text>
</g>
</svg>`
tmpl, err := template.New("Badge").Parse(svg)
if err != nil {
panic(err)
}
buffer := &bytes.Buffer{}
err = tmpl.Execute(buffer, struct {
FillColor, Status string
}{
color,
status,
})
if err != nil {
panic(err)
}
return buffer.String()
}