diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..220dd6d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: ci + +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build PDK Container + id: build + uses: docker/build-push-action@v5 + with: + context: . + push: false + load: true + tags: puppet/pdk:${{ github.sha }} + + - name: Test with no workspace volume + run: | + if docker run --rm ${{ steps.build.outputs.imageid }} --version 2>"$GITHUB_WORKSPACE/.errout" ; then + echo '::error::expected an error that was not returned' + exit 1 + fi + grep -s 'error: .* is not mounted in the container.' < "$GITHUB_WORKSPACE/.errout" + + - name: Test with a workspace volume + run: | + docker run --rm -v `pwd`:/workspace ${{ steps.build.outputs.imageid }} --version 2>"$GITHUB_WORKSPACE/.errout" | grep -E '^[[:digit:]\.]+$' + grep -s 'mount a volume to /cache in the container to improve performance' < "$GITHUB_WORKSPACE/.errout" + + - name: Test create new module + run: | + WORKSPACE_OWNER="$(stat -c '%u:%g' .)" + docker run --rm -v `pwd`:/workspace ${{ steps.build.outputs.imageid }} new module dockertest --skip-interview + if [ "${WORKSPACE_OWNER}" != "$(stat -c '%u:%g' dockertest)" ] ; then + echo "::error::pdk in container failed to run with same uid and gid of host workspace" + exit 1 + fi + cd dockertest + docker run --rm -v `pwd`:/workspace ${{ steps.build.outputs.imageid }} new class test + docker run --rm -v `pwd`:/workspace ${{ steps.build.outputs.imageid }} validate + docker run --rm -v `pwd`:/workspace ${{ steps.build.outputs.imageid }} test unit + + - name: Test running with root workspace + run: | + sudo cp -r dockertest roottest + cd roottest + docker run --rm -v `pwd`:/workspace ${{ steps.build.outputs.imageid }} new class root + + - name: Test running with workspace ownership not matching user + run: | + cd roottest + if docker run --rm --user $UID:$GID -v `pwd`:/workspace ${{ steps.build.outputs.imageid }} new class root 2>"$GITHUB_WORKSPACE/.errout" ; then + echo '::error::expected an error that was not returned' + exit 1 + fi + grep -s 'error: unable to write to /workspace' < "$GITHUB_WORKSPACE/.errout" + + - name: Test deprecated /root volume + run: | + cd roottest + docker run --rm -v `pwd`:/root ${{ steps.build.outputs.imageid }} new class toor 2>"$GITHUB_WORKSPACE/.errout" + grep -s 'the /root workdir is deprecated' < "$GITHUB_WORKSPACE/.errout" diff --git a/Dockerfile b/Dockerfile index a174b40..446ff22 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,11 @@ WORKDIR /root ADD install-pdk-release.sh . ADD install-onceover.sh . ADD pdk-release.env . +COPY entrypoint.sh /.entrypoint.sh + +RUN passwd -d root && \ + mkdir /cache && \ + chmod a+rwx /cache RUN apt-get update && \ apt-get install -y curl openssh-client && \ @@ -25,4 +30,6 @@ ENV PATH="${PATH}:/opt/puppetlabs/pdk/private/git/bin" ENV PDK_DISABLE_ANALYTICS=true ENV LANG=C.UTF-8 -ENTRYPOINT ["/opt/puppetlabs/pdk/bin/pdk"] +WORKDIR /workspace + +ENTRYPOINT ["/.entrypoint.sh"] diff --git a/README.md b/README.md index 1b2ee14..7a3022c 100644 --- a/README.md +++ b/README.md @@ -45,16 +45,23 @@ pushes all commits and tags back to this repo. Finally, Docker Hub is configured to watch the this repo and build/tag new images automatically based on the branch or tag that received new commits. + ## How to use the Image Download a release from Docker Hub as detailed above. e.g. ``` -docker pull puppet/pdk +docker pull puppet/pdk:latest ``` Run it ```bash -docker run -v /path/to/your/module:/root puppet/pdk +docker run -v /path/to/module:/workspace puppet/pdk +``` + +Run it with persistent pdk cache + +```bash +docker run -v /path/to/module:/workspace -v /path/to/cache:/cache puppet/pdk ``` diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..bb9ee4a --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +# re-entrant script to support automatically switching to an unprivileged user +# that matches the ownership of the RUN_WORKDIR (see below) + +set -e + +RUN_USER=pdk +RUN_WORKDIR="${PWD}" + +[ -z "${UID}" ] && UID=$(id -u) +[ -z "${GID}" ] && GID=$(id -g) + +[ "$UID" -ne 0 ] && RUNNING_NON_ROOT=1 + +# check if required path is mounted +# check for deprecated /root volume +if grep -sq " /root " < /proc/mounts ; then + [ -z "$ENTRYPOINT_RELOAD" ] && echo >&2 "warning: the /root workdir is deprecated, use /workspace instead." + RUN_WORKDIR="/root" +elif ! grep -sq " ${RUN_WORKDIR} " < /proc/mounts ; then + echo >&2 "error: ${RUN_WORKDIR} is not mounted in the container." ; exit 1 +fi + +create_user() { + if [ "$1" -gt 0 ] ; then + if [ "$2" -gt 0 ] ; then + su - -c "groupadd -g $2 $RUN_USER" 2>/dev/null || true + fi + su - -c "useradd -d /cache -u $1 -g $2 $RUN_USER ; chown $RUN_USER: /cache ; passwd -d $RUN_USER >/dev/null" + fi +} + +# skip if re-running under newly created user +if [ -z "$ENTRYPOINT_RELOAD" ] ; then + if [ -z "$RUNNING_NON_ROOT" ] ; then + UID=$(stat -c '%u' "$RUN_WORKDIR") + GID=$(stat -c '%g' "$RUN_WORKDIR") + [ "$UID" -eq 0 ] && RUN_USER="root" + fi + create_user "$UID" "$GID" + # re-run with new user + exec su - $RUN_USER -c "cd $RUN_WORKDIR ; ENTRYPOINT_RELOAD=1 $0 $*" + exit +fi + +# sanity check supported volumes +for volume in ${RUN_WORKDIR} /cache ; do + if [ ! -w "$volume" ] ; then + echo >&2 "error: unable to write to ${volume}. Ensure permissions are correct on the host." ; exit 1 + fi + if ! find "$volume/." -maxdepth 1 -name '.' \( -uid "$UID" -a -perm -u+rw \) -o \( -group "$GID" -a -perm -g+rw \) -exec true {} + ; then + echo >&2 "warning: pdk may not function properly with the user/group ownership or permissions on ${volume}." + fi +done + +# recommend cache path is mounted +if ! grep -sq " /cache " < /proc/mounts ; then + echo >&2 "mount a volume to /cache in the container to improve performance." +fi + +export PATH="${PATH}:/opt/puppetlabs/pdk/private/git/bin" +export PDK_DISABLE_ANALYTICS=true +export LANG=C.UTF-8 + +exec /opt/puppetlabs/pdk/bin/pdk "$@"