Skip to content

Commit

Permalink
Isolate zulip and ssh keys from build container (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmandel authored Sep 11, 2023
1 parent 2207101 commit 22b3d91
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 109 deletions.
7 changes: 4 additions & 3 deletions images/ig-build/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
FROM openjdk:21-jdk-bullseye
MAINTAINER Josh Mandel

RUN apt-get update && apt-get -y install python3 python3-pip gosu openssl wget graphviz ruby2.7 ruby2.7-dev
RUN apt-get update && apt-get -y install python3 python3-pip gosu openssl wget graphviz ruby2.7 ruby2.7-dev inotify-tools
RUN pip3 install --upgrade requests zulip
RUN gem install jekyll jekyll-asciidoc

RUN cd /tmp && \
wget --quiet https://nodejs.org/dist/latest-v18.x/node-v18.16.0-linux-x64.tar.xz && \
wget --quiet https://nodejs.org/dist/v18.17.1/node-v18.17.1-linux-x64.tar.xz && \
cd /usr/local && \
tar --strip-components 1 -xf /tmp/node-v18.16.0-linux-x64.tar.xz
tar --strip-components 1 -xf /tmp/node-v18.17.1-linux-x64.tar.xz

# Install required packages

Expand All @@ -27,6 +27,7 @@ RUN mkdir -p /root/.ssh && \
IdentitiesOnly yes" > /root/.ssh/config && \
chmod go-wrx /root/.ssh/config

ADD watch-and-publish /usr/local/bin/watch-and-publish
ADD publish /usr/local/bin/publish

ENTRYPOINT python3 -m builder.builder || true
40 changes: 22 additions & 18 deletions images/ig-build/builder/builder.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import logging
import os
import random
import requests
import shutil
import string
import subprocess
import sys

from .util import make_temp_dir, do, send_zulip
from .util import do, SCRATCH_SPACE
from os.path import normpath

GITHUB = 'https://github.com/%(org)s/%(repo)s'
Expand All @@ -26,19 +23,30 @@ def get_qa_score(build_dir):
except:
return "No QA File"


def build(config):

if config['branch'] == 'gh-pages':
sys.exit(0)

temp_dir = make_temp_dir()
temp_dir = SCRATCH_SPACE
clone_dir = os.path.join(temp_dir, 'repo')
build_dir = os.path.join(clone_dir, 'output')
logfile = os.path.join(temp_dir, 'build.log')
upload_dir = os.path.join(SCRATCH_SPACE, 'upload')
logging.basicConfig(filename=logfile, level=logging.DEBUG)
logging.info('about to clone!')

def finalize(result_dir, message, pubargs):
os.rename(result_dir, upload_dir)
message_path = os.path.join(SCRATCH_SPACE, 'message')
with open(message_path, 'w') as f:
f.write(message)

# Write each argument to a new line in a temporary file
done_path = os.path.join(SCRATCH_SPACE, 'done')
done_temp_path = done_path + '.temp'
with open(done_temp_path, 'w') as f:
for arg in pubargs:
f.write(arg + '\n')
# Atomically rename the temporary file to the desired file name
os.rename(done_temp_path, done_path)

def run_git_cmd(cmds):
return subprocess.check_output(cmds, cwd=clone_dir, universal_newlines=True).strip()

Expand Down Expand Up @@ -78,14 +86,14 @@ def is_default_branch():
message = ["**[%(org)s/%(repo)s: %(branch)s](https://github.com/%(org)s/%(repo)s/tree/%(branch)s)** rebuilt\n",
"Commit: %(commit)s :%(emoji)s:\n",
"Details: [build logs](%(root)s/%(org)s/%(repo)s/branches/%(branch)s/%(buildlog)s)"]

print("finalizing")
if not built:
print("Build error occurred")
details['emoji'] = 'thumbs_down'
details['buildlog'] = 'failure/build.log'
message += [" | [debug](%(root)s/%(org)s/%(repo)s/branches/%(branch)s/failure)"]
shutil.copy(logfile, clone_dir)
do(['publish', details['org'], details['repo'], details['branch'], 'failure', details['default']], clone_dir, pipe=True)
finalize(clone_dir, "".join(message)%details, [details['org'], details['repo'], details['branch'], 'failure', details['default']])
else:
print("Build succeeded")
details['emoji'] = 'thumbs_up'
Expand All @@ -94,12 +102,8 @@ def is_default_branch():
message += [" | [qa: %s]"%get_qa_score(build_dir), "(%(root)s/%(org)s/%(repo)s/branches/%(branch)s/qa.html)"]
print("Copying logfile")
shutil.copy(logfile, build_dir)
print("publishing")
do(['publish', details['org'], details['repo'], details['branch'], 'success', details['default']], build_dir, pipe=True)
print("published")

send_zulip('committers/notification', 'ig-build', "".join(message)%details)
# sys.exit(0 if built else 1)
finalize(build_dir, "".join(message)%details, [details['org'], details['repo'], details['branch'], 'success', details['default']])
print("finalized")

if __name__ == '__main__':
build({
Expand Down
23 changes: 0 additions & 23 deletions images/ig-build/builder/util.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import datetime
import logging
import os
import random
import string
import subprocess

import zulip

ZULIP_API = os.environ.get('ZULIP_API', 'https://chat.fhir.org')
SCRATCH_SPACE = os.environ.get('SCRATCH', '/scratch')
DEADLINE_SECONDS = int(os.environ.get('DEADLINE_SECONDS', '1800'))
DEADLINE_BUFFER = int(os.environ.get('DEADLINE_BUFFER', '120'))
deadline_time = datetime.datetime.now() + datetime.timedelta(seconds=(DEADLINE_SECONDS-DEADLINE_BUFFER))

def make_temp_dir(prefix='ig-build-temp-', N=6):
dirname = prefix + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))
dirpath = os.path.abspath(os.path.join(SCRATCH_SPACE, dirname))
os.makedirs(dirpath)
return dirpath

def do(args, cwd=SCRATCH_SPACE, pipe=False, deadline=False):
logging.debug('running: %s'%" ".join(args))
logfile = logging.getLoggerClass().root.handlers[0].baseFilename
Expand All @@ -36,16 +26,3 @@ def do(args, cwd=SCRATCH_SPACE, pipe=False, deadline=False):
pr.kill()
logging.debug("\n\n*** Timeout -- deadline reached")
return 1

def send_zulip(stream, topic, content):
logging.debug('zulip messaging: %s %s %s'%(stream, topic, content))
zulip.Client(
site=ZULIP_API,
api_key=os.environ.get('ZULIP_API_KEY'),
email=os.environ.get('ZULIP_EMAIL')
).send_message({
'type': 'stream',
'content': content,
'to': stream,
'subject': topic
})
37 changes: 37 additions & 0 deletions images/ig-build/watch-and-publish
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash

# Wait for 'done' file to appear
echo "Waiting for the builder to complete"

cd /scratch

while true; do
inotifywait -e create -q -q . && [ -f done ] && break
done
echo "Builder completed; uploading"

# At this point, both 'message' and 'done' files are present
# Read each line from 'done' and store as positional arguments
pubargs=()
while IFS= read -r line; do
pubargs+=("$line")
done < /scratch/done

echo "Message"
cat /scratch/message

echo "publish"
cat /scratch/done

# Then, Run publish.sh with the arguments from the 'done' file
cd /scratch/upload
publish "${pubargs[@]}"

# Send Zulip message
echo "Uploaded; notifying zulip"
zulip-send --stream committers/notification --subject ig-build \
--message "$(< /scratch/message)" \
--user "$ZULIP_EMAIL" \
--api-key "$ZULIP_API_KEY" \
--site https://chat.fhir.org
echo "Notified"
6 changes: 6 additions & 0 deletions k8s/create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ gcloud compute disks create fhir-ci-build-disk --size 100GB

kubectl -n fhir create secret generic zulip-secrets --from-literal=email=$ZULIP_EMAIL --from-literal=api_key=$ZULIP_API_KEY

ssh-keygen -t rsa -f id
kubectl -n fhir create secret generic ci-build-keys --from-file=id --from-file=id.pub

# get these from Grahame ;-)
kubectl -n fhir create secret generic fhir-settings --from-file=keyfile.ini --from-file=fhir-settings.json

gcloud compute disks create caddy-cert-disk --size=10GB --zone=us-east1-d
kubectl -n fhir create configmap caddy-conf-volume --from-file Caddyfile

Expand Down
27 changes: 27 additions & 0 deletions k8s/dev-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,30 @@ gcloud docker -- push gcr.io/fhir-org-starter-project/ci-build
cd triggers/ig-commit-trigger
gcloud functions deploy ig-commit-trigger --runtime nodejs18 --trigger-http
```


---

## Testing locally with minikube

```sh
minikube config set memory 20000
minikube start
eval $(minikube -p minikube docker-env)

kubectl create ns fhir

ssh-keygen -t rsa -f id
kubectl -n fhir create secret generic ci-build-keys --from-file=id --from-file=id.pub

echo "" > keyfile.ini
echo "{}" > fhir-settings.json
kubectl -n fhir create secret generic fhir-settings --from-file=keyfile.ini --from-file=fhir-settings.json

kubectl -n fhir create secret generic zulip-secrets --from-literal=email=bot@hsot --from-literal=api_key=zapi
```

Build the igbuild image as `igbuild` in minikube docker

kubectl apply -f example-job-for-minikube.yaml

83 changes: 83 additions & 0 deletions k8s/example-job-for-minikube.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
apiVersion: batch/v1
kind: Job
metadata:
generation: 1
name: buildk
namespace: fhir
spec:
activeDeadlineSeconds: 36000
backoffLimit: 6
completions: 1
parallelism: 1
suspend: false
template:
spec:
containers:
- name: ig-upload
image: igbuild
imagePullPolicy: Never
command: ["/usr/local/bin/watch-and-publish"]
args: []
env:
- name: ZULIP_EMAIL
valueFrom:
secretKeyRef:
key: email
name: zulip-secrets
- name: ZULIP_API_KEY
valueFrom:
secretKeyRef:
key: api_key
name: zulip-secrets
resources:
requests:
memory: 512Mi
volumeMounts:
- name: scratch
mountPath: /scratch
- mountPath: /etc/ci_build_keys
name: keys
- name: ig-build
image: igbuild
imagePullPolicy: Never
env:
- name: PUBLISHER_JAR_URL
value: https://github.com/HL7/fhir-ig-publisher/releases/latest/download/publisher.jar
- name: TX_SERVER_URL
value: http://tx.fhir.org
- name: DEADLINE_SECONDS
value: "36000"
- name: JAVA_MEMORY
value: 19000m
- name: IG_ORG
value: "HL7"
- name: IG_REPO
value: "smart-app-launch"
- name: IG_BRANCH
value: "master"
resources:
limits:
memory: 30Gi
requests:
memory: 1Gi
volumeMounts:
- name: scratch
mountPath: /scratch
- mountPath: /etc/ig.builder.keyfile.ini
name: fhir-settings
subPath: ig.builder.keyfile.ini
- mountPath: /etc/fhir-settings.json
name: fhir-settings
subPath: fhir-settings.json
restartPolicy: Never
volumes:
- name: scratch
emptyDir: {}
- name: keys
secret:
defaultMode: 256
secretName: ci-build-keys
- name: fhir-settings
secret:
defaultMode: 256
secretName: fhir-settings
Loading

0 comments on commit 22b3d91

Please sign in to comment.