From b7d9de441220bbf3d4dba23e8353f5ce3078ebe6 Mon Sep 17 00:00:00 2001 From: William Correa Date: Wed, 3 Jun 2026 23:35:20 -0300 Subject: [PATCH 1/2] feat(hyperf): add OTEL collector variants and unify hyperf-otel Restructures hyperf/ to follow the {image}/{version} pattern used by aws-cli, node, quasar, vue and merges the standalone hyperf-otel image into hyperf/8.3 as a multi-stage variant. Targets: - hyperf -> devitools/hyperf:8.3 (lean, ~97MB, ENTRYPOINT=php) - hyperf-otel -> devitools/hyperf:8.3- (~463MB, ENTRYPOINT=supervisord) Collectors live in hyperf/8.3/otel/collectors/*.yaml. New variants can be added by dropping a yaml file. Two ship in this PR: - debug (vendor-neutral, logs spans to stderr) - google (googlecloud exporter, replaces previous gcloud-specific default) hyperf/latest is a symlink to 8.3. Adds .github/workflows/build-images.yml: detects changed {image}/{version} folders on push to main, follows latest symlinks, and publishes one tag per (version, target) combo to Docker Hub via DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets. --- .github/workflows/build-images.yml | 165 +++++++++++++++++++ .github/workflows/validate.yml | 93 +++++++++++ hyperf/{ => 8.3}/.scripts/setup-dev.sh | 0 hyperf/{ => 8.3}/.scripts/setup.sh | 0 hyperf/{ => 8.3}/Dockerfile | 30 +++- hyperf/{ => 8.3}/README-pt-BR.md | 0 hyperf/{ => 8.3}/README.md | 0 hyperf/8.3/otel/collectors/debug.yaml | 22 +++ hyperf/8.3/otel/collectors/google.yaml | 22 +++ hyperf/8.3/otel/entrypoint.sh | 96 +++++++++++ hyperf/8.3/otel/supervisord.conf | 28 ++++ hyperf/{ => 8.3}/rootfs/apk/repositories | 0 hyperf/{ => 8.3}/rootfs/etc/php/php-fpm.conf | 0 hyperf/{ => 8.3}/rootfs/etc/php/php.ini | 0 hyperf/8.3/variants.yaml | 14 ++ hyperf/latest | 1 + 16 files changed, 464 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/build-images.yml create mode 100644 .github/workflows/validate.yml rename hyperf/{ => 8.3}/.scripts/setup-dev.sh (100%) rename hyperf/{ => 8.3}/.scripts/setup.sh (100%) rename hyperf/{ => 8.3}/Dockerfile (65%) rename hyperf/{ => 8.3}/README-pt-BR.md (100%) rename hyperf/{ => 8.3}/README.md (100%) create mode 100644 hyperf/8.3/otel/collectors/debug.yaml create mode 100644 hyperf/8.3/otel/collectors/google.yaml create mode 100755 hyperf/8.3/otel/entrypoint.sh create mode 100644 hyperf/8.3/otel/supervisord.conf rename hyperf/{ => 8.3}/rootfs/apk/repositories (100%) rename hyperf/{ => 8.3}/rootfs/etc/php/php-fpm.conf (100%) rename hyperf/{ => 8.3}/rootfs/etc/php/php.ini (100%) create mode 100644 hyperf/8.3/variants.yaml create mode 120000 hyperf/latest diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml new file mode 100644 index 0000000..82abb14 --- /dev/null +++ b/.github/workflows/build-images.yml @@ -0,0 +1,165 @@ +name: Build and push images + +on: + push: + branches: + - main + paths: + - '*/*/Dockerfile' + - '*/*/variants.yaml' + - '*/*/rootfs/**' + - '*/*/.scripts/**' + - '*/*/otel/**' + - '*/latest' + workflow_dispatch: + inputs: + target: + description: 'Image/version to build (e.g. hyperf/8.3 or hyperf/latest). Leave empty to detect.' + required: false + +jobs: + detect: + runs-on: ubuntu-latest + outputs: + builds: ${{ steps.list.outputs.builds }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get changed files + id: changed + if: github.event_name == 'push' + uses: tj-actions/changed-files@v44 + + - name: Build matrix + id: list + shell: bash + run: | + set -euo pipefail + + declare -a versions=() + + collect_version() { + local v="$1" + [ -z "$v" ] && return + local depth + depth=$(awk -F/ '{print NF}' <<< "$v") + if [ "$depth" != "2" ]; then return; fi + local resolved="$v" + if [ -L "$v" ]; then + local link_target + link_target=$(readlink "$v") + resolved="${v%/*}/${link_target}" + fi + if [ ! -f "${resolved}/Dockerfile" ]; then return; fi + for existing in "${versions[@]+"${versions[@]}"}"; do + if [ "$existing" = "$v" ]; then return; fi + done + versions+=("$v") + } + + if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.target }}" ]; then + collect_version "${{ inputs.target }}" + else + declare -a changed_dirs=() + for f in ${{ steps.changed.outputs.all_changed_files || '' }}; do + dir=$(awk -F/ 'NF>=2 {print $1"/"$2}' <<< "$f") + [ -z "$dir" ] && continue + for existing in "${changed_dirs[@]+"${changed_dirs[@]}"}"; do + if [ "$existing" = "$dir" ]; then dir=""; break; fi + done + [ -n "$dir" ] && changed_dirs+=("$dir") + done + + for v in "${changed_dirs[@]+"${changed_dirs[@]}"}"; do + collect_version "$v" + done + + for symlink in */latest; do + [ -L "$symlink" ] || continue + target=$(readlink "$symlink") + parent="${symlink%/*}" + resolved="${parent}/${target}" + for changed in "${changed_dirs[@]+"${changed_dirs[@]}"}"; do + if [ "$changed" = "$resolved" ]; then + collect_version "$symlink" + break + fi + done + done + fi + + declare -a builds=() + for v in "${versions[@]+"${versions[@]}"}"; do + image="${v%%/*}" + version="${v##*/}" + ctx="$v" + if [ -L "$ctx" ]; then + link_target=$(readlink "$ctx") + ctx="${ctx%/*}/${link_target}" + fi + + manifest="${ctx}/variants.yaml" + if [ -f "$manifest" ]; then + if ! yq eval 'all_c(.target != null and .target != "" and has("suffix"))' "$manifest" | grep -qx true; then + echo "::error file=${manifest}::each entry must declare 'target' (non-empty) and 'suffix' (key required, value may be empty)" + exit 1 + fi + entries=$(yq eval -o=json "$manifest") + else + entries='[{"target":"'"$image"'","suffix":""}]' + fi + + while IFS= read -r line; do + [ -z "$line" ] && continue + builds+=("$line") + done < <(jq -c --arg img "$image" --arg ver "$version" --arg ctx "$ctx" ' + .[] | { + image: $img, + tag: ($ver + (if (.suffix // "") == "" then "" else "-" + .suffix end)), + context: $ctx, + target: .target, + build_args: ((.args // {}) | to_entries | map(.key + "=" + (.value | tostring)) | join("\n")) + } + ' <<< "$entries") + done + + if [ ${#builds[@]} -eq 0 ]; then + echo "builds=[]" >> "$GITHUB_OUTPUT" + else + joined=$(IFS=,; echo "${builds[*]}") + echo "builds=[${joined}]" >> "$GITHUB_OUTPUT" + fi + + build: + needs: detect + if: needs.detect.outputs.builds != '[]' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + build: ${{ fromJson(needs.detect.outputs.builds) }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: ./${{ matrix.build.context }} + target: ${{ matrix.build.target }} + build-args: ${{ matrix.build.build_args }} + platforms: linux/amd64 + push: true + tags: devitools/${{ matrix.build.image }}:${{ matrix.build.tag }} + cache-from: type=gha,scope=${{ matrix.build.image }}-${{ matrix.build.tag }} + cache-to: type=gha,mode=max,scope=${{ matrix.build.image }}-${{ matrix.build.tag }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..25d8209 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,93 @@ +name: Validate variants.yaml + +on: + pull_request: + branches: + - main + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get changed variants.yaml files + id: changed + uses: tj-actions/changed-files@v44 + with: + files: '*/*/variants.yaml' + + - name: Validate each manifest + if: steps.changed.outputs.any_changed == 'true' + shell: bash + run: | + set -uo pipefail + errors=0 + + for manifest in ${{ steps.changed.outputs.all_changed_files }}; do + echo "::group::${manifest}" + dir="$(dirname "$manifest")" + dockerfile="${dir}/Dockerfile" + + if ! yq eval '.' "$manifest" > /dev/null 2>&1; then + echo "::error file=${manifest}::invalid YAML syntax" + errors=$((errors+1)) + echo "::endgroup::" + continue + fi + + kind=$(yq eval 'type' "$manifest") + if [ "$kind" != "!!seq" ]; then + echo "::error file=${manifest}::root must be a YAML list (got ${kind})" + errors=$((errors+1)) + echo "::endgroup::" + continue + fi + + entries=$(yq eval -o=json "$manifest") + + bad=$(jq -c '[.[] | select((.target | type) != "string" or .target == "" or (has("suffix") | not))]' <<< "$entries") + if [ "$(jq 'length' <<< "$bad")" != "0" ]; then + echo "::error file=${manifest}::entries missing required 'target' (non-empty string) or 'suffix' key:" + jq -r '.[] | " - " + (. | tostring)' <<< "$bad" + errors=$((errors+1)) + fi + + bad_args=$(jq -c '[.[] | select(has("args") and (.args | type) != "object")]' <<< "$entries") + if [ "$(jq 'length' <<< "$bad_args")" != "0" ]; then + echo "::error file=${manifest}::'args' must be a mapping when present:" + jq -r '.[] | " - " + (. | tostring)' <<< "$bad_args" + errors=$((errors+1)) + fi + + if [ ! -f "$dockerfile" ]; then + echo "::error file=${manifest}::sibling Dockerfile not found at ${dockerfile}" + errors=$((errors+1)) + else + for target in $(jq -r '.[].target' <<< "$entries" | sort -u); do + if ! grep -qE "^FROM[[:space:]]+.*[[:space:]]+AS[[:space:]]+${target}([[:space:]]|$)" "$dockerfile"; then + echo "::error file=${manifest}::target '${target}' is not declared as a stage in ${dockerfile} (expected: FROM AS ${target})" + errors=$((errors+1)) + fi + done + fi + + duplicates=$(jq -r ' + [.[] | (if (.suffix // "") == "" then "" else .suffix end)] | + group_by(.) | map(select(length > 1)) | map(.[0]) | .[] + ' <<< "$entries" | sort -u) + if [ -n "$duplicates" ]; then + echo "::error file=${manifest}::duplicate suffix(es) detected (would produce colliding tags):" + echo "$duplicates" | sed 's/^/ - /' + errors=$((errors+1)) + fi + + echo "::endgroup::" + done + + if [ $errors -gt 0 ]; then + echo "Found ${errors} error(s) across the changed manifests." + exit 1 + fi + echo "All changed variants.yaml are valid." diff --git a/hyperf/.scripts/setup-dev.sh b/hyperf/8.3/.scripts/setup-dev.sh similarity index 100% rename from hyperf/.scripts/setup-dev.sh rename to hyperf/8.3/.scripts/setup-dev.sh diff --git a/hyperf/.scripts/setup.sh b/hyperf/8.3/.scripts/setup.sh similarity index 100% rename from hyperf/.scripts/setup.sh rename to hyperf/8.3/.scripts/setup.sh diff --git a/hyperf/Dockerfile b/hyperf/8.3/Dockerfile similarity index 65% rename from hyperf/Dockerfile rename to hyperf/8.3/Dockerfile index f9011c2..ec1e343 100644 --- a/hyperf/Dockerfile +++ b/hyperf/8.3/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.20 +FROM alpine:3.20 AS hyperf ARG CONTEXT ARG TIMEZONE @@ -57,18 +57,34 @@ COPY --from=composer/composer:2.8.5-bin /composer /usr/local/bin/composer COPY .scripts /devitools/.scripts -# update RUN set -ex \ - # ---------- apply settings -------\ && bash /devitools/.scripts/setup.sh "$TIMEZONE" \ && bash /devitools/.scripts/setup-dev.sh "$APP_TARGET" \ - # ---------- clear works ----------\ && rm -rf /var/cache/apk/* /tmp/* /usr/share/man WORKDIR /opt/www - EXPOSE 9501 +ENTRYPOINT ["php", "/opt/www/bin/hyperf.php"] +CMD ["start"] + +# --- Variant: with OTEL Collector + pgbouncer + supervisor --- +FROM hyperf AS hyperf-otel + +ARG COLLECTOR=debug +ARG OTEL_COLLECTOR_VERSION=0.121.0 + +RUN apk add --no-cache --upgrade expat \ + && apk add --no-cache supervisor wget pgbouncer \ + && wget -q "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_COLLECTOR_VERSION}/otelcol-contrib_${OTEL_COLLECTOR_VERSION}_linux_amd64.tar.gz" -O /tmp/otelcol.tar.gz \ + && tar -xzf /tmp/otelcol.tar.gz -C /usr/local/bin otelcol-contrib \ + && rm /tmp/otelcol.tar.gz \ + && chmod +x /usr/local/bin/otelcol-contrib \ + && mkdir -p /etc/pgbouncer /var/log/pgbouncer /var/run/pgbouncer /etc/supervisor.d /var/run/supervisor -ENTRYPOINT [ "php", "/opt/www/bin/hyperf.php" ] +COPY otel/collectors/${COLLECTOR}.yaml /etc/otel-collector-config.yaml +COPY otel/supervisord.conf /etc/supervisord.conf +COPY otel/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh -CMD [ "start" ] +ENTRYPOINT ["/entrypoint.sh"] +CMD [] diff --git a/hyperf/README-pt-BR.md b/hyperf/8.3/README-pt-BR.md similarity index 100% rename from hyperf/README-pt-BR.md rename to hyperf/8.3/README-pt-BR.md diff --git a/hyperf/README.md b/hyperf/8.3/README.md similarity index 100% rename from hyperf/README.md rename to hyperf/8.3/README.md diff --git a/hyperf/8.3/otel/collectors/debug.yaml b/hyperf/8.3/otel/collectors/debug.yaml new file mode 100644 index 0000000..4dc8718 --- /dev/null +++ b/hyperf/8.3/otel/collectors/debug.yaml @@ -0,0 +1,22 @@ +receivers: + zipkin: + endpoint: "0.0.0.0:9411" + +processors: + batch: + send_batch_size: 200 + timeout: 5s + memory_limiter: + check_interval: 1s + limit_mib: 256 + +exporters: + debug: + verbosity: normal + +service: + pipelines: + traces: + receivers: [zipkin] + processors: [memory_limiter, batch] + exporters: [debug] diff --git a/hyperf/8.3/otel/collectors/google.yaml b/hyperf/8.3/otel/collectors/google.yaml new file mode 100644 index 0000000..663fae1 --- /dev/null +++ b/hyperf/8.3/otel/collectors/google.yaml @@ -0,0 +1,22 @@ +receivers: + zipkin: + endpoint: "0.0.0.0:9411" + +processors: + batch: + send_batch_size: 200 + timeout: 5s + memory_limiter: + check_interval: 1s + limit_mib: 256 + +exporters: + googlecloud: + project: ${GOOGLE_CLOUD_PROJECT} + +service: + pipelines: + traces: + receivers: [zipkin] + processors: [memory_limiter, batch] + exporters: [googlecloud] diff --git a/hyperf/8.3/otel/entrypoint.sh b/hyperf/8.3/otel/entrypoint.sh new file mode 100755 index 0000000..0d48cef --- /dev/null +++ b/hyperf/8.3/otel/entrypoint.sh @@ -0,0 +1,96 @@ +#!/bin/sh +set -e + +if [ "$PGBOUNCER_ENABLED" != "true" ]; then + exec supervisord -c /etc/supervisord.conf +fi + +PGBOUNCER_DATABASES="${PGBOUNCER_DATABASES:-default}" + +DEFAULT_POOL_SIZE="${PGBOUNCER_DEFAULT_POOL_SIZE:-5}" +MIN_POOL_SIZE="${PGBOUNCER_MIN_POOL_SIZE:-1}" +RESERVE_POOL_SIZE="${PGBOUNCER_RESERVE_POOL_SIZE:-2}" +MAX_CLIENT_CONN="${PGBOUNCER_MAX_CLIENT_CONN:-1000}" +MAX_PREPARED_STATEMENTS="${PGBOUNCER_MAX_PREPARED_STATEMENTS:-100}" +SERVER_IDLE_TIMEOUT="${PGBOUNCER_SERVER_IDLE_TIMEOUT:-300}" +SERVER_LIFETIME="${PGBOUNCER_SERVER_LIFETIME:-3600}" + +resolve_prefix() { + if [ "$1" = "default" ]; then + echo "POSTGRES_DB" + else + echo "POSTGRES_DB_$(echo "$1" | tr '[:lower:]' '[:upper:]')" + fi +} + +echo "[databases]" > /etc/pgbouncer/pgbouncer.ini + +OLD_IFS="$IFS" +IFS=',' +for alias in $PGBOUNCER_DATABASES; do + IFS="$OLD_IFS" + alias=$(echo "$alias" | tr -d ' ') + if [ -n "$alias" ]; then + prefix=$(resolve_prefix "$alias") + host=$(eval "printf '%s' \"\${${prefix}_HOST:-}\"") + port=$(eval "printf '%s' \"\${${prefix}_PORT:-5432}\"") + name=$(eval "printf '%s' \"\${${prefix}_NAME:-}\"") + user=$(eval "printf '%s' \"\${${prefix}_USERNAME:-}\"") + pass=$(eval "printf '%s' \"\${${prefix}_PASSWORD:-}\"") + + echo "pgb_${alias} = host=${host} port=${port} dbname=${name} user=${user} password=${pass}" >> /etc/pgbouncer/pgbouncer.ini + fi + IFS=',' +done +IFS="$OLD_IFS" + +cat >> /etc/pgbouncer/pgbouncer.ini < /etc/supervisor.d/pgbouncer.ini < Date: Thu, 4 Jun 2026 00:17:12 -0300 Subject: [PATCH 2/2] fix: address coderabbit review findings - workflows: declare explicit contents:read permissions and pass GitHub-context values through env: instead of inline ${{ }} expansion (mitigates template injection risk) - workflow_dispatch: require target input (avoids empty matrix on manual runs without input) - hyperf/8.3/Dockerfile: verify otelcol-contrib tarball sha256 against the published opentelemetry-collector-releases checksums - hyperf/8.3/otel/entrypoint.sh: whitelist PGBOUNCER_DATABASES alias chars before eval, and fail fast when a required POSTGRES_DB_* var is empty for an alias --- .github/workflows/build-images.yml | 21 ++++++++++++++++----- .github/workflows/validate.yml | 11 ++++++++++- hyperf/8.3/Dockerfile | 11 ++++++++--- hyperf/8.3/otel/entrypoint.sh | 16 ++++++++++++++++ 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 82abb14..e9aeec4 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -14,8 +14,11 @@ on: workflow_dispatch: inputs: target: - description: 'Image/version to build (e.g. hyperf/8.3 or hyperf/latest). Leave empty to detect.' - required: false + description: 'Image/version to build (e.g. hyperf/8.3 or hyperf/latest).' + required: true + +permissions: + contents: read jobs: detect: @@ -34,6 +37,10 @@ jobs: - name: Build matrix id: list shell: bash + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_TARGET: ${{ inputs.target }} + CHANGED_FILES: ${{ steps.changed.outputs.all_changed_files }} run: | set -euo pipefail @@ -42,6 +49,10 @@ jobs: collect_version() { local v="$1" [ -z "$v" ] && return + case "$v" in + [A-Za-z0-9_./-]*) ;; + *) echo "::warning::ignoring suspicious path '$v'"; return ;; + esac local depth depth=$(awk -F/ '{print NF}' <<< "$v") if [ "$depth" != "2" ]; then return; fi @@ -58,11 +69,11 @@ jobs: versions+=("$v") } - if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.target }}" ]; then - collect_version "${{ inputs.target }}" + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + collect_version "$INPUT_TARGET" else declare -a changed_dirs=() - for f in ${{ steps.changed.outputs.all_changed_files || '' }}; do + for f in $CHANGED_FILES; do dir=$(awk -F/ 'NF>=2 {print $1"/"$2}' <<< "$f") [ -z "$dir" ] && continue for existing in "${changed_dirs[@]+"${changed_dirs[@]}"}"; do diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 25d8209..da6b786 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -5,6 +5,9 @@ on: branches: - main +permissions: + contents: read + jobs: validate: runs-on: ubuntu-latest @@ -21,11 +24,17 @@ jobs: - name: Validate each manifest if: steps.changed.outputs.any_changed == 'true' shell: bash + env: + CHANGED_MANIFESTS: ${{ steps.changed.outputs.all_changed_files }} run: | set -uo pipefail errors=0 - for manifest in ${{ steps.changed.outputs.all_changed_files }}; do + for manifest in $CHANGED_MANIFESTS; do + case "$manifest" in + [A-Za-z0-9_./-]*) ;; + *) echo "::warning::skipping suspicious path '$manifest'"; continue ;; + esac echo "::group::${manifest}" dir="$(dirname "$manifest")" dockerfile="${dir}/Dockerfile" diff --git a/hyperf/8.3/Dockerfile b/hyperf/8.3/Dockerfile index ec1e343..9626fdb 100644 --- a/hyperf/8.3/Dockerfile +++ b/hyperf/8.3/Dockerfile @@ -75,9 +75,14 @@ ARG OTEL_COLLECTOR_VERSION=0.121.0 RUN apk add --no-cache --upgrade expat \ && apk add --no-cache supervisor wget pgbouncer \ - && wget -q "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_COLLECTOR_VERSION}/otelcol-contrib_${OTEL_COLLECTOR_VERSION}_linux_amd64.tar.gz" -O /tmp/otelcol.tar.gz \ - && tar -xzf /tmp/otelcol.tar.gz -C /usr/local/bin otelcol-contrib \ - && rm /tmp/otelcol.tar.gz \ + && OTEL_BASE_URL="https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_COLLECTOR_VERSION}" \ + && OTEL_ASSET="otelcol-contrib_${OTEL_COLLECTOR_VERSION}_linux_amd64.tar.gz" \ + && cd /tmp \ + && wget -q "${OTEL_BASE_URL}/${OTEL_ASSET}" -O "${OTEL_ASSET}" \ + && wget -q "${OTEL_BASE_URL}/opentelemetry-collector-releases_otelcol-contrib_checksums.txt" -O checksums.txt \ + && grep " ${OTEL_ASSET}$" checksums.txt | sha256sum -c - \ + && tar -xzf "${OTEL_ASSET}" -C /usr/local/bin otelcol-contrib \ + && rm "${OTEL_ASSET}" checksums.txt \ && chmod +x /usr/local/bin/otelcol-contrib \ && mkdir -p /etc/pgbouncer /var/log/pgbouncer /var/run/pgbouncer /etc/supervisor.d /var/run/supervisor diff --git a/hyperf/8.3/otel/entrypoint.sh b/hyperf/8.3/otel/entrypoint.sh index 0d48cef..57d6b3c 100755 --- a/hyperf/8.3/otel/entrypoint.sh +++ b/hyperf/8.3/otel/entrypoint.sh @@ -15,6 +15,15 @@ MAX_PREPARED_STATEMENTS="${PGBOUNCER_MAX_PREPARED_STATEMENTS:-100}" SERVER_IDLE_TIMEOUT="${PGBOUNCER_SERVER_IDLE_TIMEOUT:-300}" SERVER_LIFETIME="${PGBOUNCER_SERVER_LIFETIME:-3600}" +validate_alias() { + case "$1" in + *[!A-Za-z0-9_]*|"") + echo "entrypoint: invalid PGBOUNCER_DATABASES alias '$1' (allowed: [A-Za-z0-9_])" >&2 + exit 1 + ;; + esac +} + resolve_prefix() { if [ "$1" = "default" ]; then echo "POSTGRES_DB" @@ -31,6 +40,7 @@ for alias in $PGBOUNCER_DATABASES; do IFS="$OLD_IFS" alias=$(echo "$alias" | tr -d ' ') if [ -n "$alias" ]; then + validate_alias "$alias" prefix=$(resolve_prefix "$alias") host=$(eval "printf '%s' \"\${${prefix}_HOST:-}\"") port=$(eval "printf '%s' \"\${${prefix}_PORT:-5432}\"") @@ -38,6 +48,11 @@ for alias in $PGBOUNCER_DATABASES; do user=$(eval "printf '%s' \"\${${prefix}_USERNAME:-}\"") pass=$(eval "printf '%s' \"\${${prefix}_PASSWORD:-}\"") + [ -z "$host" ] && { echo "entrypoint: ${prefix}_HOST is required for alias '${alias}'" >&2; exit 1; } + [ -z "$name" ] && { echo "entrypoint: ${prefix}_NAME is required for alias '${alias}'" >&2; exit 1; } + [ -z "$user" ] && { echo "entrypoint: ${prefix}_USERNAME is required for alias '${alias}'" >&2; exit 1; } + [ -z "$pass" ] && { echo "entrypoint: ${prefix}_PASSWORD is required for alias '${alias}'" >&2; exit 1; } + echo "pgb_${alias} = host=${host} port=${port} dbname=${name} user=${user} password=${pass}" >> /etc/pgbouncer/pgbouncer.ini fi IFS=',' @@ -82,6 +97,7 @@ for alias in $PGBOUNCER_DATABASES; do IFS="$OLD_IFS" alias=$(echo "$alias" | tr -d ' ') if [ -n "$alias" ]; then + validate_alias "$alias" prefix=$(resolve_prefix "$alias") eval "export ${prefix}_HOST=127.0.0.1" eval "export ${prefix}_PORT=6432"