Webhooks
Continuous integration on easy mode. Webhooks allow for a ton of functionality, but we are going to use it to kick off a kubernetes job. Effectivitely automating reloading content on a static website.
Background
This docs website is a static site that is hosted inside an nginx container. The storage for these redundant pods is a longhorn rwx pvc that gets stood up. To initialize the storage a kubernetes job is run. This job does the following:
- git clones the
rskntroot/rskiorepo containing the artifacts required to render the site - executes the
mkdocscommand to render the static site
So what if when we push to github, we setup a webhook that tells kubernetes to kick off that job? Well, we achieve some form of automation. So how do we do this?
Setup
RBAC
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: job-creator
namespace: dev
rules:
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: job-creator-binding
namespace: dev
subjects:
- kind: ServiceAccount
name: webhook-job-trigger
namespace: default
roleRef:
kind: Role
name: job-creator
apiGroup: rbac.authorization.k8s.io
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: job-creator
namespace: prod
rules:
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: job-creator-binding
namespace: prod
subjects:
- kind: ServiceAccount
name: webhook-job-trigger
namespace: default
roleRef:
kind: Role
name: job-creator
apiGroup: rbac.authorization.k8s.io
ConfigMap
We will create a config map from a directory including the following files.
ConvertJob
We are going to be using curl to call the kubernetes API directly, so we need to convert our job from yaml to json.
Convert the job to JSON and save to etc/mkdocs-dev.json
apiVersion: batch/v1
kind: Job
metadata:
generateName: mkdocs-builder-
namespace: dev
spec:
ttlSecondsAfterFinished: 600
template:
spec:
containers:
- name: mkdocs
image: squidfunk/mkdocs-material
command: ["/bin/sh", "-c"]
args:
- |
git clone --single-branch -b dev https://github.com/rskntroot/rskio.git --depth 1 /docs
cd /docs/mkdocs
mkdocs build --site-dir /output
volumeMounts:
- name: mkdocs-storage
mountPath: /output
restartPolicy: Never
volumes:
- name: mkdocs-storage
persistentVolumeClaim:
claimName: mkdocs-pvc
The following docs we will assume that you also created etc/mkdocs-main.json.
Hooks
create etc/hooks.yaml
- id: rskio-mkdocs
execute-command: /etc/webhook/reload.sh
command-working-directory: /etc/webhook
response-message: payload received
response-headers:
- name: Access-Control-Allow-Origin
value: "*"
pass-arguments-to-command:
- source: payload
name: ref
- source: payload
name: repository.full_name
trigger-rule:
and:
- match:
type: value
value: push
parameter:
source: header
name: X-GitHub-Event
- match:
type: value
value: rskntroot/rskio
parameter:
source: payload
name: repository.full_name
Command
#!/bin/sh
REF=$1
REPO=$2
dispatch() {
NS=$1
JOB_JSON=$2
SA_PATH="/var/run/secrets/kubernetes.io/serviceaccount"
curl https://kubernetes.default.svc/apis/batch/v1/namespaces/${NS}/jobs \
-X POST \
-H "Authorization: Bearer $(cat ${SA_PATH}/token)" \
-H "Content-Type: application/json" \
--cacert "${SA_PATH}/ca.crt" \
-d "@${JOB_JSON}"
}
docs(){
case ${REF} in
refs/heads/dev)
dispatch dev "/etc/webhook/mkdocs-dev.json"
;;
refs/heads/main)
dispatch prod "/etc/webhook/mkdocs-main.json"
;;
*)
echo "skipping push to unsupported ref ${REF}"
exit 0
;;
esac
}
case ${REPO} in
rskntroot/rskio)
docs
;;
*)
echo "skipping push to unsupported repo ${REPO}"
;;
esac
Create
once all resources in etc are created run the following command:
if you need to update anything run the following:
Resources
The following resources will complete the work
apiVersion: apps/v1
kind: Deployment
metadata:
name: webhook-docs
spec:
replicas: 1
selector:
matchLabels:
app: webhook-docs
template:
metadata:
labels:
app: webhook-docs
spec:
serviceAccountName: webhook-job-trigger
containers:
- name: webhook-docs
image: ghcr.io/linuxserver-labs/webhook:latest
command: ["/app/webhook"]
args:
- -hooks=/etc/webhook/hooks.yaml
- -hotreload
- -verbose
volumeMounts:
- name: webhook-etc
mountPath: /etc/webhook
volumes:
- name: webhook-etc
configMap:
name: webhook-etc
defaultMode: 493 # 0755
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: io-rsk-dev-hooks-tls
spec:
secretName: io-rsk-dev-hooks-tls
issuerRef:
name: dev-step-issuer
kind: ClusterIssuer
commonName: hooks.dev.rsk.io
dnsNames:
- hooks.dev.rsk.io
privateKey:
algorithm: RSA
encoding: PKCS1
size: 2048
usages:
- server auth
- client auth
duration: 2160h # 90 days
renewBefore: 360h # 15 days
secretTemplate:
annotations:
kubeseal-secret: "true"
labels:
domain: hooks-dev-rsk-io
Testing
curl -X POST https://hooks.dev.rsk.io/hooks/rskio-mkdocs \
-H 'X-Github-Event: push' \
-H 'Content-type: application-json' \
-d '{"ref": "refs/heads/dev","repository": {"full_name":"rskntroot/rskio"}}'
Github needs access to a public domain for this to work.