diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a15682c0..00cda784 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -22,7 +22,7 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: | ${{ runner.os }}-gradle- - - name: Build Docker images + - name: Build/Test Docker images uses: eskatos/gradle-command-action@v1 with: - arguments: build --info + arguments: build test -PisCI=true --info diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index c337b905..351a796c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -28,7 +28,7 @@ jobs: - name: Set TAG Environment Variable run: | echo "TAG=$GITHUB_SHA" >> $GITHUB_ENV - - name: Build and Push Docker images + - name: Build/Test/Push Docker images uses: eskatos/gradle-command-action@v1 with: - arguments: push '-Pdocker.tags=${{ env.TAG }}' '-Pdocker.repository=${{ secrets.REPOSITORY }}' -Pdocker.driver=docker-container -Pdocker.cacheTo=true -Pdocker.platforms=linux/amd64,linux/arm64 --info + arguments: build test '-Pdocker.tags=${{ env.TAG }}' '-Pdocker.repository=${{ secrets.REPOSITORY }}' -Pdocker.push=true -Pdocker.driver=docker-container -Pdocker.cacheTo=true -Pdocker.platforms=linux/amd64,linux/arm64 -PisCI=true --info diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7a97aca..8f704e75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,8 +30,7 @@ jobs: run: | export TAG=$(echo "${GITHUB_REF}" | cut -d "/" -f3) echo "TAG=$TAG" >> $GITHUB_ENV - - name: Push Docker images + - name: Build/Push/Test Docker images uses: eskatos/gradle-command-action@v1 with: - arguments: push '-Pdocker.tags=${{ env.TAG }}' '-Pdocker.repository=${{ secrets.REPOSITORY }}' -Pdocker.driver=docker-container -Pdocker.cacheTo=true -Pdocker.platforms=linux/amd64,linux/arm64 --info - # @todo add tests. + arguments: build test '-Pdocker.tags=${{ env.TAG }}' '-Pdocker.repository=${{ secrets.REPOSITORY }}' -Pdocker.push=true -Pdocker.driver=docker-container -Pdocker.cacheTo=true -Pdocker.platforms=linux/amd64,linux/arm64 -PisCI=true --info diff --git a/.gitignore b/.gitignore index cd8fe820..5aa41136 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ scratch scratch.md volumes -docker-compose.yml \ No newline at end of file +/docker-compose.yml \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ed02fc6..eab09775 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,14 +14,16 @@ "composer": "shellscript" }, "cSpell.words": [ - "MODESHAPE", - "POSTGRESQL", - "SIGTERM", "binarystorage", "catchable", "classpath", + "crond", "elif", "getenv", - "nativeplatform" + "getenv", + "MODESHAPE", + "nativeplatform", + "POSTGRESQL", + "SIGTERM" ] } \ No newline at end of file diff --git a/abuild/.dockerignore b/abuild/.dockerignore index b43bf86b..badf6cb7 100644 --- a/abuild/.dockerignore +++ b/abuild/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/activemq/.dockerignore b/activemq/.dockerignore index b43bf86b..badf6cb7 100644 --- a/activemq/.dockerignore +++ b/activemq/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/activemq/rootfs/etc/services.d/activemq/finish b/activemq/rootfs/etc/services.d/activemq/finish index f8984dd3..964ffe33 100644 --- a/activemq/rootfs/etc/services.d/activemq/finish +++ b/activemq/rootfs/etc/services.d/activemq/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh : -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/activemq/rootfs/etc/services.d/activemq/run b/activemq/rootfs/etc/services.d/activemq/run index a5096511..d71d9eec 100644 --- a/activemq/rootfs/etc/services.d/activemq/run +++ b/activemq/rootfs/etc/services.d/activemq/run @@ -1,5 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh : -s6-setuidgid activemq -/opt/activemq/bin/activemq console +#!/usr/bin/env bash +set -e +exec s6-setuidgid activemq /opt/activemq/bin/activemq console diff --git a/activemq/tests/ServiceStartsWithDefaults/build.gradle.kts b/activemq/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..c88b2598 --- /dev/null +++ b/activemq/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("started | org.apache.activemq.broker.BrokerService") +} diff --git a/alpaca/.dockerignore b/alpaca/.dockerignore index b43bf86b..badf6cb7 100644 --- a/alpaca/.dockerignore +++ b/alpaca/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/alpaca/tests/ServiceStartsWithDefaults/build.gradle.kts b/alpaca/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..75ef1980 --- /dev/null +++ b/alpaca/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("INFO: Lock acquired") +} diff --git a/base/.dockerignore b/base/.dockerignore index b43bf86b..badf6cb7 100644 --- a/base/.dockerignore +++ b/base/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/base/Dockerfile b/base/Dockerfile index ede6da71..31e0ccd0 100644 --- a/base/Dockerfile +++ b/base/Dockerfile @@ -30,7 +30,7 @@ ARG TARGETARCH COPY --from=download /usr/local/bin/*.sh /usr/local/bin # Install s6 -RUN --mount=type=cache,id=base-downloads,sharing=locked,target=/opt/downloads \ +RUN --mount=type=cache,id=base-downloads,sharing=locked,from=download,target=/opt/downloads \ if [ "${TARGETARCH}" = "arm64" ]; then \ S6_FILE="s6-overlay-aarch64.tar.gz"; \ S6_SHA256="84f585a100b610124bb80e441ef2dc2d68ac2c345fd393d75a6293e0951ccfc5"; \ @@ -69,6 +69,7 @@ ENV \ S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \ S6_CMD_WAIT_FOR_SERVICE=1 \ S6_CMD_WAIT_FOR_SERVICES_MAXTIME=30000 \ + S6_SERVICES_GRACETIME=30000 \ S6_LOGGING=0 \ TERM=xterm diff --git a/base/rootfs/etc/cont-init.d/00-container-environment-02-database-defaults.sh b/base/rootfs/etc/cont-init.d/00-container-environment-02-database-defaults.sh index fea120e5..a4d7b562 100755 --- a/base/rootfs/etc/cont-init.d/00-container-environment-02-database-defaults.sh +++ b/base/rootfs/etc/cont-init.d/00-container-environment-02-database-defaults.sh @@ -4,6 +4,9 @@ set -e # This is stage `00-container-environment-02` so child images can provide # custom logic for setting the DB_DRIVER as stage `00-container-environment-01`. +# Allow DB_DRIVER to be overridden by FCREPO_DB_DRIVER, etc. +/usr/local/bin/confd-override-environment.sh --prefix DB + # Dervive DB_HOST/DB_PORT from the given driver if not specified. DB_DRIVER=$(&2 + echo "Only MySQL / PostgreSQL / SQLite are supported values for DB_DRIVER." >&2 exit 1 esac # Use what has been provided by the user or default to the derived values. cat << EOF | /usr/local/bin/confd-import-environment.sh -DB_HOST={{ getenv "DB_HOST" "${DB_HOST}" }} -DB_PORT={{ getenv "DB_PORT" "${DB_PORT}" }} +DB_HOST="{{ getenv "DB_HOST" "${DB_HOST}" }}" +DB_PORT="{{ getenv "DB_PORT" "${DB_PORT}" }}" EOF - -# Allow DB_NAME to be overridden by FCREPO_DB_NAME, etc. -/usr/local/bin/confd-override-environment.sh --prefix DB diff --git a/base/rootfs/etc/services.d/confd/finish b/base/rootfs/etc/services.d/confd/finish index ad50ea87..f6f13371 100644 --- a/base/rootfs/etc/services.d/confd/finish +++ b/base/rootfs/etc/services.d/confd/finish @@ -3,5 +3,5 @@ set -e # Only shutdown the container if this service was enabled otherwise ignore. if [[ "${CONFD_ENABLE_SERVICE}" == "true" ]]; then - s6-svscanctl -t /var/run/s6/services + source /usr/local/share/s6/finish fi diff --git a/base/rootfs/etc/services.d/confd/run b/base/rootfs/etc/services.d/confd/run index e7db9d1d..5e8d1ec2 100644 --- a/base/rootfs/etc/services.d/confd/run +++ b/base/rootfs/etc/services.d/confd/run @@ -3,5 +3,5 @@ set -e # Only run the service if explicitly told to do so. if [[ "${CONFD_ENABLE_SERVICE}" == "true" ]]; then - confd-render-templates.sh -- -interval ${CONFD_POLLING_INTERVAL} + exec confd-render-templates.sh -- -interval ${CONFD_POLLING_INTERVAL} fi \ No newline at end of file diff --git a/base/rootfs/usr/local/bin/confd-render-templates.sh b/base/rootfs/usr/local/bin/confd-render-templates.sh index cd0b0249..ee6c0a1c 100755 --- a/base/rootfs/usr/local/bin/confd-render-templates.sh +++ b/base/rootfs/usr/local/bin/confd-render-templates.sh @@ -95,6 +95,6 @@ function main { ;; esac - confd ${args} "${OPTIONS[@]}" + exec confd ${args} "${OPTIONS[@]}" } main diff --git a/base/rootfs/usr/local/share/s6/finish b/base/rootfs/usr/local/share/s6/finish new file mode 100644 index 00000000..6ce32498 --- /dev/null +++ b/base/rootfs/usr/local/share/s6/finish @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -e + +readonly SERVICE_DIR="$( cd "$( dirname "${BASH_SOURCE[1]}" )" &> /dev/null && pwd )" +readonly SERVICE=$(basename ${SERVICE_DIR}) + +# Handles exit codes / signals to ensure the container exits with the expected +# value. Meant to be sourced in /etc/service.d/*/finish scripts. +# See https://skarnet.org/software/s6/s6-supervise.html + +# Process received a non-catchable signal (i.e. SIGINT). s6 sets the exit +# code to >= 256 and expects the user to inspect the signal value instead. +# Though it is possible for the service to recieve a signal directly and exit +# with a exit code that indicates it exited due to receiving a signal. +if test ${1} -gt 255; then + readonly EXIT_CODE=$(s6-expr 128 + ${2}) + readonly SIGNAL=${2} +elif test ${1} -gt 128; then + readonly EXIT_CODE=${1} + readonly SIGNAL=$(s6-expr ${1} - 128) +else + readonly EXIT_CODE=${1} +fi + +echo "[services.d] service ${SERVICE} finish: executing..." >&2 + +# Report the exit code / signal and exit. +if test -z "$SIGNAL"; then + echo ${EXIT_CODE} > /var/run/s6/env-stage3/S6_STAGE2_EXITED + echo "[services.d] service ${SERVICE} exiting with exit code: ${EXIT_CODE}" >&2 +else + if test ${SIGNAL} -eq 15; then + # Process received a SIGTERM. Shutdown gracefully and do not set exit code. + echo "[services.d] service ${SERVICE} received SIGTERM exiting gracefully" >&2 + else + echo ${EXIT_CODE} > /var/run/s6/env-stage3/S6_STAGE2_EXITED + echo "[services.d] service ${SERVICE} received signal: ${SIGNAL}, exiting with exit code: ${EXIT_CODE}" >&2 + fi +fi + +# Regardless take down all other services. +s6-svscanctl -t /var/run/s6/services 2>/dev/null \ No newline at end of file diff --git a/base/tests/EnvironmentOverrideDatabase/build.gradle.kts b/base/tests/EnvironmentOverrideDatabase/build.gradle.kts new file mode 100644 index 00000000..975756eb --- /dev/null +++ b/base/tests/EnvironmentOverrideDatabase/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.DockerComposeTest +tasks.register("test") \ No newline at end of file diff --git a/base/tests/EnvironmentOverrideDatabase/docker-compose.yml b/base/tests/EnvironmentOverrideDatabase/docker-compose.yml new file mode 100644 index 00000000..415602b4 --- /dev/null +++ b/base/tests/EnvironmentOverrideDatabase/docker-compose.yml @@ -0,0 +1,26 @@ +# file: docker-compose.yml +# +# Tests that the base values for database environment variables can be +# overridden by prefixing them. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + base: + # Allow downstream container to override `DB` environment variables. + environment: + TEST_DB_DRIVER: "postgresql" + TEST_DB_MYSQL_HOST: "DB_MYSQL_HOST override" + TEST_DB_MYSQL_PORT: "DB_MYSQL_PORT override" + TEST_DB_NAME: "DB_NAME override" + TEST_DB_PASSWORD: "DB_PASSWORD override" + TEST_DB_POSTGRESQL_HOST: "DB_POSTGRESQL_HOST override" + TEST_DB_POSTGRESQL_PORT: "DB_POSTGRESQL_PORT override" + TEST_DB_ROOT_PASSWORD: "DB_ROOT_PASSWORD override" + TEST_DB_ROOT_USER: "DB_ROOT_USER override" + TEST_DB_USER: "DB_USER override" + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/base:latest} \ No newline at end of file diff --git a/base/tests/EnvironmentOverrideDatabase/test.sh b/base/tests/EnvironmentOverrideDatabase/test.sh new file mode 100755 index 00000000..8b277692 --- /dev/null +++ b/base/tests/EnvironmentOverrideDatabase/test.sh @@ -0,0 +1,18 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +# Checks that all `DB` environment variables can be overriden. +expect "DB_DRIVER" "postgresql" +expect "DB_MYSQL_HOST" "DB_MYSQL_HOST override" +expect "DB_MYSQL_PORT" "DB_MYSQL_PORT override" +expect "DB_NAME" "DB_NAME override" +expect "DB_PASSWORD" "DB_PASSWORD override" +expect "DB_POSTGRESQL_HOST" "DB_POSTGRESQL_HOST override" +expect "DB_POSTGRESQL_PORT" "DB_POSTGRESQL_PORT override" +expect "DB_ROOT_PASSWORD" "DB_ROOT_PASSWORD override" +expect "DB_ROOT_USER" "DB_ROOT_USER override" +expect "DB_USER" "DB_USER override" + +# All tests were successful +exit 0 diff --git a/base/tests/EnvironmentPrecedence/build.gradle.kts b/base/tests/EnvironmentPrecedence/build.gradle.kts new file mode 100644 index 00000000..c184f19a --- /dev/null +++ b/base/tests/EnvironmentPrecedence/build.gradle.kts @@ -0,0 +1,22 @@ +@file:Suppress("UnstableApiUsage") + +import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform +import java.io.ByteArrayOutputStream +import java.time.Duration.ofSeconds +import tasks.DockerCompose +import java.lang.RuntimeException + +tasks.register("test") { + val arch = DefaultNativePlatform.getCurrentArchitecture()!! + timeout.convention(ofSeconds(30)) + environment.put("ETCD_TAG", if (arch.isArm) "gcr.io/etcd-development/etcd:v3.4.15-arm64" else "gcr.io/etcd-development/etcd:v3.4.15") + doFirst { + setUp() + // Populate etcd before starting other containers. + up("-d", "etcd") + exec("-T", "etcd", "sh", "/populate-etcd.sh") + up( "--abort-on-container-exit") // Run test.sh as a CMD service and blocking until completion or failure. + tearDown() + checkExitCodes(0L) + } +} \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/defaults/DB_NAME b/base/tests/EnvironmentPrecedence/defaults/DB_NAME new file mode 100644 index 00000000..349a0a98 --- /dev/null +++ b/base/tests/EnvironmentPrecedence/defaults/DB_NAME @@ -0,0 +1 @@ +DB_NAME /etc/defaults value \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/defaults/DB_PASSWORD b/base/tests/EnvironmentPrecedence/defaults/DB_PASSWORD new file mode 100644 index 00000000..bfc6d51f --- /dev/null +++ b/base/tests/EnvironmentPrecedence/defaults/DB_PASSWORD @@ -0,0 +1 @@ +DB_PASSWORD /etc/defaults value \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/defaults/DB_USER b/base/tests/EnvironmentPrecedence/defaults/DB_USER new file mode 100644 index 00000000..3e83b412 --- /dev/null +++ b/base/tests/EnvironmentPrecedence/defaults/DB_USER @@ -0,0 +1 @@ +DB_USER /etc/defaults value \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/defaults/JWT_ADMIN_TOKEN b/base/tests/EnvironmentPrecedence/defaults/JWT_ADMIN_TOKEN new file mode 100644 index 00000000..50c19516 --- /dev/null +++ b/base/tests/EnvironmentPrecedence/defaults/JWT_ADMIN_TOKEN @@ -0,0 +1 @@ +JWT_ADMIN_TOKEN /etc/defaults value \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/docker-compose.yml b/base/tests/EnvironmentPrecedence/docker-compose.yml new file mode 100644 index 00000000..728b26fc --- /dev/null +++ b/base/tests/EnvironmentPrecedence/docker-compose.yml @@ -0,0 +1,50 @@ +# file: docker-compose.yml +# +# Used for testing environment precedence follows what is expected from: +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +secrets: + # Secrets are #2 on the precedence list. + DB_PASSWORD: + file: "./secrets/DB_PASSWORD" + JWT_ADMIN_TOKEN: + file: "./secrets/JWT_ADMIN_TOKEN" + JWT_PRIVATE_KEY: + file: "./secrets/JWT_PRIVATE_KEY" +services: + # Single node cluster. + etcd: + # etcd uses a different tag for arm64, default to x86_64 have gradle pass in the appropriate tag based on host. + image: ${ETCD_TAG:-gcr.io/etcd-development/etcd:v3.4.15} + environment: + ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2379" + ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379" + volumes: + - ./populate-etcd.sh:/populate-etcd.sh + # etcd will be the confd backend and as such is #1 on the precedence list. + command: > + etcd --data-dir=/data + base: + # Environment variables defined here are #3 on on the precedence list. + # Environment variables specified in the Dockerfile for are #4 on the + # precedence list. Followed by files in /etc/defaults in the image, + # which are #5 on the precedence list + environment: + CONFD_BACKEND: "etcd" + DB_NAME: "DB_NAME passed in value" # Should take precedence. + DB_PASSWORD: "DB_PASSWORD passed in value" # Should be overridden by the secret. + JWT_ADMIN_TOKEN: "JWT_ADMIN_TOKEN passed in value" # Should be overridden by the confd backend. + secrets: + - DB_PASSWORD # Should take precedence. + - JWT_ADMIN_TOKEN # Should be overridden by confd backend. + - JWT_PRIVATE_KEY # Used to test template generation, should take precedence over /etc/defaults. + volumes: + - ./test.sh:/test.sh # Test to run. + - ./defaults/DB_NAME:/etc/defaults/DB_NAME # Should be overridden by the passed in environment variable. + - ./defaults/DB_PASSWORD:/etc/defaults/DB_PASSWORD # Should be overridden by the secret. + - ./defaults/DB_USER:/etc/defaults/DB_USER # Should be overridden by environment variable defined in Dockerfile. + - ./defaults/JWT_ADMIN_TOKEN:/etc/defaults/JWT_ADMIN_TOKEN # Should be overridden by confd backend. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/base:latest} \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/populate-etcd.sh b/base/tests/EnvironmentPrecedence/populate-etcd.sh new file mode 100755 index 00000000..52f83768 --- /dev/null +++ b/base/tests/EnvironmentPrecedence/populate-etcd.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# Wait for etcd to start +while true; do + etcdctl endpoint status >/dev/null 2>&1 + if [ "$?" -eq "0" ]; then + break + fi + sleep 1 +done + +etcdctl put /jwt/admin/token "JWT_ADMIN_TOKEN confd value" \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/secrets/DB_PASSWORD b/base/tests/EnvironmentPrecedence/secrets/DB_PASSWORD new file mode 100644 index 00000000..ab0bcb16 --- /dev/null +++ b/base/tests/EnvironmentPrecedence/secrets/DB_PASSWORD @@ -0,0 +1 @@ +DB_PASSWORD secret value \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/secrets/JWT_ADMIN_TOKEN b/base/tests/EnvironmentPrecedence/secrets/JWT_ADMIN_TOKEN new file mode 100644 index 00000000..48eaba09 --- /dev/null +++ b/base/tests/EnvironmentPrecedence/secrets/JWT_ADMIN_TOKEN @@ -0,0 +1 @@ +JWT_ADMIN_TOKEN secret value \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/secrets/JWT_PRIVATE_KEY b/base/tests/EnvironmentPrecedence/secrets/JWT_PRIVATE_KEY new file mode 100644 index 00000000..d394d070 --- /dev/null +++ b/base/tests/EnvironmentPrecedence/secrets/JWT_PRIVATE_KEY @@ -0,0 +1 @@ +JWT_PRIVATE_KEY secret value \ No newline at end of file diff --git a/base/tests/EnvironmentPrecedence/test.sh b/base/tests/EnvironmentPrecedence/test.sh new file mode 100755 index 00000000..4b8f087c --- /dev/null +++ b/base/tests/EnvironmentPrecedence/test.sh @@ -0,0 +1,32 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +# Check environment variables match expectations otherwise exit non-zero. +# +# For each level we specify an a value for that level and all levels that are +# lower precedence than it. We then check to see that precedence holds as +# expected: +# +# 1. Confd backend (highest) +# 2. Secrets kept in /run/secrets +# 3. Environment variables passed into the container +# 4. Environment variables defined in Dockerfile(s) +# 5. Environment variables defined in the /etc/defaults directory (lowest only used for multiline variables) + +# For ease of reading overridden values follow the format: +# ENV_VAR_NAME="ENV_VAR_NAME SOURCE value" +expect "JWT_ADMIN_TOKEN" "JWT_ADMIN_TOKEN confd value" # Confd backend should take precedence +expect "DB_PASSWORD" "DB_PASSWORD secret value" # Secret should take precedence +expect "DB_NAME" "DB_NAME passed in value" # Environment passed into the container should take precedence +expect "DB_USER" "default" # Environment variables defined in Dockerfile should take precedence +expect "JWT_PUBLIC_KEY" "$(cat /etc/defaults/JWT_PUBLIC_KEY)" # Unspecified /etc/defaults value is used. + +# Check templated output from confd backend matches expectations. +diff /opt/keys/jwt/syn-settings.xml <(cat /etc/confd/templates/syn-settings.xml.tmpl | sed -e "s|{{ getenv \"JWT_ADMIN_TOKEN\" }}|${JWT_ADMIN_TOKEN}|") + +# Check templated output from secrets matches expectations. +diff /opt/keys/jwt/private.key <(echo -n "${JWT_PRIVATE_KEY}") + +# All tests were successful +exit 0 diff --git a/base/tests/ServiceExitCode/build.gradle.kts b/base/tests/ServiceExitCode/build.gradle.kts new file mode 100644 index 00000000..a9768ab2 --- /dev/null +++ b/base/tests/ServiceExitCode/build.gradle.kts @@ -0,0 +1,15 @@ +import tasks.DockerCompose +tasks.register("test") { + doFirst { + setUp() + up("--abort-on-container-exit", ignoreExitValue = true) + tearDown() + // Check if any of the containers exited with the same value as the failed service. + info.get()["base"]!!.let { info -> + val state = info.state + if (state.exitCodeLong != 11L) { + throw RuntimeException("Container base exited with ${state.exitCodeLong} and status ${state.status}.") + } + } + } +} diff --git a/base/tests/ServiceExitCode/docker-compose.yml b/base/tests/ServiceExitCode/docker-compose.yml new file mode 100644 index 00000000..c42c8900 --- /dev/null +++ b/base/tests/ServiceExitCode/docker-compose.yml @@ -0,0 +1,9 @@ +# file: docker-compose.yml +# +# Tests that when a service exits, it's exit code is used as the return exit code for the container. +version: "3.8" +services: + base: + image: ${BASE_IMAGE:-local/base:latest} + volumes: + - ./service:/etc/services.d/test \ No newline at end of file diff --git a/base/tests/ServiceExitCode/service/finish b/base/tests/ServiceExitCode/service/finish new file mode 100755 index 00000000..177a768f --- /dev/null +++ b/base/tests/ServiceExitCode/service/finish @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash +set -e + +source /usr/local/share/s6/finish \ No newline at end of file diff --git a/base/tests/ServiceExitCode/service/run b/base/tests/ServiceExitCode/service/run new file mode 100755 index 00000000..8bcc8a36 --- /dev/null +++ b/base/tests/ServiceExitCode/service/run @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash +set -e + +# Exit code of service should be returned by the container. +exit 11 \ No newline at end of file diff --git a/base/tests/ServiceStartsWithDefaults/build.gradle.kts b/base/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..646c3ddd --- /dev/null +++ b/base/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") diff --git a/base/tests/SigTermExitCode/build.gradle.kts b/base/tests/SigTermExitCode/build.gradle.kts new file mode 100644 index 00000000..305d2175 --- /dev/null +++ b/base/tests/SigTermExitCode/build.gradle.kts @@ -0,0 +1,14 @@ +import tasks.DockerCompose +tasks.register("test") { + doFirst { + setUp() + up("--abort-on-container-exit", ignoreExitValue = true) + tearDown() + info.get()["base"]!!.let { info -> + val state = info.state + if (state.exitCodeLong != 0L) { + throw RuntimeException("Container $name exited with ${state.exitCodeLong} and status ${state.status}.") + } + } + } +} diff --git a/base/tests/SigTermExitCode/docker-compose.yml b/base/tests/SigTermExitCode/docker-compose.yml new file mode 100644 index 00000000..6613d6dc --- /dev/null +++ b/base/tests/SigTermExitCode/docker-compose.yml @@ -0,0 +1,9 @@ +# file: docker-compose.yml +# +# Tests that when a service receives a SIGTERM it exits 0 after cleaning up the running services. +version: "3.8" +services: + base: + image: ${BASE_IMAGE:-local/base:latest} + volumes: + - ./service:/etc/services.d/test \ No newline at end of file diff --git a/base/tests/SigTermExitCode/service/finish b/base/tests/SigTermExitCode/service/finish new file mode 100755 index 00000000..177a768f --- /dev/null +++ b/base/tests/SigTermExitCode/service/finish @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash +set -e + +source /usr/local/share/s6/finish \ No newline at end of file diff --git a/base/tests/SigTermExitCode/service/run b/base/tests/SigTermExitCode/service/run new file mode 100755 index 00000000..88231218 --- /dev/null +++ b/base/tests/SigTermExitCode/service/run @@ -0,0 +1,11 @@ +#!/usr/bin/with-contenv bash +set -e + +{ + # Make sure we stay alive long enough to receive the signal. + sleep 1000 + exit 0 +}& +echo "[services.d] Send SIGTERM to test service." >&2 +s6-svc -t /var/run/s6/services/test +wait \ No newline at end of file diff --git a/base/tests/SignalExitCode/build.gradle.kts b/base/tests/SignalExitCode/build.gradle.kts new file mode 100644 index 00000000..e33a75fd --- /dev/null +++ b/base/tests/SignalExitCode/build.gradle.kts @@ -0,0 +1,14 @@ +import tasks.DockerCompose +tasks.register("test") { + doFirst { + setUp() + up("--abort-on-container-exit", ignoreExitValue = true) + tearDown() // Sends SIGTERM container should recieve it and exit gracefully. + info.get()["base"]!!.let { info -> + val state = info.state + if (state.exitCodeLong != 130L) { // 128 + 2 (SIGINT) == 130 + throw RuntimeException("Container $name exited with ${state.exitCodeLong} and status ${state.status}.") + } + } + } +} diff --git a/base/tests/SignalExitCode/docker-compose.yml b/base/tests/SignalExitCode/docker-compose.yml new file mode 100644 index 00000000..08a12ee9 --- /dev/null +++ b/base/tests/SignalExitCode/docker-compose.yml @@ -0,0 +1,11 @@ +# file: docker-compose.yml +# +# Tests that when a service exits, it's exit code is used as the return exit code for the container. +version: "3.8" +services: + base: + image: ${BASE_IMAGE:-local/base:latest} + command: "/test.sh" + volumes: + - ./service:/etc/services.d/test + - ./test.sh:/test.sh \ No newline at end of file diff --git a/base/tests/SignalExitCode/service/finish b/base/tests/SignalExitCode/service/finish new file mode 100755 index 00000000..177a768f --- /dev/null +++ b/base/tests/SignalExitCode/service/finish @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash +set -e + +source /usr/local/share/s6/finish \ No newline at end of file diff --git a/base/tests/SignalExitCode/service/run b/base/tests/SignalExitCode/service/run new file mode 100755 index 00000000..2ffb0c7d --- /dev/null +++ b/base/tests/SignalExitCode/service/run @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash +set -e + +# Make sure we stay alive long enough to receive the signal. +exec sleep 100000 \ No newline at end of file diff --git a/base/tests/SignalExitCode/test.sh b/base/tests/SignalExitCode/test.sh new file mode 100755 index 00000000..4122ebd7 --- /dev/null +++ b/base/tests/SignalExitCode/test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash + +s6-svc -i /var/run/s6/services/test +echo "[services.d] Send SIGINT to test service." >&2 +sleep 100000 diff --git a/blazegraph/.dockerignore b/blazegraph/.dockerignore index b43bf86b..badf6cb7 100644 --- a/blazegraph/.dockerignore +++ b/blazegraph/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/blazegraph/tests/ServiceStartsWithDefaults/build.gradle.kts b/blazegraph/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..70e8d105 --- /dev/null +++ b/blazegraph/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("org.apache.catalina.startup.Catalina.start Server startup") +} diff --git a/build.gradle.kts b/build.gradle.kts index e8b97174..8a525449 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ buildscript { } } dependencies { - classpath("ca.islandora:isle-gradle-docker-plugin:0.0.4") + classpath("ca.islandora:isle-gradle-docker-plugin:0.0.5") } } diff --git a/cantaloupe/.dockerignore b/cantaloupe/.dockerignore index b43bf86b..badf6cb7 100644 --- a/cantaloupe/.dockerignore +++ b/cantaloupe/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/cantaloupe/tests/ServiceStartsWithDefaults/build.gradle.kts b/cantaloupe/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..70e8d105 --- /dev/null +++ b/cantaloupe/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("org.apache.catalina.startup.Catalina.start Server startup") +} diff --git a/crayfish/.dockerignore b/crayfish/.dockerignore index b43bf86b..badf6cb7 100644 --- a/crayfish/.dockerignore +++ b/crayfish/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/crayfish/tests/ServiceStartsWithDefaults/build.gradle.kts b/crayfish/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/crayfish/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/crayfits/.dockerignore b/crayfits/.dockerignore index b43bf86b..badf6cb7 100644 --- a/crayfits/.dockerignore +++ b/crayfits/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/crayfits/tests/ServiceStartsWithDefaults/build.gradle.kts b/crayfits/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/crayfits/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/demo/.dockerignore b/demo/.dockerignore index b43bf86b..badf6cb7 100644 --- a/demo/.dockerignore +++ b/demo/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/demo/tests/ServiceStartsWithDefaults/build.gradle.kts b/demo/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..646c3ddd --- /dev/null +++ b/demo/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") diff --git a/drupal/.dockerignore b/drupal/.dockerignore index b43bf86b..badf6cb7 100644 --- a/drupal/.dockerignore +++ b/drupal/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/drupal/rootfs/usr/local/bin/install-drupal-site.sh b/drupal/rootfs/usr/local/bin/install-drupal-site.sh index d1892e2c..f458d302 100755 --- a/drupal/rootfs/usr/local/bin/install-drupal-site.sh +++ b/drupal/rootfs/usr/local/bin/install-drupal-site.sh @@ -155,15 +155,18 @@ function installed { function main { cmdline ${ARGS} - + local protocol=mysql if installed; then echo "Site already is installed." return 0 fi + if [[ "${DRIVER}" == "postgresql" ]]; then + protocol=pgsql + fi echo "Installing site." drush \ -n \ si ${DRUSH_ARGS} \ - --db-url="${DRIVER}://${DB_USER}:${DB_PASSWORD}@${HOST}:${PORT}/${DB_NAME}" + --db-url="${protocol}://${DB_USER}:${DB_PASSWORD}@${HOST}:${PORT}/${DB_NAME}" } main diff --git a/drupal/tests/ServiceStartsWithBackendMySQL/build.gradle.kts b/drupal/tests/ServiceStartsWithBackendMySQL/build.gradle.kts new file mode 100644 index 00000000..be9d4595 --- /dev/null +++ b/drupal/tests/ServiceStartsWithBackendMySQL/build.gradle.kts @@ -0,0 +1,6 @@ +import java.time.Duration.ofMinutes +import tasks.tests.DockerComposeTest + +tasks.register("test") { + timeout.convention(ofMinutes(10)) +} \ No newline at end of file diff --git a/drupal/tests/ServiceStartsWithBackendMySQL/docker-compose.yml b/drupal/tests/ServiceStartsWithBackendMySQL/docker-compose.yml new file mode 100644 index 00000000..0cae8752 --- /dev/null +++ b/drupal/tests/ServiceStartsWithBackendMySQL/docker-compose.yml @@ -0,0 +1,15 @@ +# file: docker-compose.yml +# +# Tests that we can bring up the demo site. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + drupal: + image: ${DRUPAL_IMAGE:-local/drupal:latest} + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + mariadb: + image: ${DATABASE_IMAGE:-local/mariadb:latest} \ No newline at end of file diff --git a/drupal/tests/ServiceStartsWithBackendMySQL/test.sh b/drupal/tests/ServiceStartsWithBackendMySQL/test.sh new file mode 100755 index 00000000..b6c912b3 --- /dev/null +++ b/drupal/tests/ServiceStartsWithBackendMySQL/test.sh @@ -0,0 +1,33 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +# Install basic Drupal +rm -fr /var/www/drupal/* +composer create-project drupal/recommended-project:^9.1 \ + --prefer-dist \ + --no-interaction \ + --stability stable \ + --no-dev \ + -- /var/www/drupal + +# Install Drush. +(cd /var/www/drupal && composer require drush/drush:^10.0) + +# Install actual site. +source /etc/islandora/utilities.sh +mkdir -p /var/www/drupal/web/sites/default/files +chown -R nginx:nginx /var/www/drupal +create_database "DEFAULT" +install_site "DEFAULT" + +# Exit non-zero if database does not exist. +cat <<- EOF | execute-sql-file.sh +use ${DB_NAME} +EOF + +# Wait for Drupal to start. +wait_20x http://localhost:80/user + +# All tests were successful +exit 0 \ No newline at end of file diff --git a/drupal/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts b/drupal/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts new file mode 100644 index 00000000..be9d4595 --- /dev/null +++ b/drupal/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts @@ -0,0 +1,6 @@ +import java.time.Duration.ofMinutes +import tasks.tests.DockerComposeTest + +tasks.register("test") { + timeout.convention(ofMinutes(10)) +} \ No newline at end of file diff --git a/drupal/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml b/drupal/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml new file mode 100644 index 00000000..59e3d4f5 --- /dev/null +++ b/drupal/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml @@ -0,0 +1,17 @@ +# file: docker-compose.yml +# +# Tests that we can bring up the demo site. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + drupal: + image: ${DRUPAL_IMAGE:-local/drupal:latest} + environment: + DRUPAL_DEFAULT_DB_DRIVER: postgresql + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - bash # /test.sh # Run test and exit. + postgresql: + image: ${MYSQL_IMAGE:-local/postgresql:latest} \ No newline at end of file diff --git a/drupal/tests/ServiceStartsWithBackendPostgreSQL/test.sh b/drupal/tests/ServiceStartsWithBackendPostgreSQL/test.sh new file mode 100755 index 00000000..150c6569 --- /dev/null +++ b/drupal/tests/ServiceStartsWithBackendPostgreSQL/test.sh @@ -0,0 +1,36 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +# Install basic Drupal +rm -fr /var/www/drupal/* +composer create-project drupal/recommended-project:^9.1 \ + --prefer-dist \ + --no-interaction \ + --stability stable \ + --no-dev \ + -- /var/www/drupal + +# Install Drush. +(cd /var/www/drupal && composer require drush/drush:^10.0) + +# Install actual site. +source /etc/islandora/utilities.sh +mkdir -p /var/www/drupal/web/sites/default/files +chown -R nginx:nginx /var/www/drupal +create_database "DEFAULT" +install_site "DEFAULT" + +# Exit non-zero if database does not exist. +PGPASSWORD="${DB_ROOT_PASSWORD}" psql \ + --host="${DB_HOST}" \ + --port="${DB_PORT}" \ + --username="${DB_ROOT_USER}" \ + --dbname="drupal_default" \ + -c "\q" + +# Wait for Drupal to start. +wait_20x http://localhost:80/user + +# All tests were successful +exit 0 \ No newline at end of file diff --git a/drupal/tests/ServiceStartsWithDefaults/build.gradle.kts b/drupal/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..646c3ddd --- /dev/null +++ b/drupal/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") diff --git a/fcrepo/.dockerignore b/fcrepo/.dockerignore index b43bf86b..badf6cb7 100644 --- a/fcrepo/.dockerignore +++ b/fcrepo/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/fcrepo/Dockerfile b/fcrepo/Dockerfile index 525432ab..4d7aaf92 100644 --- a/fcrepo/Dockerfile +++ b/fcrepo/Dockerfile @@ -33,7 +33,9 @@ RUN mkdir -p /data && \ ENV \ FCREPO_ACTIVEMQ_BROKER=tcp://activemq:61616 \ + FCREPO_ACTIVEMQ_QUEUE_ENABLE=false \ FCREPO_ACTIVEMQ_QUEUE=fedora \ + FCREPO_ACTIVEMQ_TOPIC_ENABLE=true \ FCREPO_ACTIVEMQ_TOPIC=fedora \ FCREPO_BINARYSTORAGE_TYPE=file \ FCREPO_DB_NAME=fcrepo \ diff --git a/fcrepo/tests/ServiceStartsWithBackendMySQL/build.gradle.kts b/fcrepo/tests/ServiceStartsWithBackendMySQL/build.gradle.kts new file mode 100644 index 00000000..975756eb --- /dev/null +++ b/fcrepo/tests/ServiceStartsWithBackendMySQL/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.DockerComposeTest +tasks.register("test") \ No newline at end of file diff --git a/fcrepo/tests/ServiceStartsWithBackendMySQL/docker-compose.yml b/fcrepo/tests/ServiceStartsWithBackendMySQL/docker-compose.yml new file mode 100644 index 00000000..99add84a --- /dev/null +++ b/fcrepo/tests/ServiceStartsWithBackendMySQL/docker-compose.yml @@ -0,0 +1,18 @@ +# file: docker-compose.yml +# +# Tests that the base values for database environment variables can be +# overridden by prefixing them. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + mariadb: + image: ${MYSQL_IMAGE:-local/mariadb:latest} + fcrepo: + environment: + FCREPO_PERSISTENCE_TYPE: mysql + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/fcrepo:latest} \ No newline at end of file diff --git a/fcrepo/tests/ServiceStartsWithBackendMySQL/test.sh b/fcrepo/tests/ServiceStartsWithBackendMySQL/test.sh new file mode 100755 index 00000000..1df036d8 --- /dev/null +++ b/fcrepo/tests/ServiceStartsWithBackendMySQL/test.sh @@ -0,0 +1,33 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +function count { + cat <<- EOF | execute-sql-file.sh --database "fcrepo" - -- -N 2>/dev/null +SELECT COUNT(ID) as count FROM MODESHAPE_REPOSITORY; +EOF +} + +# Wait for fcrepo to start. +wait_20x http://localhost:8080/fcrepo/rest + +# Add some content. +old_count=$(count) +echo "Old Count: ${old_count}" +object=$(curl --fail -X POST -H "Authorization: Bearer islandora" -H "Content-Type:text/plain" "http://localhost/fcrepo/rest" 2>/dev/null) +echo "Create Object: $object" + +# Check that the database has been modified. +new_count=$(count) +echo "New Count: ${new_count}" + +# Check if results meet expectations. +if [[ "${new_count}" -gt "${old_count}" ]]; then + echo "Database was modified." +else + echo "Database was not modified." + exit 1 +fi + +# All tests were successful +exit 0 \ No newline at end of file diff --git a/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts b/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts new file mode 100644 index 00000000..975756eb --- /dev/null +++ b/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.DockerComposeTest +tasks.register("test") \ No newline at end of file diff --git a/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml b/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml new file mode 100644 index 00000000..9658653c --- /dev/null +++ b/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml @@ -0,0 +1,19 @@ +# file: docker-compose.yml +# +# Tests that the base values for database environment variables can be +# overridden by prefixing them. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + postgresql: + image: ${MYSQL_IMAGE:-local/postgresql:latest} + fcrepo: + # Allow downstream container to override `DB` environment variables. + environment: + FCREPO_PERSISTENCE_TYPE: postgresql + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/fcrepo:latest} \ No newline at end of file diff --git a/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/test.sh b/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/test.sh new file mode 100755 index 00000000..c18658df --- /dev/null +++ b/fcrepo/tests/ServiceStartsWithBackendPostgreSQL/test.sh @@ -0,0 +1,33 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +function count { + cat </dev/null) +echo "Create Object: $object" + +# Check that the database has been modified. +new_count=$(count) +echo "New Count: ${new_count}" + +# Check if results meet expectations. +if [[ "${new_count}" -gt "${old_count}" ]]; then + echo "Database was modified." +else + echo "Database was not modified." + exit 1 +fi + +# All tests were successful +exit 0 \ No newline at end of file diff --git a/fcrepo/tests/ServiceStartsWithDefaults/build.gradle.kts b/fcrepo/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..70e8d105 --- /dev/null +++ b/fcrepo/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("org.apache.catalina.startup.Catalina.start Server startup") +} diff --git a/fcrepo6/.dockerignore b/fcrepo6/.dockerignore index b43bf86b..badf6cb7 100644 --- a/fcrepo6/.dockerignore +++ b/fcrepo6/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/fits/.dockerignore b/fits/.dockerignore index b43bf86b..badf6cb7 100644 --- a/fits/.dockerignore +++ b/fits/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/fits/tests/ServiceStartsWithDefaults/build.gradle.kts b/fits/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..70e8d105 --- /dev/null +++ b/fits/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("org.apache.catalina.startup.Catalina.start Server startup") +} diff --git a/gradle.properties b/gradle.properties index f14e7e47..3c1027e1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,6 +3,12 @@ org.gradle.caching=true org.gradle.parallel=true +# ============================================================================== +# CI properties. +# Set to true if building in an Continous Integration environment, +# To reduce space used since the environment is ephemeral. +isCI=false + # ============================================================================== # Docker properties. # See https://github.com/docker/buildx#documentation for in-depth explaination. @@ -20,6 +26,11 @@ docker.tags=latest # *not* support multi-arch builds. docker.driver=docker +# Conditionally allows pushing when `docker.driver` is set to `docker`. If we +# are building with "docker-container" or "kubernetes" we must push as we need +# to be able to pull from from the registry when building downstream images. +docker.push=false + # Enable caching from/to remote/inline cache. # These flags are forced to "false" if `docker.noCache` is set to "true". # Defaults to the following depending on `docker.driver`: diff --git a/handle/rootfs/etc/services.d/handle/finish b/handle/rootfs/etc/services.d/handle/finish index 9030da41..964ffe33 100644 --- a/handle/rootfs/etc/services.d/handle/finish +++ b/handle/rootfs/etc/services.d/handle/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh: -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/handle/rootfs/etc/services.d/handle/run b/handle/rootfs/etc/services.d/handle/run index a9bcc32b..a3114f57 100644 --- a/handle/rootfs/etc/services.d/handle/run +++ b/handle/rootfs/etc/services.d/handle/run @@ -1,5 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh: -s6-setuidgid handle -/opt/handle/bin/hdl-server /var/handle +#!/usr/bin/env bash +set -e +exec s6-setuidgid handle /opt/handle/bin/hdl-server /var/handle diff --git a/handle/tests/ServiceStartsWithBackendMySQL/build.gradle.kts b/handle/tests/ServiceStartsWithBackendMySQL/build.gradle.kts new file mode 100644 index 00000000..975756eb --- /dev/null +++ b/handle/tests/ServiceStartsWithBackendMySQL/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.DockerComposeTest +tasks.register("test") \ No newline at end of file diff --git a/handle/tests/ServiceStartsWithBackendMySQL/docker-compose.yml b/handle/tests/ServiceStartsWithBackendMySQL/docker-compose.yml new file mode 100644 index 00000000..bd628bbd --- /dev/null +++ b/handle/tests/ServiceStartsWithBackendMySQL/docker-compose.yml @@ -0,0 +1,18 @@ +# file: docker-compose.yml +# +# Tests that the base values for database environment variables can be +# overridden by prefixing them. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + mariadb: + image: ${MYSQL_IMAGE:-local/mariadb:latest} + handle: + environment: + HANDLE_PERSISTENCE_TYPE: mysql + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/handle:latest} \ No newline at end of file diff --git a/handle/tests/ServiceStartsWithBackendMySQL/test.sh b/handle/tests/ServiceStartsWithBackendMySQL/test.sh new file mode 100755 index 00000000..a772bbe4 --- /dev/null +++ b/handle/tests/ServiceStartsWithBackendMySQL/test.sh @@ -0,0 +1,11 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +# Exit non-zero if database does not exist. +cat <<- EOF | execute-sql-file.sh +use ${DB_NAME} +EOF + +# All tests were successful +exit 0 diff --git a/handle/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts b/handle/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts new file mode 100644 index 00000000..975756eb --- /dev/null +++ b/handle/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.DockerComposeTest +tasks.register("test") \ No newline at end of file diff --git a/handle/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml b/handle/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml new file mode 100644 index 00000000..95c7d6c4 --- /dev/null +++ b/handle/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml @@ -0,0 +1,18 @@ +# file: docker-compose.yml +# +# Tests that the base values for database environment variables can be +# overridden by prefixing them. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + postgresql: + image: ${MYSQL_IMAGE:-local/postgresql:latest} + handle: + environment: + HANDLE_PERSISTENCE_TYPE: postgresql + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/handle:latest} \ No newline at end of file diff --git a/handle/tests/ServiceStartsWithBackendPostgreSQL/test.sh b/handle/tests/ServiceStartsWithBackendPostgreSQL/test.sh new file mode 100755 index 00000000..51c74d9b --- /dev/null +++ b/handle/tests/ServiceStartsWithBackendPostgreSQL/test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh +count=$(execute-sql-file.sh <(echo "SELECT 1 FROM pg_database WHERE datname='handle'") -- --csv -t) + +if [[ "${count}" -eq "1" ]]; then + echo "Database exists." +else + echo "Database missing." + exit 1 +fi + +# All tests were successful +exit 0 diff --git a/handle/tests/ServiceStartsWithDefaults/build.gradle.kts b/handle/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..a1e069e3 --- /dev/null +++ b/handle/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,5 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + // Uses `bdbje` backend by default. + waitForMessage.set("INFO org.eclipse.jetty.server.Server - Started") +} diff --git a/homarus/.dockerignore b/homarus/.dockerignore index b43bf86b..badf6cb7 100644 --- a/homarus/.dockerignore +++ b/homarus/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/homarus/tests/ServiceStartsWithDefaults/build.gradle.kts b/homarus/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/homarus/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/houdini/.dockerignore b/houdini/.dockerignore index b43bf86b..badf6cb7 100644 --- a/houdini/.dockerignore +++ b/houdini/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/houdini/tests/ServiceStartsWithDefaults/build.gradle.kts b/houdini/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/houdini/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/hypercube/.dockerignore b/hypercube/.dockerignore index b43bf86b..badf6cb7 100644 --- a/hypercube/.dockerignore +++ b/hypercube/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/hypercube/tests/ServiceStartsWithDefaults/build.gradle.kts b/hypercube/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/hypercube/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/imagemagick/.dockerignore b/imagemagick/.dockerignore index b43bf86b..badf6cb7 100644 --- a/imagemagick/.dockerignore +++ b/imagemagick/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/java/.dockerignore b/java/.dockerignore index b43bf86b..badf6cb7 100644 --- a/java/.dockerignore +++ b/java/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/java/tests/ServiceStartsWithDefaults/build.gradle.kts b/java/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..646c3ddd --- /dev/null +++ b/java/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") diff --git a/karaf/.dockerignore b/karaf/.dockerignore index 42061c01..badf6cb7 100644 --- a/karaf/.dockerignore +++ b/karaf/.dockerignore @@ -1 +1,5 @@ -README.md \ No newline at end of file +build.gradle.kts +Dockerfile +README.md +tests +tests/**/* \ No newline at end of file diff --git a/karaf/rootfs/etc/services.d/karaf/finish b/karaf/rootfs/etc/services.d/karaf/finish index f8984dd3..964ffe33 100644 --- a/karaf/rootfs/etc/services.d/karaf/finish +++ b/karaf/rootfs/etc/services.d/karaf/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh : -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/karaf/rootfs/etc/services.d/karaf/run b/karaf/rootfs/etc/services.d/karaf/run index d1607dc7..2b69c114 100644 --- a/karaf/rootfs/etc/services.d/karaf/run +++ b/karaf/rootfs/etc/services.d/karaf/run @@ -1,6 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh : -with-contenv -s6-setuidgid karaf -/opt/karaf/bin/karaf server +#!/usr/bin/env bash +set -e +exec s6-setuidgid karaf /opt/karaf/bin/karaf server diff --git a/karaf/tests/ServiceStartsWithDefaults/build.gradle.kts b/karaf/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..7cf7dc7b --- /dev/null +++ b/karaf/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("org.apache.karaf.features.core - 4.0.8 | Done") +} diff --git a/mariadb/.dockerignore b/mariadb/.dockerignore index b43bf86b..badf6cb7 100644 --- a/mariadb/.dockerignore +++ b/mariadb/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/mariadb/rootfs/etc/services.d/mysqld/finish b/mariadb/rootfs/etc/services.d/mysqld/finish index 32f06197..964ffe33 100644 --- a/mariadb/rootfs/etc/services.d/mysqld/finish +++ b/mariadb/rootfs/etc/services.d/mysqld/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S0 -# -*- mode: sh -*- -# vi: set ft=sh: -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/mariadb/rootfs/etc/services.d/mysqld/run b/mariadb/rootfs/etc/services.d/mysqld/run index 0547bbee..692fc2f6 100644 --- a/mariadb/rootfs/etc/services.d/mysqld/run +++ b/mariadb/rootfs/etc/services.d/mysqld/run @@ -1,5 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh: -s6-setuidgid mysql -mysqld --user mysql +#!/usr/bin/env bash +set -e +exec s6-setuidgid mysql mysqld --user mysql diff --git a/mariadb/tests/ServiceStartsWithDefaults/build.gradle.kts b/mariadb/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..7e335fcf --- /dev/null +++ b/mariadb/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("mysqld: ready for connections.") +} diff --git a/matomo/.dockerignore b/matomo/.dockerignore index b43bf86b..badf6cb7 100644 --- a/matomo/.dockerignore +++ b/matomo/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/matomo/tests/ServiceStartsWithDefaults/build.gradle.kts b/matomo/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..975756eb --- /dev/null +++ b/matomo/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.DockerComposeTest +tasks.register("test") \ No newline at end of file diff --git a/matomo/tests/ServiceStartsWithDefaults/docker-compose.yml b/matomo/tests/ServiceStartsWithDefaults/docker-compose.yml new file mode 100644 index 00000000..4bab2681 --- /dev/null +++ b/matomo/tests/ServiceStartsWithDefaults/docker-compose.yml @@ -0,0 +1,20 @@ +# file: docker-compose.yml +# +# Tests that the base values for database environment variables can be +# overridden by prefixing them. +# +# `base/rootfs/etc/cont-init.d/00-container-environment-00-init.sh` +version: "3.8" +services: + mariadb: + image: ${MYSQL_IMAGE:-local/mariadb:latest} + matomo: + image: ${BASE_IMAGE:-local/matomo:latest} + environment: + # Testing without traefik, so we do not have SSL. + MATOMO_FORCE_SSL: 0 + MATOMO_ASSUME_SECURE_PROTOCOL: 0 + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. \ No newline at end of file diff --git a/matomo/tests/ServiceStartsWithDefaults/test.sh b/matomo/tests/ServiceStartsWithDefaults/test.sh new file mode 100755 index 00000000..03b634fd --- /dev/null +++ b/matomo/tests/ServiceStartsWithDefaults/test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +# Exit non-zero if database does not exist. +cat <<- EOF | execute-sql-file.sh +use ${DB_NAME} +EOF + +# Wait for access +wait_20x http://localhost:80/index.php + +# All tests were successful +exit 0 diff --git a/milliner/.dockerignore b/milliner/.dockerignore index b43bf86b..badf6cb7 100644 --- a/milliner/.dockerignore +++ b/milliner/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/milliner/tests/ServiceStartsWithDefaults/build.gradle.kts b/milliner/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/milliner/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/nginx/.dockerignore b/nginx/.dockerignore index b43bf86b..badf6cb7 100644 --- a/nginx/.dockerignore +++ b/nginx/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/nginx/rootfs/etc/services.d/fpm/finish b/nginx/rootfs/etc/services.d/fpm/finish index 9030da41..964ffe33 100644 --- a/nginx/rootfs/etc/services.d/fpm/finish +++ b/nginx/rootfs/etc/services.d/fpm/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh: -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/nginx/rootfs/etc/services.d/fpm/run b/nginx/rootfs/etc/services.d/fpm/run index a3805cfa..14a2a919 100644 --- a/nginx/rootfs/etc/services.d/fpm/run +++ b/nginx/rootfs/etc/services.d/fpm/run @@ -1,4 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh: -/usr/sbin/php-fpm7 --pid /var/run/php-fpm7/php-fpm7.pid --prefix /var/run/php-fpm7 --fpm-config /etc/php7/php-fpm.conf +#!/usr/bin/env bash +set -e +exec /usr/sbin/php-fpm7 --pid /var/run/php-fpm7/php-fpm7.pid --prefix /var/run/php-fpm7 --fpm-config /etc/php7/php-fpm.conf diff --git a/nginx/rootfs/etc/services.d/nginx/finish b/nginx/rootfs/etc/services.d/nginx/finish index 9030da41..964ffe33 100644 --- a/nginx/rootfs/etc/services.d/nginx/finish +++ b/nginx/rootfs/etc/services.d/nginx/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh: -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/nginx/rootfs/etc/services.d/nginx/run b/nginx/rootfs/etc/services.d/nginx/run index 7503678e..4d500b4a 100644 --- a/nginx/rootfs/etc/services.d/nginx/run +++ b/nginx/rootfs/etc/services.d/nginx/run @@ -1,4 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh: -/usr/sbin/nginx +#!/usr/bin/env bash +set -e +exec /usr/sbin/nginx diff --git a/nginx/tests/ServiceStartsWithDefaults/build.gradle.kts b/nginx/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/nginx/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/postgresql/.dockerignore b/postgresql/.dockerignore index b43bf86b..badf6cb7 100644 --- a/postgresql/.dockerignore +++ b/postgresql/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/postgresql/rootfs/etc/services.d/mysqld/finish b/postgresql/rootfs/etc/services.d/mysqld/finish deleted file mode 100644 index 32f06197..00000000 --- a/postgresql/rootfs/etc/services.d/mysqld/finish +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/execlineb -S0 -# -*- mode: sh -*- -# vi: set ft=sh: -s6-svscanctl -t /var/run/s6/services diff --git a/postgresql/rootfs/etc/services.d/mysqld/run b/postgresql/rootfs/etc/services.d/mysqld/run deleted file mode 100644 index e117c7b7..00000000 --- a/postgresql/rootfs/etc/services.d/mysqld/run +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh: -with-contenv -importas -i PGDATA PGDATA -s6-setuidgid postgres -postgres diff --git a/postgresql/rootfs/etc/services.d/postgres/finish b/postgresql/rootfs/etc/services.d/postgres/finish new file mode 100644 index 00000000..964ffe33 --- /dev/null +++ b/postgresql/rootfs/etc/services.d/postgres/finish @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/postgresql/rootfs/etc/services.d/postgres/run b/postgresql/rootfs/etc/services.d/postgres/run new file mode 100644 index 00000000..e62a7fd5 --- /dev/null +++ b/postgresql/rootfs/etc/services.d/postgres/run @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e +exec with-contenv \ + importas -i PGDATA PGDATA \ + s6-setuidgid \ + postgres postgres diff --git a/postgresql/tests/ServiceStartsWithDefaults/build.gradle.kts b/postgresql/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..0c3c86eb --- /dev/null +++ b/postgresql/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("database system is ready to accept connections") +} \ No newline at end of file diff --git a/recast/.dockerignore b/recast/.dockerignore index b43bf86b..badf6cb7 100644 --- a/recast/.dockerignore +++ b/recast/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/recast/tests/ServiceStartsWithDefaults/build.gradle.kts b/recast/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..5e972e0e --- /dev/null +++ b/recast/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/riprap/.dockerignore b/riprap/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/riprap/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/riprap/Dockerfile b/riprap/Dockerfile new file mode 100644 index 00000000..52d6fe9c --- /dev/null +++ b/riprap/Dockerfile @@ -0,0 +1,79 @@ +# syntax=docker/dockerfile:1.2.1 +ARG repository=local +ARG tag=latest +FROM --platform=$BUILDPLATFORM ${repository}/composer:${tag} AS composer + +RUN --mount=type=cache,id=riprap-composer,sharing=locked,target=/root/.composer/cache \ + --mount=type=cache,id=riprap-downloads,sharing=locked,target=/opt/downloads \ + COMMIT=7d7cae2d09dec20caa3c3f5752434af32401819e && \ + git-clone-cached.sh \ + --url https://github.com/mjordan/riprap.git \ + --cache-dir "${DOWNLOAD_CACHE_DIRECTORY}" \ + --commit "${COMMIT}" \ + --worktree /var/www/riprap && \ + composer install -d /var/www/riprap --no-dev + +FROM alpine:3.13.2 AS cache +FROM ${repository}/nginx:${tag} + +EXPOSE 8000 + +RUN --mount=type=cache,id=riprap-apk,sharing=locked,from=cache,target=/var/cache/apk \ + apk add php7-pdo_sqlite && \ + cleanup.sh + +# The driver is given explicitly as Rip Rap can be run on SQLite without +# further configuration. +ENV \ + RIPRAP_APP_ENV=dev \ + RIPRAP_APP_SECRET=f58c87e1d737c4422b45ba4310abede6 \ + RIPRAP_CROND_ENABLE_SERVICE=true \ + RIPRAP_CROND_LOG_LEVEL=8 \ + RIPRAP_CROND_SCHEDULE="0 0 1 * *" \ + RIPRAP_DB_DRIVER=sqlite \ + RIPRAP_DB_NAME=riprap \ + RIPRAP_DB_PASSWORD=password \ + RIPRAP_DB_USER=riprap \ + RIPRAP_LOG_LEVEL=debug \ + RIPRAP_MAILER_URL=null://localhost \ + RIPRAP_TRUSTED_HOSTS="" \ + RIPRAP_TRUSTED_PROXIES="" + +# Configuration specific to check fixity command: +ENV \ + RIPRAP_CONFIG_DIGEST_COMMAND=/usr/bin/sha1sum \ + RIPRAP_CONFIG_DRUPAL_BASEURL=https://islandora.traefik.me \ + RIPRAP_CONFIG_DRUPAL_CONTENT_TYPES="['islandora_object']" \ + RIPRAP_CONFIG_DRUPAL_FILE_FIELDNAMES="['field_media_audio', 'field_media_document', 'field_edited_text', 'field_media_file', 'field_media_image', 'field_media_video_file']" \ + RIPRAP_CONFIG_DRUPAL_MEDIA_AUTH="['admin', 'islandora']" \ + RIPRAP_CONFIG_DRUPAL_MEDIA_TAGS="[]" \ + RIPRAP_CONFIG_DRUPAL_PASSWORD=password \ + RIPRAP_CONFIG_DRUPAL_USER=admin \ + RIPRAP_CONFIG_EMAIL_FROM="" \ + RIPRAP_CONFIG_EMAIL_TO="" \ + RIPRAP_CONFIG_FAILURES_LOG_PATH=var/riprap_failed_events.log \ + RIPRAP_CONFIG_FEDORAAPI_DIGEST_HEADER_LEADER_PATTERN="^.+=" \ + RIPRAP_CONFIG_FEDORAAPI_METHOD=HEAD \ + RIPRAP_CONFIG_FIXITY_ALGORITHM=sha1 \ + RIPRAP_CONFIG_GEMINI_AUTH_HEADER="Bearer islandora" \ + RIPRAP_CONFIG_GEMINI_ENDPOINT=http://gemini:8000 \ + RIPRAP_CONFIG_JSONAPI_AUTHORIZATION_HEADERS="" \ + RIPRAP_CONFIG_JSONAPI_PAGE_SIZE=50 \ + RIPRAP_CONFIG_JSONAPI_PAGER_DATA_FILE_PATH=var/fetchresourcelist.from.drupal.pager.txt \ + RIPRAP_CONFIG_MAX_RESOURCES=1000 \ + RIPRAP_CONFIG_OUTPUT_CSV_PATH=var/riprap_events.csv \ + RIPRAP_CONFIG_PLUGINS_FETCHDIGEST=PluginFetchDigestFromShell \ + RIPRAP_CONFIG_PLUGINS_FETCHRESOURCELIST="['PluginFetchResourceListFromFile']" \ + RIPRAP_CONFIG_PLUGINS_PERSIST=PluginPersistToDatabase \ + RIPRAP_CONFIG_PLUGINS_POSTCHECK="['PluginPostCheckCopyFailures']" \ + RIPRAP_CONFIG_RESOURCE_DIR_PATHS="" \ + RIPRAP_CONFIG_RESOURCE_LIST_PATH="['resources/csv_file_list.csv']" \ + RIPRAP_CONFIG_THIN=false \ + RIPRAP_CONFIG_USE_FEDORA_URLS=true \ + RIPRAP_CONFIG_VIEWS_PAGER_DATA_FILE_PATH=var/fetchresourcelist.from.drupal.pager.txt + +COPY --from=composer --chown=nginx:nginx /var/www /var/www + +COPY rootfs / + +WORKDIR /var/www/riprap \ No newline at end of file diff --git a/riprap/README.md b/riprap/README.md new file mode 100644 index 00000000..1991d996 --- /dev/null +++ b/riprap/README.md @@ -0,0 +1,127 @@ +# Riprap + +Docker image for [Riprap] (**unreleased version**) micro-service. + +Please refer to the [Riprap Documentation] for more in-depth information. + +## Dependencies + +Requires `islandora/nginx` docker image to build. Please refer to the +[Nginx Image README](../nginx/README.md) for additional information including +additional settings, volumes, ports, etc. + +Additionally you can run with different database backends, by default it will +use the bundled SQLite backend which requires no additional configuration. +However if you wish to use a MySQL or PostgreSQL backend please refer to the +[MariaDB Image README](../mariadb/README.md) and +[PostgreSQL Image README](../postgresql/README.md) respectively, and change +`RIPRAP_DB_DRIVER` to your selected backend, along with any other +relevant settings. + +## Ports + +| Port | Description | +| :--- | :---------- | +| 8000 | HTTP | + +## Volumes + +| Path | Description | +| :----------------------------- | :------------------------------------- | +| /var/www/riprap/src/Migrations | Generated Migrations | +| /var/www/riprap/var | SQLite Database / Cache files location | + +## Settings + +### Confd Settings + +| Environment Variable | Confd Key | Default | Description | +| :-------------------------- | :--------------------------- | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | +| RIPRAP_APP_ENV | /riprap/app/env | dev | Only 'dev' is supported at this time | +| RIPRAP_APP_SECRET | /riprap/app/secret | f58c87e1d737c4422b45ba4310abede6 | This is a string that should be unique to your application and it's commonly used to add more entropy to security related operations. | +| RIPRAP_CROND_ENABLE_SERVICE | /riprap/crond/enable/service | true | Enable / disable crond service | +| RIPRAP_CROND_LOG_LEVEL | /riprap/crond/log/level | 8 | The log level for crond | +| RIPRAP_CROND_SCHEDULE | /riprap/crond/schedule | 0 0 1 * * | The schedule for running check_fixity command, default is once a month | +| RIPRAP_LOG_LEVEL | /riprap/log/level | debug | Log level. Possible Values: debug, info, notice, warning, error, critical, alert, emergency, none | +| RIPRAP_MAILER_URL | /riprap/mailer/url | null://localhost | | +| RIPRAP_TRUSTED_HOSTS | /riprap/trusted/hosts | | | +| RIPRAP_TRUSTED_PROXIES | /riprap/trusted/proxies | | | + +You can generate your own secret using the following command: + +```bash +cat /dev/urandom | base64 | head -c 32 && echo "" +``` + +What follows is configuration specific to the check fixity command. Not all +configurations are applicable in all situations, they are largely dependent on +which plugins you enable. + +Please refer to the [Riprap Plugin Overview] and [Riprap Plugin Documentation] +for more in-depth information, as well as the [Riprap Plugins] themselves. + +If starting out fresh its recommend to use +`PluginFetchResourceListFromDrupalView` rather than +`PluginFetchResourceListFromDrupal` which is currently in the process of being +deprecated. + +| Environment Variable | Confd Key | Default | Description | +| :--------------------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RIPRAP_CONFIG_DIGEST_COMMAND | /riprap/config/digest/command | /usr/bin/sha1sum | | +| RIPRAP_CONFIG_DRUPAL_BASEURL | /riprap/config/drupal/baseurl | https://islandora.traefik.me | | +| RIPRAP_CONFIG_DRUPAL_CONTENT_TYPES | /riprap/config/drupal/content/types | ['islandora_object'] | | +| RIPRAP_CONFIG_DRUPAL_FILE_FIELDNAMES | /riprap/config/drupal/file/fieldnames | ['field_media_audio', 'field_media_document', 'field_edited_text', 'field_media_file', 'field_media_image', 'field_media_video_file'] | | +| RIPRAP_CONFIG_DRUPAL_MEDIA_AUTH | /riprap/config/drupal/media/auth | ['admin', 'islandora'] | | +| RIPRAP_CONFIG_DRUPAL_MEDIA_TAGS | /riprap/config/drupal/media/tags | [] | e.g. ['/taxonomy/term/15'] | +| RIPRAP_CONFIG_DRUPAL_PASSWORD | /riprap/config/drupal/password | password | | +| RIPRAP_CONFIG_DRUPAL_USER | /riprap/config/drupal/user | admin | | +| RIPRAP_CONFIG_EMAIL_FROM | /riprap/config/email/from | | | +| RIPRAP_CONFIG_EMAIL_TO | /riprap/config/email/to | | | +| RIPRAP_CONFIG_FAILURES_LOG_PATH | /riprap/config/failures/log/path | var/riprap_failed_events.log | Absolute or relative to the Riprap application directory | +| RIPRAP_CONFIG_FEDORAAPI_DIGEST_HEADER_LEADER_PATTERN | /riprap/config/fedoraapi/digest/header/leader/pattern | "^.+=" | var/riprap_failed_events.log | +| RIPRAP_CONFIG_FEDORAAPI_METHOD | /riprap/config/fedoraapi/method | HEAD | | +| RIPRAP_CONFIG_FIXITY_ALGORITHM | /riprap/config/fixity/algorithm | sha1 | One of 'md5', 'sha1', or 'sha256' | +| RIPRAP_CONFIG_GEMINI_AUTH_HEADER | /riprap/config/gemini/auth/header | "Bearer islandora" | | +| RIPRAP_CONFIG_GEMINI_ENDPOINT | /riprap/config/gemini/endpoint | http://gemini:8000 | | +| RIPRAP_CONFIG_JSONAPI_AUTHORIZATION_HEADERS | /riprap/config/jsonapi/authorization/headers | | e.g. ['Authorization: Basic YWRtaW46aXNsYW5kb3Jh'] | +| RIPRAP_CONFIG_JSONAPI_PAGER_DATA_FILE_PATH | /riprap/config/jsonapi/pager/data/file/path | var/fetchresourcelist.from.drupal.pager.txt | Absolute or relative to the Riprap application directory | +| RIPRAP_CONFIG_JSONAPI_PAGE_SIZE | /riprap/config/jsonapi/page/size | 50 | | +| RIPRAP_CONFIG_MAX_RESOURCES | /riprap/config/max/resources | 1000 | Must be a multiple of RIPRAP_CONFIG_JSONAPI_PAGE_SIZE | +| RIPRAP_CONFIG_OUTPUT_CSV_PATH | /riprap/config/output/csv/path | var/riprap_events.csv | | +| RIPRAP_CONFIG_PLUGINS_FETCHDIGEST | /riprap/config/plugins/fetchdigest | PluginFetchDigestFromShell | Either "PluginFetchDigestFromDrupal", "PluginFetchDigestFromFedoraAPI", or "PluginFetchDigestFromShell" | +| RIPRAP_CONFIG_PLUGINS_FETCHRESOURCELIST | /riprap/config/plugins/fetchresourcelist | ['PluginFetchResourceListFromFile'] | Either "PluginFetchResourceListFromDrupal", "PluginFetchResourceListFromDrupalView", "PluginFetchResourceListFromFile", or "PluginFetchResourceListFromGlob" | +| RIPRAP_CONFIG_PLUGINS_PERSIST | /riprap/config/plugins/persist | PluginPersistToDatabase | Either "PluginPersistToCsv" or "PluginPersistToDatabase" | +| RIPRAP_CONFIG_PLUGINS_POSTCHECK | /riprap/config/plugins/postcheck | ['PluginPostCheckCopyFailures'] | Either "PluginPostCheckCopyFailures", "PluginPostCheckMailFailures", "PluginPostCheckMigrateFedora3AuditLog", "PluginPostCheckSayHello", or unspecified | +| RIPRAP_CONFIG_RESOURCE_DIR_PATHS | /riprap/config/resource/dir/paths | | e.g. ['resources/filesystemexample/resourcefiles'] | +| RIPRAP_CONFIG_RESOURCE_LIST_PATH | /riprap/config/resource/list/path | ['resources/csv_file_list.csv'] | | +| RIPRAP_CONFIG_THIN | /riprap/config/thin | false | | +| RIPRAP_CONFIG_USE_FEDORA_URLS | /riprap/config/use/fedora/urls | true | | +| RIPRAP_CONFIG_VIEWS_PAGER_DATA_FILE_PATH | /riprap/config/views/pager/data/file/path | var/fetchresourcelist.from.drupal.pager.txt | | + +> N.B. Configuration list was generated by searching for all instances of +> `$this->settings['some_setting']` in the riprap repository. When upgrading +> riprap commit be sure to check that the options for configuration have been +> updated appropriately along with their defaults. + +### Database Settings + +[Riprap] can optionally make use of different database backends. Please see +the documentation in the [base image] for more information about the default +database connection configuration. + +Aside from `RIPRAP_DB_DRIVER`, the following settings are only used if +`RIPRAP_DB_DRIVER` is set to `mysql` or `postgresql`. + +| Environment Variable | Confd Key | Default | Description | +| :------------------- | :------------------ | :------- | :------------------------------------------------------------ | +| RIPRAP_DB_DRIVER | /riprap/db/driver | sqlite | The database driver either 'sqlite', 'mysql', or 'postgresql' | +| RIPRAP_DB_NAME | /riprap/db/name | riprap | The name of the database | +| RIPRAP_DB_PASSWORD | /riprap/db/password | password | The database users password | +| RIPRAP_DB_USER | /riprap/db/user | riprap | The database user | + +[base image]: ../base/README.md +[Riprap Documentation]: https://github.com/mjordan/riprap#riprap +[Riprap Plugin Documentation]: https://github.com/mjordan/riprap/blob/master/docs/plugins.md +[Riprap Plugin Overview]: https://github.com/mjordan/riprap#plugins +[Riprap Plugins]: https://github.com/mjordan/riprap/tree/master/src/Plugin +[Riprap]: https://github.com/mjordan/riprap diff --git a/riprap/rootfs/etc/confd/conf.d/.env.toml b/riprap/rootfs/etc/confd/conf.d/.env.toml new file mode 100644 index 00000000..42cece18 --- /dev/null +++ b/riprap/rootfs/etc/confd/conf.d/.env.toml @@ -0,0 +1,7 @@ +[template] +src = ".env.tmpl" +dest = "/var/www/riprap/.env" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/" ] \ No newline at end of file diff --git a/riprap/rootfs/etc/confd/conf.d/cron_config.yml.toml b/riprap/rootfs/etc/confd/conf.d/cron_config.yml.toml new file mode 100644 index 00000000..cd68de50 --- /dev/null +++ b/riprap/rootfs/etc/confd/conf.d/cron_config.yml.toml @@ -0,0 +1,7 @@ +[template] +src = "cron_config.yml.tmpl" +dest = "/var/www/riprap/cron_config.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/" ] \ No newline at end of file diff --git a/riprap/rootfs/etc/confd/conf.d/doctrine.yaml.toml b/riprap/rootfs/etc/confd/conf.d/doctrine.yaml.toml new file mode 100644 index 00000000..23f0212c --- /dev/null +++ b/riprap/rootfs/etc/confd/conf.d/doctrine.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "doctrine.yaml.tmpl" +dest = "/var/www/riprap/config/packages/doctrine.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/" ] \ No newline at end of file diff --git a/riprap/rootfs/etc/confd/conf.d/monolog.yaml.toml b/riprap/rootfs/etc/confd/conf.d/monolog.yaml.toml new file mode 100644 index 00000000..ddeb9511 --- /dev/null +++ b/riprap/rootfs/etc/confd/conf.d/monolog.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "monolog.yaml.tmpl" +dest = "/var/www/riprap/config/packages/dev/monolog.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/" ] diff --git a/riprap/rootfs/etc/confd/templates/.env.tmpl b/riprap/rootfs/etc/confd/templates/.env.tmpl new file mode 100644 index 00000000..0397369a --- /dev/null +++ b/riprap/rootfs/etc/confd/templates/.env.tmpl @@ -0,0 +1,35 @@ +# This file is a "template" of which env vars need to be defined for your application +# Copy this file to .env file for development, create environment variables when deploying to production +# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration + +###> symfony/framework-bundle ### +APP_ENV={{ getenv "RIPRAP_APP_ENV" }} +APP_SECRET={{ getenv "RIPRAP_APP_SECRET" }} +{{ if ne (getenv "RIPRAP_TRUSTED_PROXIES") "" }} +TRUSTED_PROXIES={{ getenv "RIPRAP_TRUSTED_PROXIES" }} +{{ end }} +{{ if ne (getenv "RIPRAP_TRUSTED_HOSTS") "" }} +TRUSTED_HOSTS={{ getenv "RIPRAP_TRUSTED_HOSTS" }} +{{ end }} +###< symfony/framework-bundle ### + +###> doctrine/doctrine-bundle ### +# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url +# Configure your db driver and server_version in config/packages/doctrine.yaml +{{ if eq (getenv "DB_DRIVER") "sqlite" }} +DATABASE_URL=sqlite:///%kernel.project_dir%/var/data.db +{{ end }} +{{ if eq (getenv "DB_DRIVER") "mysql" }} +DATABASE_URL=mysql://{{ getenv "RIPRAP_DB_USER" }}:{{ getenv "RIPRAP_DB_PASSWORD" }}@{{ getenv "DB_MYSQL_HOST" }}:{{ getenv "DB_MYSQL_PORT" }}/{{ getenv "RIPRAP_DB_NAME" }} +{{ end }} +{{ if eq (getenv "DB_DRIVER") "postgresql" }} +DATABASE_URL=pgsql://{{ getenv "RIPRAP_DB_USER" }}:{{ getenv "RIPRAP_DB_PASSWORD" }}@{{ getenv "DB_POSTGRESQL_HOST" }}:{{ getenv "DB_POSTGRESQL_PORT" }}/{{ getenv "RIPRAP_DB_NAME" }} +{{ end }} +###< doctrine/doctrine-bundle ### + +###> symfony/swiftmailer-bundle ### +# For Gmail as a transport, use: "gmail://username:password@localhost" +# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" +# Delivery is disabled by default via "null://localhost" +MAILER_URL={{ getenv "RIPRAP_MAILER_URL" }} +###< symfony/swiftmailer-bundle ### \ No newline at end of file diff --git a/riprap/rootfs/etc/confd/templates/cron_config.yml.tmpl b/riprap/rootfs/etc/confd/templates/cron_config.yml.tmpl new file mode 100644 index 00000000..03fab0b3 --- /dev/null +++ b/riprap/rootfs/etc/confd/templates/cron_config.yml.tmpl @@ -0,0 +1,40 @@ +# Riprap config file used by the crond service. +# +# Requires that the "Riprap resource list" View be enabled in +# the Islandora instance. This View is bundled with the Islandora Riprap +# module. +# +# This plugin is agnostic to which media have fixity event checks performed +# on them. The filters in the "Riprap resource list" View determine that. +# See the View's filter criteria GUI for examples. + +digest_command: '{{ getenv "RIPRAP_CONFIG_DIGEST_COMMAND" }}' +drupal_baseurl: '{{ getenv "RIPRAP_CONFIG_DRUPAL_BASEURL" }}' +drupal_content_types: {{ getenv "RIPRAP_CONFIG_DRUPAL_CONTENT_TYPES" }} +drupal_file_fieldnames: {{ getenv "RIPRAP_CONFIG_DRUPAL_FILE_FIELDNAMES" }} +drupal_media_auth: {{ getenv "RIPRAP_CONFIG_DRUPAL_MEDIA_AUTH" }} +drupal_media_tags: {{ getenv "RIPRAP_CONFIG_DRUPAL_MEDIA_TAGS" }} +drupal_password: '{{ getenv "RIPRAP_CONFIG_DRUPAL_PASSWORD" }}' +drupal_user: '{{ getenv "RIPRAP_CONFIG_DRUPAL_USER" }}' +email_from: '{{ getenv "RIPRAP_CONFIG_EMAIL_FROM" }}' +email_to: '{{ getenv "RIPRAP_CONFIG_EMAIL_TO" }}' +failures_log_path: '{{ getenv "RIPRAP_CONFIG_FAILURES_LOG_PATH" }}' +fedoraapi_digest_header_leader_pattern: '{{ getenv "RIPRAP_CONFIG_FEDORAAPI_DIGEST_HEADER_LEADER_PATTERN" }}' +fedoraapi_method: '{{ getenv "RIPRAP_CONFIG_FEDORAAPI_METHOD" }}' +fixity_algorithm: '{{ getenv "RIPRAP_CONFIG_FIXITY_ALGORITHM" }}' +gemini_auth_header: '{{ getenv "RIPRAP_CONFIG_GEMINI_AUTH_HEADER" }}' +gemini_endpoint: '{{ getenv "RIPRAP_CONFIG_GEMINI_ENDPOINT" }}' +jsonapi_authorization_headers: '{{ getenv "RIPRAP_CONFIG_JSONAPI_AUTHORIZATION_HEADERS" }}' +jsonapi_pager_data_file_path: '{{ getenv "RIPRAP_CONFIG_JSONAPI_PAGER_DATA_FILE_PATH" }}' +jsonapi_page_size: {{ getenv "RIPRAP_CONFIG_JSONAPI_PAGE_SIZE" }} +max_resources: {{ getenv "RIPRAP_CONFIG_MAX_RESOURCES" }} +output_csv_path: '{{ getenv "RIPRAP_CONFIG_OUTPUT_CSV_PATH" }}' +plugins.fetchdigest: '{{ getv "/config/plugins.fetchdigest" (getenv "RIPRAP_CONFIG_PLUGINS_FETCHDIGEST") }}' +plugins.fetchresourcelist: {{ getv "/config/plugins.fetchresourcelist" (getenv "RIPRAP_CONFIG_PLUGINS_FETCHRESOURCELIST") }} +plugins.persist: '{{ getv "/config/plugins.persist" (getenv "RIPRAP_CONFIG_PLUGINS_PERSIST") }}' +plugins.postcheck: {{ getv "/config/plugins.postcheck" (getenv "RIPRAP_CONFIG_PLUGINS_POSTCHECK") }} +resource_dir_paths: {{ getenv "RIPRAP_CONFIG_RESOURCE_DIR_PATHS" }} +resource_list_path: {{ getenv "RIPRAP_CONFIG_RESOURCE_LIST_PATH" }} +thin: {{ getenv "RIPRAP_CONFIG_THIN" }} +use_fedora_urls: {{ getenv "RIPRAP_CONFIG_USE_FEDORA_URLS" }} +views_pager_data_file_path: '{{ getenv "RIPRAP_CONFIG_VIEWS_PAGER_DATA_FILE_PATH" }}' \ No newline at end of file diff --git a/riprap/rootfs/etc/confd/templates/doctrine.yaml.tmpl b/riprap/rootfs/etc/confd/templates/doctrine.yaml.tmpl new file mode 100644 index 00000000..3f137bd4 --- /dev/null +++ b/riprap/rootfs/etc/confd/templates/doctrine.yaml.tmpl @@ -0,0 +1,32 @@ +parameters: + # Adds a fallback DATABASE_URL if the env var is not set. + # This allows you to run cache:warmup even if your + # environment variables are not available yet. + # You should not need to change this value. + env(DATABASE_URL): '' + +doctrine: + dbal: +{{ if eq (getenv "DB_DRIVER") "mysql" }} + driver: 'pdo_mysql' + charset: utf8mb4 + default_table_options: + charset: utf8mb4 + collate: utf8mb4_unicode_ci +{{ end }} +{{ if eq (getenv "DB_DRIVER") "postgresql" }} + driver: 'pdo_pgsql' + charset: utf8 +{{ end }} + url: '%env(resolve:DATABASE_URL)%' + orm: + auto_generate_proxy_classes: '%kernel.debug%' + naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware + auto_mapping: true + mappings: + App: + is_bundle: false + type: annotation + dir: '%kernel.project_dir%/src/Entity' + prefix: 'App\Entity' + alias: App \ No newline at end of file diff --git a/riprap/rootfs/etc/confd/templates/monolog.yaml.tmpl b/riprap/rootfs/etc/confd/templates/monolog.yaml.tmpl new file mode 100644 index 00000000..1b9a3f28 --- /dev/null +++ b/riprap/rootfs/etc/confd/templates/monolog.yaml.tmpl @@ -0,0 +1,20 @@ +monolog: + handlers: + main: + type: stream + # path: "%kernel.logs_dir%/%kernel.environment%.log" + path: "php://stderr" + level: {{ getenv "RIPRAP_LOG_LEVEL" }} + channels: ["!event"] + # uncomment to get logging in your browser + # you may have to allow bigger header sizes in your Web server configuration + #firephp: + # type: firephp + # level: info + #chromephp: + # type: chromephp + # level: info + console: + type: console + process_psr_3_messages: false + channels: ["!event", "!doctrine", "!console"] \ No newline at end of file diff --git a/riprap/rootfs/etc/cont-init.d/03-riprap-setup.sh b/riprap/rootfs/etc/cont-init.d/03-riprap-setup.sh new file mode 100755 index 00000000..5d88eb10 --- /dev/null +++ b/riprap/rootfs/etc/cont-init.d/03-riprap-setup.sh @@ -0,0 +1,80 @@ +#!/usr/bin/with-contenv bash +set -e + +function mysql_create_database { + cat <<- EOF | create-database.sh +-- Create database in mariadb or mysql. +CREATE DATABASE IF NOT EXISTS ${RIPRAP_DB_NAME} CHARACTER SET utf8 COLLATE utf8_general_ci; + +-- Create user and grant rights. +CREATE USER IF NOT EXISTS '${RIPRAP_DB_USER}'@'%' IDENTIFIED BY '${RIPRAP_DB_PASSWORD}'; +GRANT ALL PRIVILEGES ON ${RIPRAP_DB_NAME}.* to '${RIPRAP_DB_USER}'@'%'; +FLUSH PRIVILEGES; + +-- Update user password if changed. +SET PASSWORD FOR ${RIPRAP_DB_USER}@'%' = PASSWORD('${RIPRAP_DB_PASSWORD}') +EOF +} + +function postgresql_create_database { + cat <<- EOF | create-database.sh +BEGIN; + +DO \$\$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${RIPRAP_DB_USER}') THEN + CREATE ROLE ${RIPRAP_DB_USER}; + END IF; +END +\$\$; + +ALTER ROLE ${RIPRAP_DB_USER} WITH LOGIN; +ALTER USER ${RIPRAP_DB_USER} PASSWORD '${RIPRAP_DB_PASSWORD}'; + +ALTER DATABASE ${RIPRAP_DB_NAME} OWNER TO ${RIPRAP_DB_USER}; +GRANT ALL PRIVILEGES ON DATABASE ${RIPRAP_DB_NAME} TO ${RIPRAP_DB_USER}; + +COMMIT; +EOF +} + +function create_database { + case "${DB_DRIVER}" in + sqlite) + # Running migrations will create the database. + ;; + mysql) + mysql_create_database + ;; + postgresql) + postgresql_create_database + ;; + *) + echo "Only SQLite/MySQL/PostgresSQL databases are supported for now." >&2 + exit 1 + esac +} + +function setup_cron { + if [[ "${RIPRAP_CROND_ENABLE_SERVICE}" == "true" ]]; then + cat <("test") \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithBackendMySQL/docker-compose.yml b/riprap/tests/ServiceStartsWithBackendMySQL/docker-compose.yml new file mode 100644 index 00000000..2d06f2e4 --- /dev/null +++ b/riprap/tests/ServiceStartsWithBackendMySQL/docker-compose.yml @@ -0,0 +1,13 @@ +# file: docker-compose.yml +version: "3.8" +services: + mariadb: + image: ${MYSQL_IMAGE:-local/mariadb:latest} + riprap: + environment: + RIPRAP_DB_DRIVER: mysql + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/riprap:latest} \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithBackendMySQL/test.sh b/riprap/tests/ServiceStartsWithBackendMySQL/test.sh new file mode 100755 index 00000000..e5f348e5 --- /dev/null +++ b/riprap/tests/ServiceStartsWithBackendMySQL/test.sh @@ -0,0 +1,31 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +function count { + cat <<- EOF | execute-sql-file.sh --database "${DB_NAME}" - -- -N 2>/dev/null +SELECT COUNT(*) as count FROM fixity_check_event; +EOF +} + +# Exit non-zero if database does not exist. +cat <<- EOF | execute-sql-file.sh +use ${DB_NAME} +EOF + +# Perform check-fixity (ingests from CSV). +check-fixity.sh "--settings=/var/www/riprap/cron_config.yaml" + +# Query the database to determine if the expected number of checks occured. +rows=$(count) + +# Check if results meet expectations. +if [[ "${rows}" != "3" ]]; then + echo "Failed to created the expected number of rows: ${rows}!=3." + exit 1 +else + echo "Created the expected number of rows." +fi + +# All tests were successful +exit 0 \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts b/riprap/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts new file mode 100644 index 00000000..975756eb --- /dev/null +++ b/riprap/tests/ServiceStartsWithBackendPostgreSQL/build.gradle.kts @@ -0,0 +1,2 @@ +import tasks.tests.DockerComposeTest +tasks.register("test") \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml b/riprap/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml new file mode 100644 index 00000000..406b3479 --- /dev/null +++ b/riprap/tests/ServiceStartsWithBackendPostgreSQL/docker-compose.yml @@ -0,0 +1,14 @@ +# file: docker-compose.yml +version: "3.8" +services: + postgresql: + image: ${MYSQL_IMAGE:-local/postgresql:latest} + riprap: + # Allow downstream container to override `DB` environment variables. + environment: + RIPRAP_DB_DRIVER: postgresql + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/riprap:latest} \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithBackendPostgreSQL/test.sh b/riprap/tests/ServiceStartsWithBackendPostgreSQL/test.sh new file mode 100755 index 00000000..8ab16a96 --- /dev/null +++ b/riprap/tests/ServiceStartsWithBackendPostgreSQL/test.sh @@ -0,0 +1,26 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +function count { + cat <("test") \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithBackendSqlite/docker-compose.yml b/riprap/tests/ServiceStartsWithBackendSqlite/docker-compose.yml new file mode 100644 index 00000000..476a6df9 --- /dev/null +++ b/riprap/tests/ServiceStartsWithBackendSqlite/docker-compose.yml @@ -0,0 +1,9 @@ +# file: docker-compose.yml +version: "3.8" +services: + riprap: + volumes: + - ./test.sh:/test.sh # Test to run. + command: + - /test.sh # Run test and exit. + image: ${BASE_IMAGE:-local/riprap:latest} \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithBackendSqlite/test.sh b/riprap/tests/ServiceStartsWithBackendSqlite/test.sh new file mode 100755 index 00000000..17eab802 --- /dev/null +++ b/riprap/tests/ServiceStartsWithBackendSqlite/test.sh @@ -0,0 +1,36 @@ +#!/usr/bin/with-contenv bash + +source /usr/local/share/isle/utilities.sh + +# Perform check-fixity (ingests from CSV). +check-fixity.sh "--settings=/var/www/riprap/cron_config.yaml" + +# Confirm sqlite database exists. +test -e /var/www/riprap/var/data.db + +# Query the database to determine if the expected number of checks occured. +rows=$( +cat <<'EOF' | php -f /dev/stdin +query($sql); +if($result){ + while($row = $result->fetch(PDO::FETCH_ASSOC)){ + echo $row['count']; + } +} +?> +EOF +) + +# Check if results meet expectations. +if [[ "${rows}" != "3" ]]; then + echo "Failed to created the expected number of rows: ${rows}!=3." + exit 1 +else + echo "Created the expected number of rows." +fi + +# All tests were successful +exit 0 \ No newline at end of file diff --git a/riprap/tests/ServiceStartsWithDefaults/build.gradle.kts b/riprap/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..c1430982 --- /dev/null +++ b/riprap/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("NOTICE: ready to handle connections") +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 937cf978..a67a5350 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,10 +9,22 @@ sourceControl { // Include any folder that has a Dockerfile as a sub-project. rootProject.projectDir - .walk() - .maxDepth(1) // Only immediate directories. - .filter { it.isDirectory && it.resolve("Dockerfile").exists() } // Must have a Dockerfile. - .forEach { - // Include as a sub-project. - include(it.relativeTo(rootProject.projectDir).path) + .walk() + .maxDepth(1) // Only immediate directories. + .filter { it.isDirectory && it.resolve("Dockerfile").exists() } // Must have a Dockerfile. + .forEach { docker -> + // Include as a sub-project. + include(docker.relativeTo(rootProject.projectDir).path) + // Include any tests as sub-projects of the docker project. + val tests = docker.resolve("tests") + if (tests.isDirectory) { + include(tests.relativeTo(rootProject.projectDir).path.replace("/", ":")) + // Add any sub-folders that container project files as well. + tests + .walk() + .filter { it.isDirectory && it.resolve("build.gradle.kts").exists() } + .forEach { + include(it.relativeTo(rootProject.projectDir).path.replace("/", ":")) + } } + } diff --git a/solr/.dockerignore b/solr/.dockerignore index b43bf86b..badf6cb7 100644 --- a/solr/.dockerignore +++ b/solr/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/solr/rootfs/etc/services.d/solr/finish b/solr/rootfs/etc/services.d/solr/finish index f8984dd3..964ffe33 100644 --- a/solr/rootfs/etc/services.d/solr/finish +++ b/solr/rootfs/etc/services.d/solr/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh : -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/solr/rootfs/etc/services.d/solr/run b/solr/rootfs/etc/services.d/solr/run index dc64b621..04878537 100644 --- a/solr/rootfs/etc/services.d/solr/run +++ b/solr/rootfs/etc/services.d/solr/run @@ -1,5 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh : -with-contenv -s6-setuidgid solr /opt/solr/bin/solr start -f +#!/usr/bin/env bash +set -e +exec s6-setuidgid solr /opt/solr/bin/solr start -f \ No newline at end of file diff --git a/solr/tests/ServiceStartsWithDefaults/build.gradle.kts b/solr/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..49697350 --- /dev/null +++ b/solr/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("o.e.j.s.Server Started") +} diff --git a/tomcat/.dockerignore b/tomcat/.dockerignore index b43bf86b..badf6cb7 100644 --- a/tomcat/.dockerignore +++ b/tomcat/.dockerignore @@ -1 +1,5 @@ +build.gradle.kts +Dockerfile README.md +tests +tests/**/* \ No newline at end of file diff --git a/tomcat/rootfs/etc/services.d/nginx/finish b/tomcat/rootfs/etc/services.d/nginx/finish index 9030da41..964ffe33 100644 --- a/tomcat/rootfs/etc/services.d/nginx/finish +++ b/tomcat/rootfs/etc/services.d/nginx/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh: -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/tomcat/rootfs/etc/services.d/nginx/run b/tomcat/rootfs/etc/services.d/nginx/run index 7503678e..4d500b4a 100644 --- a/tomcat/rootfs/etc/services.d/nginx/run +++ b/tomcat/rootfs/etc/services.d/nginx/run @@ -1,4 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh: -/usr/sbin/nginx +#!/usr/bin/env bash +set -e +exec /usr/sbin/nginx diff --git a/tomcat/rootfs/etc/services.d/tomcat/finish b/tomcat/rootfs/etc/services.d/tomcat/finish index f8984dd3..964ffe33 100644 --- a/tomcat/rootfs/etc/services.d/tomcat/finish +++ b/tomcat/rootfs/etc/services.d/tomcat/finish @@ -1,4 +1,4 @@ -#!/usr/bin/execlineb -S1 -# -*- mode: sh -*- -# vi: set ft=sh : -s6-svscanctl -t /var/run/s6/services +#!/usr/bin/env bash +set -e + +source /usr/local/share/s6/finish diff --git a/tomcat/rootfs/etc/services.d/tomcat/run b/tomcat/rootfs/etc/services.d/tomcat/run index b66a361b..3145cc78 100644 --- a/tomcat/rootfs/etc/services.d/tomcat/run +++ b/tomcat/rootfs/etc/services.d/tomcat/run @@ -1,6 +1,3 @@ -#!/usr/bin/execlineb -P -# -*- mode: sh -*- -# vi: set ft=sh : -with-contenv -s6-setuidgid tomcat -/opt/tomcat/bin/catalina.sh run +#!/usr/bin/env bash +set -e +exec with-contenv s6-setuidgid tomcat /opt/tomcat/bin/catalina.sh run diff --git a/tomcat/tests/ServiceStartsWithDefaults/build.gradle.kts b/tomcat/tests/ServiceStartsWithDefaults/build.gradle.kts new file mode 100644 index 00000000..70e8d105 --- /dev/null +++ b/tomcat/tests/ServiceStartsWithDefaults/build.gradle.kts @@ -0,0 +1,4 @@ +import tasks.tests.ServiceStartsWithDefaultsTest +tasks.register("test") { + waitForMessage.set("org.apache.catalina.startup.Catalina.start Server startup") +}