diff --git a/.gitignore b/.gitignore index 2101448..752172c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -*.xz +*.bak *.dsk +*.gz *.tgz # generated from template diff --git a/README.md b/README.md index ccc3a78..5c51337 100644 --- a/README.md +++ b/README.md @@ -3,18 +3,25 @@ ## Docker To build the container, run `docker/build.sh [tag]` with the tag you want to apply to the image. +**Important:** If you want to bake a disk image into the container which will be copied into /mnt and used if no image is found there on startup, make sure to put it as `rq0.dsk` in the build directory. + To run the container, use the following command adapted to your needs: docker run -d --rm --privileged --name pdp -v `pwd`/rq0.dsk:/mnt/rq0.dsk ## Kubernetes -To run in Kubernetes, first you need to build the docker image and push it to a registry your cluster has access to. +To run in Kubernetes, first you need to build the docker image (preferably with a disk baked in) and push it to a registry your cluster has access to. -Once you've done that, run `./bootstrap-k8s.sh `. `tag` should be a ref to the docker image, uploaded to a registry the cluster can access. `disk_filename` should be an xz-compressed PDP-11 disk image, which will be extracted and attached to the pdp as rq0. +Once you've done that, run `./bootstrap-k8s.sh `. `tag` should be a ref to the docker image, uploaded to a registry the cluster can access. -To expose the services on an external IP, first install MetalLB, and then use `./configure-metallb.sh `. `ip` should be an IP address on a broadcast domain the k8s nodes are on. +To expose the services on an external IP, first install MetalLB, and then use `./misc/configure-metallb.sh `. `ip` should be an IP address on a broadcast domain the k8s nodes are on. + +### Imageless container +If you built the Docker container without a baked-in image, instead of using the normal `bootstrap-k8s.sh`, you'll need to use `misc/bootstrap-k8s-imageless.sh` as follows: + +Run `./misc/bootstrap-k8s.sh `. `tag` should be a ref to the docker image, uploaded to a registry the cluster can access. `disk_filename` should be a gzip-compressed PDP-11 disk image, which will be extracted and attached to the pdp as rq0. ### k3s To use an external IP, MetalLB is required. Therefore, the cluster must not have ServiceLB enabled, as it will conflict. For k3s, just add `--disable=servicelb` to the server command line options. For k3d, create the cluster as follows: - k3d cluster create pdp --k3s-arg "--disable=servicelb@server:*" \ No newline at end of file + k3d cluster create pdp --k3s-arg "--disable=servicelb@server:*" diff --git a/bootstrap-k8s.sh b/bootstrap-k8s.sh index 949647e..06d3d97 100755 --- a/bootstrap-k8s.sh +++ b/bootstrap-k8s.sh @@ -6,35 +6,17 @@ usage () { exit 1 } -[[ $# -eq 2 ]] || usage +[[ $# -eq 1 ]] || usage tag="$1" -disk_filename="$2" cd "$(dirname $0)" -[[ -f "$disk_filename" && -r "$disk_filename" ]] || { echo "$disk_filename does not exist, is not a regular file, or is not readable"; exit 1; } -[[ "$disk_filename" == *.xz ]] || { echo "$disk_filename does not appear to be an xz-compressed disk image"; exit 1; } sed "s!!${tag}!" ./k8s/deployment.yaml.template > ./k8s/deployment.yaml echo "Creating PVC..." kubectl apply -f ./k8s/pvc.yaml -echo "Creating temporary pod..." -kubectl apply -f ./k8s/pod-tmp.yaml -echo "Waiting for temporary pod..." -while ! kubectl get pod pdp-tmp|grep -q Running; do sleep 1; done -echo "Checking for disk image..." -if ! kubectl exec -it pdp-tmp -- test -f /mnt/rq0.dsk &>/dev/null; then - echo "Uploading compressed disk image..." - kubectl exec pdp-tmp -- rm -f /mnt/rq0.dsk.xz - kubectl cp "$disk_filename" pdp-tmp:/mnt/rq0.dsk.xz - echo "Extracting disk image..." - kubectl exec pdp-tmp -- apk add xz - kubectl exec pdp-tmp -- xz -d /mnt/rq0.dsk.xz -fi -echo "Removing temporary pod..." -kubectl delete -f ./k8s/pod-tmp.yaml echo "Creating PDP deployment..." kubectl apply -f ./k8s/deployment.yaml echo "Creating services..." for i in ./k8s/svc-*.yaml; do kubectl apply -f "$i"; done -echo "Done! Optionally, run ./k8s/configure-metallb.sh to put the services on an external IP." +echo "Done! Optionally, run ./misc/configure-metallb.sh to put the services on an external IP." diff --git a/docker/Corefile b/docker/Corefile new file mode 100644 index 0000000..90c4a46 --- /dev/null +++ b/docker/Corefile @@ -0,0 +1,4 @@ +.:53 { + forward . /etc/resolv.conf + reload +} diff --git a/docker/Dockerfile b/docker/Dockerfile index 7de09d6..9e5ea3f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,13 +1,27 @@ +# Build container FROM debian:11-slim AS builder RUN apt-get -y update && apt-get -y upgrade && apt-get -y install gcc libpcap-dev libvdeplug-dev libpcre3-dev libedit-dev libsdl2-dev libpng-dev libsdl2-ttf-dev build-essential && apt-get clean ADD simh-3.9-0.tgz / WORKDIR /simh-3.9-0 RUN make pdp11 +# Final container FROM debian:11-slim ENV DISK_FILENAME=rq0.dsk -RUN apt-get -y update && apt-get -y upgrade && apt-get -y install ed libpcap-dev iproute2 iptables gcc vdeplug libpcre3 net-tools telnet +RUN apt-get -y update && apt-get -y upgrade && apt-get -y install curl ed gcc iproute2 iptables libpcap-dev libpcre3 net-tools telnet vdeplug + +# Add Tini to handle reaping processes +ENV TINI_VERSION 0.19.0 +ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tini +RUN chmod +x /tini +ENTRYPOINT ["/tini", "--"] + +# Add CoreDNS to forward DNS based on /etc/resolv.conf +ENV COREDNS_VERSION 1.11.1 +RUN curl -L https://github.com/coredns/coredns/releases/download/v${COREDNS_VERSION}/coredns_${COREDNS_VERSION}_linux_amd64.tgz|tar -xzC /usr/local/bin/ + COPY --from=builder /simh-3.9-0/BIN/pdp11 /usr/local/bin/pdp11 -COPY boot.ini.template / -COPY startup.sh / -CMD /startup.sh + +# Uses pattern matching to optionally copy rq0.dsk.gz if it exists +COPY startup.sh Corefile boot.ini.template rq0.dsk.g[z] / +CMD ["/startup.sh"] diff --git a/docker/startup.sh b/docker/startup.sh index f8e23e9..76b562e 100755 --- a/docker/startup.sh +++ b/docker/startup.sh @@ -1,25 +1,43 @@ #!/bin/sh -set -ex +set -e [ -n "$DISK_FILENAME" ] || DISK_FILENAME="rq0.dsk" -[ -f "/mnt/$DISK_FILENAME" ] || { echo "Unable to find tape /mnt/$DISK_FILENAME"; exit 1; } -[ -w "/mnt/$DISK_FILENAME" ] || { echo "Tape /mnt/$DISK_FILENAME is not writable"; exit 1; } + +echo -n "Checking for disk... " +if [ -f "/mnt/$DISK_FILENAME" ]; then + [ -w "/mnt/$DISK_FILENAME" ] || { echo "Disk /mnt/$DISK_FILENAME exists and is not writable"; exit 1; } + echo "Disk found and is writable." +else + [ -w "/mnt/" ] || { echo "Disk /mnt/$DISK_FILENAME not found and /mnt not writable"; exit 1; } + [ -f "/rq0.dsk.gz" ] || { echo "Disk /mnt/$DISK_FILENAME not found and image was not built with a default disk"; exit 1; } + echo "Disk not found." + echo "Extracting default disk image..." + gzip -cd /rq0.dsk.gz > "/mnt/$DISK_FILENAME" || { echo "Failed to extract default disk image to /mnt/$DISK_FILENAME"; exit 1; } +fi + +# Start CoreDNS as soon as possible after validations done +echo "Starting CoreDNS..." +[ -z "$COREDNS_DEBUG" ] && COREDNS_QUIET="-quiet" +coredns "$COREDNS_QUIET" -conf /Corefile & # Set exit command only if NOEXIT not set [ -z "$NOEXIT" ] && EXIT_CMD="exit" || EXIT_CMD="" +echo "Configuring simh..." sed -e "s//${DISK_FILENAME}/" \ -e "s//${EXIT_CMD}/" /boot.ini.template > /boot.ini -cidr=28 -hostip=1.3.3.1 -pdpip=1.3.3.7 +echo "Configuring networking..." +[ -z "$CIDR" ] && CIDR=28 +[ -z "$HOSTIP" ] && HOSTIP=1.3.3.1 +[ -z "$PDPIP" ] && PDPIP=1.3.3.7 ip tuntap add pdp mode tap ip link set pdp up -ip addr add "${hostip}/${cidr}" dev pdp -iptables -t nat -A PREROUTING ! -i pdp -p tcp --dport 4000 -j ACCEPT -iptables -t nat -A PREROUTING ! -i pdp -j DNAT --to-destination 1.3.3.7 +ip addr add "${HOSTIP}/${CIDR}" dev pdp +iptables -t nat -A PREROUTING ! -i pdp -p tcp --dport 4000 -j ACCEPT # dz serial device +iptables -t nat -A PREROUTING ! -i pdp -j DNAT --to-destination "$PDPIP" iptables -t nat -A POSTROUTING ! -o pdp -j MASQUERADE +echo "Booting PDP-11..." pdp11 /boot.ini diff --git a/misc/bootstrap-k8s-imageless.sh b/misc/bootstrap-k8s-imageless.sh new file mode 100755 index 0000000..5220ba2 --- /dev/null +++ b/misc/bootstrap-k8s-imageless.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e + +# +# This file is used to deploy a PDP-11 from a container image without a baked-in disk image. +# + +usage () { + echo "usage: $0 tag disk_filename" + exit 1 +} + +[[ $# -eq 2 ]] || usage +tag="$1" +disk_filename="$2" + +cd "$(dirname $(dirname $0))" +[[ -f "$disk_filename" && -r "$disk_filename" ]] || { echo "$disk_filename does not exist, is not a regular file, or is not readable"; exit 1; } +[[ "$disk_filename" == *.gz ]] || { echo "$disk_filename does not appear to be a gzip-compressed disk image"; exit 1; } + +sed "s!!${tag}!" ./k8s/deployment.yaml.template > ./k8s/deployment.yaml + +echo "Creating PVC..." +kubectl apply -f ./k8s/pvc.yaml +echo "Creating temporary pod..." +kubectl apply -f ./k8s/pod-tmp.yaml +echo "Waiting for temporary pod..." +while ! kubectl get pod pdp-tmp|grep -q Running; do sleep 1; done +echo "Checking for disk image..." +if ! kubectl exec -it pdp-tmp -- test -f /mnt/rq0.dsk &>/dev/null; then + echo "Uploading compressed disk image..." + kubectl exec pdp-tmp -- rm -f /mnt/rq0.dsk.gz + kubectl cp "$disk_filename" pdp-tmp:/mnt/rq0.dsk.gz + echo "Extracting disk image..." + kubectl exec pdp-tmp -- gzip -d /mnt/rq0.dsk.gz +fi +echo "Removing temporary pod..." +kubectl delete -f ./k8s/pod-tmp.yaml +echo "Creating PDP deployment..." +kubectl apply -f ./k8s/deployment.yaml +echo "Creating services..." +for i in ./k8s/svc-*.yaml; do kubectl apply -f "$i"; done +echo "Done! Optionally, run ./misc/configure-metallb.sh to put the services on an external IP." diff --git a/k8s/configure-metallb.sh b/misc/configure-metallb.sh similarity index 93% rename from k8s/configure-metallb.sh rename to misc/configure-metallb.sh index 7a90469..737d29d 100755 --- a/k8s/configure-metallb.sh +++ b/misc/configure-metallb.sh @@ -9,7 +9,7 @@ usage () { [[ $# -eq 1 ]] || usage ip="$1" -cd "$(dirname $0)" +cd "$(dirname $(dirname $0))/k8s/" sed "s!!${ip}!" ./metallb.yaml.template > ./metallb.yaml diff --git a/misc/metallb.yaml b/misc/metallb.yaml new file mode 100644 index 0000000..e69de29