-
Notifications
You must be signed in to change notification settings - Fork 10
/
fetcher.go
125 lines (106 loc) · 2.69 KB
/
fetcher.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
package main
import (
"flag"
"fmt"
"log"
"net/http"
"github.com/PuerkitoBio/goquery"
"github.com/hoisie/redis"
)
var redisUrl = flag.String("redis", "localhost:6379", "Redis URL <host ip>:<port>")
var serveAddr = flag.String("serve", ":80", "Interface address and port, <ip>:<port>")
var keyTimeout = flag.Int("cache-timeout", 300, "Time, in seconds, for key expiration")
var assetsDir = flag.String("assets", "/fetcher", "Dir which has assets stored in it")
var Redis redis.Client
func main() {
flag.Parse()
Redis = redis.Client{Addr: *redisUrl}
http.HandleFunc("/", handler)
http.ListenAndServe(*serveAddr, Log(http.DefaultServeMux))
}
func Log(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
handler.ServeHTTP(w, r)
})
}
func handler(w http.ResponseWriter, r *http.Request) {
repo := r.URL.Path[1:]
status, err := cache(repo)
if err != nil {
http.Error(w, fmt.Sprintf("%s", err), 500)
} else {
status = *assetsDir + "/" + status + ".svg"
w.Header().Set("Content-Type", "image/svg+xml")
http.ServeFile(w, r, status)
}
}
func GetBuildStatus(repo string) (string, error) {
hub_url := "https://registry.hub.docker.com"
url := fmt.Sprintf("%s/u/%s", hub_url, repo)
build_uri, err := getBuildUri(url)
if err != nil {
return "", err
}
url = fmt.Sprintf("%s/%s", hub_url, build_uri)
return getBuildStatus(url)
}
func cache(key string) (string, error) {
rkey := "hub_repo_status:" + key
val, err := Redis.Get(rkey)
if err != nil {
s, err := GetBuildStatus(key)
if err != nil {
return "", err
}
val = []byte(s)
go func() {
Redis.Set(rkey, val)
Redis.Expire(rkey, int64(*keyTimeout))
}()
return string(val), nil
}
return string(val), nil
}
func getBuildUri(src string) (string, error) {
var (
href string
err error
)
doc, err := goquery.NewDocument(src)
if err != nil {
return "", err
}
doc.Find("a:contains(\"Build Details\")").Each(func(i int, s *goquery.Selection) {
var exists bool
href, exists = s.Attr("href")
if !exists {
err = fmt.Errorf("Could not get build details")
}
})
return href, err
}
func getBuildStatus(src string) (string, error) {
doc, err := goquery.NewDocument(src)
if err != nil {
return "", err
}
cssPath := "#repo-info-tab > div.repository > table > tbody tr > td"
var status string
doc.Find(cssPath).Each(func(i int, s *goquery.Selection) {
txt := s.Text()
if txt == "Finished" || txt == "Error" {
if status == "" {
switch txt {
case "Finished":
status = "passing"
case "Error":
status = "failing"
default:
status = txt
}
}
}
})
return status, nil
}