first commit
This commit is contained in:
commit
ad18bb8ad3
9 changed files with 309 additions and 0 deletions
12
Dockerfile
Normal file
12
Dockerfile
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
FROM golang:alpine as builder
|
||||
RUN adduser -D -g '' appuser
|
||||
RUN mkdir /build
|
||||
ADD . /build/
|
||||
WORKDIR /build
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -gcflags 'all=-e' -ldflags "-s -w" -o main .
|
||||
FROM scratch
|
||||
COPY --from=builder /etc/passwd /etc/passwd
|
||||
COPY --from=builder /build/main /main
|
||||
USER appuser
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/main"]
|
||||
24
LICENSE
Normal file
24
LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Copyright (C) 2018 Marius Schellenberger
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The names of the authors and/or contributors may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
35
README.md
Normal file
35
README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# webhook-interseptor
|
||||
|
||||
The Webhook Interseptor checks for specific conditions when a webhook is received.
|
||||
|
||||
This way we can trigger cleanups when the PR is closed.
|
||||
|
||||
# HTTP Headers
|
||||
|
||||
## Push Event
|
||||
|
||||
```
|
||||
X-Interseptor-Ref: refs/heads/master
|
||||
```
|
||||
|
||||
## Pull-Request Event
|
||||
|
||||
```
|
||||
X-Interseptor-Action: [create|delete]
|
||||
```
|
||||
|
||||
## Issue Comment Event
|
||||
|
||||
**TBD**
|
||||
|
||||
```
|
||||
X-Interseptor-Comment: /test
|
||||
```
|
||||
|
||||
# Build and run
|
||||
|
||||
```
|
||||
export KO_DOCKER_REPO='gcr.io/my-gcloud-project-name'
|
||||
|
||||
ko apply -f config/
|
||||
```
|
||||
4
config/00-namespace.yaml
Normal file
4
config/00-namespace.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: webhook-interseptor
|
||||
26
config/01-podsecuritypolicy.yaml
Normal file
26
config/01-podsecuritypolicy.yaml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: policy/v1beta1
|
||||
kind: PodSecurityPolicy
|
||||
metadata:
|
||||
name: webhook-interseptor
|
||||
spec:
|
||||
privileged: false
|
||||
allowPrivilegeEscalation: false
|
||||
volumes:
|
||||
- 'secret'
|
||||
hostNetwork: false
|
||||
hostIPC: false
|
||||
hostPID: false
|
||||
runAsUser:
|
||||
rule: 'RunAsAny'
|
||||
seLinux:
|
||||
rule: 'RunAsAny'
|
||||
supplementalGroups:
|
||||
rule: 'MustRunAs'
|
||||
ranges:
|
||||
- min: 1
|
||||
max: 65535
|
||||
fsGroup:
|
||||
rule: 'MustRunAs'
|
||||
ranges:
|
||||
- min: 1
|
||||
max: 65535
|
||||
28
config/02-sa.yaml
Normal file
28
config/02-sa.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: webhook-interseptor
|
||||
namespace: webhook-interseptor
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: webhook-interseptor
|
||||
namespace: webhook-interseptor
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: webhook-interseptor
|
||||
namespace: webhook-interseptor
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: webhook-interseptor
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: webhook-interseptor
|
||||
28
config/deployment.yaml
Normal file
28
config/deployment.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: webhook-interseptor
|
||||
namespace: webhook-interseptor
|
||||
labels:
|
||||
app: webhook-interseptor
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: webhook-interseptor
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: webhook-interseptor
|
||||
spec:
|
||||
serviceAccountName: webhook-interseptor
|
||||
containers:
|
||||
- name: webhook-interseptor
|
||||
image: git.giftfish.de/ston1th/webhook-interseptor
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
15
config/service.yaml
Normal file
15
config/service.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: webhook-interseptor
|
||||
namespace: webhook-interseptor
|
||||
labels:
|
||||
app: webhook-interseptor
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: webhook-interseptor
|
||||
137
main.go
Normal file
137
main.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"time"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
var version string
|
||||
|
||||
type PR struct {
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
func prHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var pr PR
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("debug headers: %#v", r.Header)
|
||||
log.Printf("debug body: %s", string(body))
|
||||
err = json.Unmarshal(body, &pr)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
switch pr.Action {
|
||||
case "opened", "reopened", "synchronize":
|
||||
if r.Header.Get("X-Interseptor-Action") == "create" {
|
||||
w.Write(body)
|
||||
return
|
||||
}
|
||||
case "closed":
|
||||
if r.Header.Get("X-Interseptor-Action") == "delete" {
|
||||
w.Write(body)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
type Push struct {
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
|
||||
func pushHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var p Push
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("debug headers: %#v", r.Header)
|
||||
log.Printf("debug: %s", string(body))
|
||||
err = json.Unmarshal(body, &p)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if r.Header.Get("X-Interseptor-Ref") == p.Ref {
|
||||
w.Write(body)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
type CommentBody struct {
|
||||
Comment Comment `json:"comment"`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
func commentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var c CommentBody
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("debug headers: %#v", r.Header)
|
||||
log.Printf("debug: %s", string(body))
|
||||
err = json.Unmarshal(body, &c)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if r.Header.Get("X-Interseptor-Comment") == c.Comment.Body {
|
||||
w.Write(body)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func dumpHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("debug headers: %#v", r.Header)
|
||||
log.Printf("debug: %s", string(body))
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
event := r.Header.Get("X-GitHub-Event")
|
||||
log.Printf("%s %s %s: %s", r.RemoteAddr, r.Method, r.URL, event)
|
||||
switch event {
|
||||
case "pull_request":
|
||||
prHandler(w, r)
|
||||
case "push":
|
||||
pushHandler(w, r)
|
||||
//case "issue_comment":
|
||||
// commentHandler(w, r)
|
||||
default:
|
||||
dumpHandler(w, r)
|
||||
}
|
||||
})
|
||||
s := &http.Server{
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
Addr: ":8080",
|
||||
}
|
||||
log.Fatal(s.ListenAndServe())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue