244 lines
5.6 KiB
Go
244 lines
5.6 KiB
Go
// Copyright (C) 2019 Marius Schellenberger
|
|
|
|
package main
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"reflect"
|
|
"regexp"
|
|
"time"
|
|
//corev1 "k8s.io/api/core/v1"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/rest"
|
|
)
|
|
|
|
var debug = false
|
|
|
|
const (
|
|
prAction = "X-Interceptor-Pr-Action"
|
|
pushRef = "X-Interceptor-Push-Ref"
|
|
commentHeader = "X-Interceptor-Comment"
|
|
commentAction = "X-Interceptor-Comment-Action"
|
|
labelHeader = "X-Interceptor-Label"
|
|
removeLabelsHeader = "X-Interceptor-Remove-Labels"
|
|
includeHeader = "X-Interceptor-Include-Repo"
|
|
excludeHeader = "X-Interceptor-Exclude-Repo"
|
|
|
|
githubEvent = "X-GitHub-Event"
|
|
giteaEvent = "X-Gitea-Event"
|
|
|
|
issueHeader = "Issue"
|
|
commitHeader = "Commit"
|
|
)
|
|
|
|
func init() {
|
|
log.SetFlags(log.Flags() | log.Lshortfile)
|
|
}
|
|
|
|
type Handler struct {
|
|
git *Git
|
|
}
|
|
|
|
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
var valid bool
|
|
event := r.Header.Get(githubEvent)
|
|
switch event {
|
|
case "pull_request":
|
|
valid = h.prHandler(w, r)
|
|
case "push":
|
|
valid = h.pushHandler(w, r)
|
|
case "issue_comment":
|
|
valid = h.commentHandler(w, r)
|
|
default:
|
|
h.dumpHandler(w, r)
|
|
}
|
|
status := int(reflect.Indirect(reflect.ValueOf(w)).FieldByName("status").Int())
|
|
log.Printf("%s %s %s event:%s valid:%t status:%d", r.RemoteAddr, r.Method, r.URL, event, valid, status)
|
|
}
|
|
|
|
func (h *Handler) prHandler(w http.ResponseWriter, r *http.Request) (valid bool) {
|
|
var prb PullRequestBody
|
|
body, err := readBody(r.Body, &prb)
|
|
if err != nil {
|
|
logError(w, err.Error())
|
|
return
|
|
}
|
|
if valid = h.git.validate(r, body, prb.Repository); !valid {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if contains(r.Header[prAction], prb.Action) {
|
|
if l := r.Header.Get(labelHeader); l != "" {
|
|
label, _ := getLabel(l)
|
|
var labels []string
|
|
for _, l := range prb.PullRequest.Labels {
|
|
labels = append(labels, l.Name)
|
|
}
|
|
|
|
if !contains(labels, label) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
}
|
|
h := w.Header()
|
|
h.Set(issueHeader, itoa(prb.Number))
|
|
h.Set(commitHeader, prb.PullRequest.Head.SHA)
|
|
w.Write(body)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
func (h *Handler) pushHandler(w http.ResponseWriter, r *http.Request) (valid bool) {
|
|
var pb PushBody
|
|
body, err := readBody(r.Body, &pb)
|
|
if err != nil {
|
|
logError(w, err.Error())
|
|
return
|
|
}
|
|
if valid = h.git.validate(r, body, pb.Repository); !valid {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
push := r.Header.Get(pushRef)
|
|
//if contains(r.Header[pushAction], pb.Ref) {
|
|
if push != "" && push == pb.Ref {
|
|
inc := r.Header[includeHeader]
|
|
exc := r.Header[excludeHeader]
|
|
if inc != nil && !contains(inc, pb.Repository.FullName) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
if exc != nil && contains(exc, pb.Repository.FullName) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
h := w.Header()
|
|
h.Set(commitHeader, pb.After)
|
|
w.Write(body)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
func (h *Handler) commentHandler(w http.ResponseWriter, r *http.Request) (valid bool) {
|
|
var cb CommentBody
|
|
body, err := readBody(r.Body, &cb)
|
|
if err != nil {
|
|
logError(w, err.Error())
|
|
return
|
|
}
|
|
if valid = h.git.validate(r, body, cb.Repository); !valid {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if !contains(r.Header[commentAction], cb.Action) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
//if contains(r.Header[commentAction], cb.Comment.Body) {
|
|
comment := r.Header.Get(commentHeader)
|
|
if comment == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
m, err := regexp.MatchString("(?m)^"+comment+"$", cb.Comment.Body)
|
|
if err != nil {
|
|
logError(w, err.Error())
|
|
return
|
|
}
|
|
if m {
|
|
issue := itoa(cb.Issue.Number)
|
|
api := detectAPI(r)
|
|
if api == "" {
|
|
logError(w, "unable to detect git provider API")
|
|
return
|
|
}
|
|
if l := r.Header.Get(labelHeader); l != "" {
|
|
label, color := getLabel(l)
|
|
var labels []string
|
|
for _, l := range cb.Issue.Labels {
|
|
labels = append(labels, l.Name)
|
|
}
|
|
if !contains(labels, label) {
|
|
err := h.git.setLabel(cb.Repository, api, issue, label, color, r.Header[removeLabelsHeader])
|
|
if err != nil {
|
|
logError(w, err.Error())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
pr, err := h.git.getPullRequest(cb.Repository, api, issue)
|
|
if err != nil {
|
|
logError(w, err.Error())
|
|
return
|
|
}
|
|
|
|
h := w.Header()
|
|
h.Set(issueHeader, issue)
|
|
h.Set(commitHeader, pr.Head.SHA)
|
|
w.Write(body)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
func (h *Handler) dumpHandler(w http.ResponseWriter, r *http.Request) {
|
|
defer r.Body.Close()
|
|
if debug {
|
|
body, err := ioutil.ReadAll(r.Body)
|
|
if err != nil {
|
|
logError(w, err.Error())
|
|
return
|
|
}
|
|
log.Printf("debug headers: %#v", r.Header)
|
|
log.Printf("debug: %s", string(body))
|
|
}
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
}
|
|
|
|
func main() {
|
|
cfg, err := rest.InClusterConfig()
|
|
if err != nil {
|
|
log.Fatalf("error configuring kube client: %s\n", err)
|
|
}
|
|
cl, err := kubernetes.NewForConfig(cfg)
|
|
if err != nil {
|
|
log.Fatalf("error creating new kube client: %s\n", err)
|
|
}
|
|
ns, err := getNS()
|
|
if err != nil {
|
|
log.Fatalf("error getting current namespace %s\n", err)
|
|
}
|
|
h := &Handler{
|
|
&Git{
|
|
sec: cl.CoreV1().Secrets(ns),
|
|
cl: &http.Client{Transport: &http.Transport{
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 30 * time.Second,
|
|
}).DialContext,
|
|
}},
|
|
},
|
|
}
|
|
|
|
http.Handle("/", h)
|
|
s := &http.Server{
|
|
ReadTimeout: 5 * time.Second,
|
|
WriteTimeout: 10 * time.Second,
|
|
IdleTimeout: 120 * time.Second,
|
|
Addr: ":8080",
|
|
}
|
|
log.Println("started webhook listener")
|
|
log.Fatal(s.ListenAndServe())
|
|
}
|