Compare commits
10 Commits
0fb2e2b092
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 05a62dfeb9 | |||
| 256b21e25b | |||
| 058d835f07 | |||
| f97ac2f7c5 | |||
| d8dfe1559d | |||
| 21edc39155 | |||
| f9e5b65d39 | |||
| 564d5afbfb | |||
|
|
ef77980d1c | ||
| b6501f14b0 |
397
.github/workflows/release.yml
vendored
397
.github/workflows/release.yml
vendored
@@ -3,10 +3,18 @@ name: Build and Release Module
|
|||||||
"on":
|
"on":
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
nginx_versions:
|
distro_targets:
|
||||||
description: "Space-separated Nginx versions to build against"
|
description: "Space-separated distro images to build in, for example: ubuntu:24.04 debian:12 debian:13"
|
||||||
required: true
|
required: true
|
||||||
default: "1.24.0 1.26.3 1.28.0"
|
default: "ubuntu:24.04 debian:12 debian:13"
|
||||||
|
upstream_nginx_versions:
|
||||||
|
description: "Space-separated upstream nginx.org versions for generic tarball builds"
|
||||||
|
required: true
|
||||||
|
default: "1.22.1 1.24.0 1.26.3 1.28.0 1.30.0"
|
||||||
|
target_arches:
|
||||||
|
description: "Space-separated targets: linux-x86_64 linux-arm64 linux-armv7"
|
||||||
|
required: true
|
||||||
|
default: "linux-x86_64 linux-arm64 linux-armv7"
|
||||||
version_bump:
|
version_bump:
|
||||||
description: "Semantic version part to increment from the latest v* tag"
|
description: "Semantic version part to increment from the latest v* tag"
|
||||||
required: true
|
required: true
|
||||||
@@ -17,7 +25,7 @@ name: Build and Release Module
|
|||||||
- minor
|
- minor
|
||||||
- major
|
- major
|
||||||
push_image:
|
push_image:
|
||||||
description: "Push Docker image to GHCR using the new tag"
|
description: "Push multi-platform demo Docker image to GHCR using the new tag"
|
||||||
type: boolean
|
type: boolean
|
||||||
required: true
|
required: true
|
||||||
default: true
|
default: true
|
||||||
@@ -27,8 +35,13 @@ permissions:
|
|||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_NGINX_VERSIONS: "1.24.0 1.26.3 1.28.0"
|
DEFAULT_DISTRO_TARGETS: "ubuntu:24.04 debian:12 debian:13"
|
||||||
|
DEFAULT_UPSTREAM_NGINX_VERSIONS: "1.22.1 1.24.0 1.26.3 1.28.0 1.30.0"
|
||||||
|
DEFAULT_TARGET_ARCHES: "linux-x86_64 linux-arm64 linux-armv7"
|
||||||
MODULE_NAME: ngx_http_monitoring_module
|
MODULE_NAME: ngx_http_monitoring_module
|
||||||
|
DEB_PACKAGE_NAME: libnginx-mod-http-monitoring
|
||||||
|
RELEASE_BUILD_IMAGE: ubuntu:18.04
|
||||||
|
RELEASE_LIBC_BASELINE: ubuntu-bionic-glibc-2.27
|
||||||
NGINX_CONFIGURE_ARGS: >-
|
NGINX_CONFIGURE_ARGS: >-
|
||||||
--with-compat
|
--with-compat
|
||||||
--with-http_ssl_module
|
--with-http_ssl_module
|
||||||
@@ -39,8 +52,9 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Build module and publish release
|
name: Build distro modules and publish release
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 240
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository
|
- name: Check out repository
|
||||||
@@ -112,136 +126,247 @@ jobs:
|
|||||||
echo "Latest tag: $latest_tag"
|
echo "Latest tag: $latest_tag"
|
||||||
echo "Next tag: $next_tag"
|
echo "Next tag: $next_tag"
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Normalize target architectures
|
||||||
run: |
|
id: targets
|
||||||
set -euo pipefail
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y --no-install-recommends \
|
|
||||||
build-essential \
|
|
||||||
ca-certificates \
|
|
||||||
curl \
|
|
||||||
file \
|
|
||||||
libpcre2-dev \
|
|
||||||
libssl-dev \
|
|
||||||
zlib1g-dev
|
|
||||||
|
|
||||||
- name: Build release packages
|
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
INPUT_NGINX_VERSIONS: ${{ github.event.inputs.nginx_versions }}
|
INPUT_TARGET_ARCHES: ${{ github.event.inputs.target_arches }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
versions="${INPUT_NGINX_VERSIONS:-$DEFAULT_NGINX_VERSIONS}"
|
raw_targets="${INPUT_TARGET_ARCHES:-$DEFAULT_TARGET_ARCHES}"
|
||||||
mkdir -p "$GITHUB_WORKSPACE/dist" "$GITHUB_WORKSPACE/.release-work"
|
normalized_targets=""
|
||||||
|
docker_platforms=""
|
||||||
|
|
||||||
for nginx_version in $versions; do
|
add_target() {
|
||||||
workdir="$GITHUB_WORKSPACE/.release-work/nginx-$nginx_version"
|
local target_arch="$1"
|
||||||
archive="nginx-$nginx_version.tar.gz"
|
local docker_platform="$2"
|
||||||
|
|
||||||
curl -fsSLo "$GITHUB_WORKSPACE/.release-work/$archive" \
|
case " $normalized_targets " in
|
||||||
"https://nginx.org/download/$archive"
|
*" $target_arch "*)
|
||||||
tar -xzf "$GITHUB_WORKSPACE/.release-work/$archive" \
|
return
|
||||||
-C "$GITHUB_WORKSPACE/.release-work"
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
cd "$workdir"
|
normalized_targets="${normalized_targets}${normalized_targets:+ }${target_arch}"
|
||||||
./configure \
|
docker_platforms="${docker_platforms}${docker_platforms:+,}${docker_platform}"
|
||||||
--prefix=/usr/local/nginx \
|
}
|
||||||
--sbin-path=/usr/local/sbin/nginx \
|
|
||||||
--modules-path=/usr/local/nginx/modules \
|
|
||||||
--conf-path=/etc/nginx/nginx.conf \
|
|
||||||
--error-log-path=/var/log/nginx/error.log \
|
|
||||||
--http-log-path=/var/log/nginx/access.log \
|
|
||||||
--pid-path=/var/run/nginx.pid \
|
|
||||||
--lock-path=/var/run/nginx.lock \
|
|
||||||
--http-client-body-temp-path=/var/cache/nginx/client_temp \
|
|
||||||
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
|
|
||||||
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
|
|
||||||
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
|
|
||||||
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
|
|
||||||
$NGINX_CONFIGURE_ARGS \
|
|
||||||
--add-dynamic-module="$GITHUB_WORKSPACE"
|
|
||||||
|
|
||||||
make -j"$(nproc)" modules
|
for raw_target in $raw_targets; do
|
||||||
|
case "$raw_target" in
|
||||||
package="${MODULE_NAME}-${NEXT_VERSION}-${NEXT_TAG}-nginx-${nginx_version}-linux-x86_64"
|
linux-x86_64|x86_64|amd64|linux-amd64)
|
||||||
package_dir="$GITHUB_WORKSPACE/dist/$package"
|
add_target "linux-x86_64" "linux/amd64"
|
||||||
mkdir -p "$package_dir/modules" "$package_dir/examples" "$package_dir/docs"
|
;;
|
||||||
|
linux-arm64|linux-aarch64|arm64|aarch64)
|
||||||
cp "objs/${MODULE_NAME}.so" "$package_dir/modules/${MODULE_NAME}.so"
|
add_target "linux-arm64" "linux/arm64"
|
||||||
strip --strip-unneeded "$package_dir/modules/${MODULE_NAME}.so" || true
|
;;
|
||||||
cp "$GITHUB_WORKSPACE/examples/nginx.conf" "$package_dir/examples/nginx.conf"
|
linux-armv7|linux-armhf|armv7|armhf)
|
||||||
cp "$GITHUB_WORKSPACE/examples/monitoring-site.conf" "$package_dir/examples/monitoring-site.conf"
|
add_target "linux-armv7" "linux/arm/v7"
|
||||||
cp "$GITHUB_WORKSPACE/README.md" "$package_dir/README.md"
|
;;
|
||||||
cp "$GITHUB_WORKSPACE/docs/API.md" "$package_dir/docs/API.md"
|
*)
|
||||||
cp "$GITHUB_WORKSPACE/docs/CONFIGURATION.md" "$package_dir/docs/CONFIGURATION.md"
|
echo "Unsupported target_arches entry: $raw_target" >&2
|
||||||
cp "$GITHUB_WORKSPACE/docs/PERFORMANCE.md" "$package_dir/docs/PERFORMANCE.md"
|
echo "Supported entries: linux-x86_64 linux-arm64 linux-armv7" >&2
|
||||||
|
exit 1
|
||||||
{
|
;;
|
||||||
printf 'module=%s\n' "$MODULE_NAME"
|
esac
|
||||||
printf 'module_version=%s\n' "$NEXT_VERSION"
|
|
||||||
printf 'release_tag=%s\n' "$NEXT_TAG"
|
|
||||||
printf 'previous_tag=%s\n' "$LATEST_TAG"
|
|
||||||
printf 'nginx_version=%s\n' "$nginx_version"
|
|
||||||
printf 'target=linux-x86_64\n'
|
|
||||||
printf 'runner=ubuntu-22.04\n'
|
|
||||||
printf 'git_ref=%s\n' "$GITHUB_REF"
|
|
||||||
printf 'git_sha=%s\n' "$GITHUB_SHA"
|
|
||||||
printf 'configure_args=%s\n' "$NGINX_CONFIGURE_ARGS"
|
|
||||||
printf 'built_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
||||||
} > "$package_dir/METADATA.txt"
|
|
||||||
|
|
||||||
{
|
|
||||||
printf '# %s for Nginx %s\n\n' "$MODULE_NAME" "$nginx_version"
|
|
||||||
printf 'Release: `%s`\n\n' "$NEXT_TAG"
|
|
||||||
printf 'This package contains a prebuilt Linux x86_64 dynamic module:\n\n'
|
|
||||||
printf '```text\n'
|
|
||||||
printf 'modules/%s.so\n' "$MODULE_NAME"
|
|
||||||
printf '```\n\n'
|
|
||||||
printf 'Copy it into your Nginx modules directory and add this near the top of `nginx.conf`:\n\n'
|
|
||||||
printf '```nginx\n'
|
|
||||||
printf 'load_module modules/%s.so;\n' "$MODULE_NAME"
|
|
||||||
printf '```\n\n'
|
|
||||||
printf 'This binary is built with `--with-compat` against Nginx %s on Ubuntu 22.04. ' "$nginx_version"
|
|
||||||
printf 'For safest production use, run the same Nginx version and a compatible Linux/glibc environment. '
|
|
||||||
printf 'If your Nginx package has a materially different module ABI, rebuild from source with this repository.\n'
|
|
||||||
} > "$package_dir/INSTALL.md"
|
|
||||||
|
|
||||||
(
|
|
||||||
cd "$package_dir"
|
|
||||||
sha256sum "modules/${MODULE_NAME}.so" > SHA256SUMS
|
|
||||||
file "modules/${MODULE_NAME}.so" > FILE.txt
|
|
||||||
)
|
|
||||||
|
|
||||||
tar -czf "$GITHUB_WORKSPACE/dist/$package.tar.gz" \
|
|
||||||
-C "$GITHUB_WORKSPACE/dist" "$package"
|
|
||||||
rm -rf "$package_dir"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
(
|
if [[ -z "$normalized_targets" ]]; then
|
||||||
cd "$GITHUB_WORKSPACE/dist"
|
echo "No release target architectures were selected" >&2
|
||||||
sha256sum *.tar.gz > SHA256SUMS.txt
|
exit 1
|
||||||
)
|
fi
|
||||||
|
|
||||||
{
|
{
|
||||||
printf '# ngx_http_monitoring_module %s\n\n' "$NEXT_TAG"
|
printf 'target_arches=%s\n' "$normalized_targets"
|
||||||
|
printf 'docker_platforms=%s\n' "$docker_platforms"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
echo "Release targets: $normalized_targets"
|
||||||
|
echo "Docker platforms: $docker_platforms"
|
||||||
|
|
||||||
|
- name: Set up QEMU for ARM builds
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
with:
|
||||||
|
platforms: arm64,arm
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build distro release packages
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
INPUT_DISTRO_TARGETS: ${{ github.event.inputs.distro_targets }}
|
||||||
|
NORMALIZED_TARGET_ARCHES: ${{ steps.targets.outputs.target_arches }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
distro_targets="${INPUT_DISTRO_TARGETS:-$DEFAULT_DISTRO_TARGETS}"
|
||||||
|
mkdir -p "$GITHUB_WORKSPACE/dist" "$GITHUB_WORKSPACE/.release-work"
|
||||||
|
|
||||||
|
if [[ -z "$distro_targets" ]]; then
|
||||||
|
echo "No distro_targets were selected" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for distro_image in $distro_targets; do
|
||||||
|
for target_arch in $NORMALIZED_TARGET_ARCHES; do
|
||||||
|
case "$target_arch" in
|
||||||
|
linux-x86_64)
|
||||||
|
docker_platform="linux/amd64"
|
||||||
|
;;
|
||||||
|
linux-arm64)
|
||||||
|
docker_platform="linux/arm64"
|
||||||
|
;;
|
||||||
|
linux-armv7)
|
||||||
|
docker_platform="linux/arm/v7"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unexpected normalized target: $target_arch" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "::group::Build ${distro_image} ${target_arch}"
|
||||||
|
docker run --rm -i \
|
||||||
|
--platform "$docker_platform" \
|
||||||
|
-e DEB_PACKAGE_NAME \
|
||||||
|
-e DISTRO_IMAGE="$distro_image" \
|
||||||
|
-e DOCKER_PLATFORM="$docker_platform" \
|
||||||
|
-e GITHUB_REF \
|
||||||
|
-e GITHUB_SHA \
|
||||||
|
-e LATEST_TAG \
|
||||||
|
-e MODULE_NAME \
|
||||||
|
-e NEXT_TAG \
|
||||||
|
-e NEXT_VERSION \
|
||||||
|
-e TARGET_ARCH="$target_arch" \
|
||||||
|
-v "$GITHUB_WORKSPACE:/work" \
|
||||||
|
-w /work \
|
||||||
|
"$distro_image" \
|
||||||
|
bash /work/scripts/build-distro-release.sh
|
||||||
|
echo "::endgroup::"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Build upstream nginx.org release tarballs
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
INPUT_UPSTREAM_NGINX_VERSIONS: ${{ github.event.inputs.upstream_nginx_versions }}
|
||||||
|
NORMALIZED_TARGET_ARCHES: ${{ steps.targets.outputs.target_arches }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
upstream_versions="${INPUT_UPSTREAM_NGINX_VERSIONS:-$DEFAULT_UPSTREAM_NGINX_VERSIONS}"
|
||||||
|
if [[ -z "$upstream_versions" ]]; then
|
||||||
|
echo "No upstream_nginx_versions were selected" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for target_arch in $NORMALIZED_TARGET_ARCHES; do
|
||||||
|
case "$target_arch" in
|
||||||
|
linux-x86_64)
|
||||||
|
docker_platform="linux/amd64"
|
||||||
|
;;
|
||||||
|
linux-arm64)
|
||||||
|
docker_platform="linux/arm64"
|
||||||
|
;;
|
||||||
|
linux-armv7)
|
||||||
|
docker_platform="linux/arm/v7"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unexpected normalized target: $target_arch" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "::group::Build nginx.org ${target_arch} tarballs"
|
||||||
|
docker run --rm -i \
|
||||||
|
--platform "$docker_platform" \
|
||||||
|
-e BUILD_NGINX_VERSIONS="$upstream_versions" \
|
||||||
|
-e DIST_DIR=/work/dist \
|
||||||
|
-e DOCKER_PLATFORM="$docker_platform" \
|
||||||
|
-e GITHUB_REF \
|
||||||
|
-e GITHUB_SHA \
|
||||||
|
-e LATEST_TAG \
|
||||||
|
-e MODULE_NAME \
|
||||||
|
-e NEXT_TAG \
|
||||||
|
-e NEXT_VERSION \
|
||||||
|
-e NGINX_CONFIGURE_ARGS \
|
||||||
|
-e RELEASE_BUILD_IMAGE \
|
||||||
|
-e RELEASE_LIBC_BASELINE \
|
||||||
|
-e TARGET_ARCH="$target_arch" \
|
||||||
|
-v "$GITHUB_WORKSPACE:/work" \
|
||||||
|
-w /work \
|
||||||
|
"$RELEASE_BUILD_IMAGE" \
|
||||||
|
bash /work/scripts/build-upstream-release.sh
|
||||||
|
echo "::endgroup::"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Generate release manifests and notes
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$GITHUB_WORKSPACE/dist"
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
compatibility_files=( *.compatibility.json )
|
||||||
|
if [[ "${#compatibility_files[@]}" -eq 0 ]]; then
|
||||||
|
echo "No compatibility metadata files were generated" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
jq -s \
|
||||||
|
--arg releaseTag "$NEXT_TAG" \
|
||||||
|
--arg generatedAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
|
'{
|
||||||
|
schema: 1,
|
||||||
|
release_tag: $releaseTag,
|
||||||
|
generated_at: $generatedAt,
|
||||||
|
artifacts: .
|
||||||
|
}' \
|
||||||
|
"${compatibility_files[@]}" > COMPATIBILITY-MATRIX.json
|
||||||
|
|
||||||
|
find . -maxdepth 1 -type f \
|
||||||
|
\( -name '*.deb' -o -name '*.tar.gz' -o -name '*.compatibility.json' -o -name 'COMPATIBILITY-MATRIX.json' \) \
|
||||||
|
-print0 \
|
||||||
|
| sort -z \
|
||||||
|
| xargs -0 sha256sum > SHA256SUMS.txt
|
||||||
|
|
||||||
|
{
|
||||||
|
printf '# %s %s\n\n' "$MODULE_NAME" "$NEXT_TAG"
|
||||||
printf 'Previous tag: `%s`\n\n' "$LATEST_TAG"
|
printf 'Previous tag: `%s`\n\n' "$LATEST_TAG"
|
||||||
printf 'Prebuilt Linux x86_64 dynamic module packages are attached.\n\n'
|
printf 'This release includes two artifact tiers:\n\n'
|
||||||
printf 'Each tarball is built against the Nginx version in its filename and includes:\n\n'
|
printf -- '- distro `.deb` packages built from Ubuntu/Debian Nginx source packages and pinned to the matching packaged Nginx revision\n'
|
||||||
printf -- '- `modules/%s.so`\n' "$MODULE_NAME"
|
printf -- '- nginx.org upstream-version tarballs for users who build or run Nginx from upstream source versions\n\n'
|
||||||
printf -- '- `INSTALL.md`\n'
|
printf 'Recommended production artifacts are the `.deb` packages. They depend on the exact distro `nginx-abi-*` virtual package when the distribution exposes one and on the exact Nginx binary package version used for the build. That strict pin prevents Nginx package revisions from silently reusing an unrebuilt module.\n\n'
|
||||||
printf -- '- `METADATA.txt`\n'
|
printf 'The nginx.org `.tar.gz` packages preserve upstream-version coverage but are not a safe substitute for distro packages on Ubuntu/Debian packaged Nginx.\n\n'
|
||||||
printf -- '- `SHA256SUMS`\n'
|
printf '## Distro Package Matrix\n\n'
|
||||||
printf -- '- example configuration and API docs\n\n'
|
jq -r '
|
||||||
printf 'Nginx dynamic modules are ABI-sensitive. Use the package matching your Nginx version and rebuild locally if your Nginx distribution uses incompatible build options.\n'
|
.artifacts[]
|
||||||
} > "$GITHUB_WORKSPACE/dist/RELEASE_NOTES.md"
|
| select(.build.method == "distro-source-package")
|
||||||
|
| "- `\(.artifacts.deb_file)`: \(.target.os_id) \(.target.os_version) \(.target.architecture), "
|
||||||
|
+ "\(.nginx.binary_package) \(.nginx.binary_version), source \(.nginx.source_package) \(.nginx.source_version), "
|
||||||
|
+ "ABI `\(if .nginx.abi == "" then "package-version-pinned" else .nginx.abi end)`"
|
||||||
|
' COMPATIBILITY-MATRIX.json
|
||||||
|
printf '\n## nginx.org Tarballs\n\n'
|
||||||
|
jq -r '
|
||||||
|
.artifacts[]
|
||||||
|
| select(.build.method == "upstream-nginxorg-source")
|
||||||
|
| "- `\(.artifacts.tarball_file)`: nginx.org \(.nginx.source_version), \(.target.architecture), baseline `\(.target.libc_baseline)`"
|
||||||
|
' COMPATIBILITY-MATRIX.json
|
||||||
|
printf '\n## Validation\n\n'
|
||||||
|
printf 'Distro `.deb` artifacts are installed into their target distro containers and validated with packaged `nginx -t` plus live endpoint checks. nginx.org tarballs are validated with the Nginx binary built from the same upstream source during the tarball build.\n'
|
||||||
|
} > RELEASE_NOTES.md
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ngx-http-monitoring-module-${{ steps.version.outputs.next_tag }}-release-assets
|
name: ngx-http-monitoring-module-${{ steps.version.outputs.next_tag }}-release-assets
|
||||||
path: |
|
path: |
|
||||||
|
dist/*.deb
|
||||||
dist/*.tar.gz
|
dist/*.tar.gz
|
||||||
|
dist/*.compatibility.json
|
||||||
|
dist/COMPATIBILITY-MATRIX.json
|
||||||
dist/SHA256SUMS.txt
|
dist/SHA256SUMS.txt
|
||||||
dist/RELEASE_NOTES.md
|
dist/RELEASE_NOTES.md
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
@@ -260,25 +385,43 @@ jobs:
|
|||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
gh release create "$NEXT_TAG" dist/*.tar.gz dist/SHA256SUMS.txt \
|
gh release create "$NEXT_TAG" \
|
||||||
|
dist/*.deb \
|
||||||
|
dist/*.tar.gz \
|
||||||
|
dist/*.compatibility.json \
|
||||||
|
dist/COMPATIBILITY-MATRIX.json \
|
||||||
|
dist/SHA256SUMS.txt \
|
||||||
--title "$NEXT_TAG" \
|
--title "$NEXT_TAG" \
|
||||||
--notes-file dist/RELEASE_NOTES.md
|
--notes-file dist/RELEASE_NOTES.md
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Compute Docker image name
|
||||||
if: github.event.inputs.push_image == 'true'
|
if: github.event.inputs.push_image == 'true'
|
||||||
env:
|
id: image
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
image_repo="$(echo "ghcr.io/${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')"
|
image_repo="$(echo "ghcr.io/${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
printf 'repo=%s\n' "$image_repo" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
docker build \
|
- name: Log in to GHCR
|
||||||
-f dockerized/Dockerfile \
|
if: github.event.inputs.push_image == 'true'
|
||||||
-t "$image_repo:$NEXT_TAG" \
|
uses: docker/login-action@v3
|
||||||
-t "$image_repo:latest" \
|
with:
|
||||||
.
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ github.token }}
|
||||||
|
|
||||||
echo "$GH_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
|
- name: Build and push demo Docker image
|
||||||
docker push "$image_repo:$NEXT_TAG"
|
if: github.event.inputs.push_image == 'true'
|
||||||
docker push "$image_repo:latest"
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: dockerized/Dockerfile
|
||||||
|
platforms: ${{ steps.targets.outputs.docker_platforms }}
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
BASE_IMAGE=debian:bookworm-slim
|
||||||
|
OPENSSL_RUNTIME_PACKAGE=libssl3
|
||||||
|
tags: |
|
||||||
|
${{ steps.image.outputs.repo }}:${{ steps.version.outputs.next_tag }}
|
||||||
|
${{ steps.image.outputs.repo }}:latest
|
||||||
|
|||||||
231
.github/workflows/test.yml
vendored
231
.github/workflows/test.yml
vendored
@@ -13,6 +13,7 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
TEST_BUILD_IMAGE: ubuntu:18.04
|
||||||
NGINX_CONFIGURE_ARGS: >-
|
NGINX_CONFIGURE_ARGS: >-
|
||||||
--with-compat
|
--with-compat
|
||||||
--with-http_ssl_module
|
--with-http_ssl_module
|
||||||
@@ -23,27 +24,60 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-configure:
|
build-and-configure:
|
||||||
name: Nginx ${{ matrix.nginx-version }}
|
name: Nginx ${{ matrix.nginx-version }} (${{ matrix.target.name }})
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
timeout-minutes: 30
|
timeout-minutes: 90
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
nginx-version:
|
nginx-version:
|
||||||
|
- "1.22.1"
|
||||||
- "1.24.0"
|
- "1.24.0"
|
||||||
- "1.26.3"
|
- "1.26.3"
|
||||||
- "1.28.0"
|
- "1.28.0"
|
||||||
|
- "1.30.0"
|
||||||
|
target:
|
||||||
|
- name: linux-x86_64
|
||||||
|
platform: linux/amd64
|
||||||
|
- name: linux-arm64
|
||||||
|
platform: linux/arm64
|
||||||
|
- name: linux-armv7
|
||||||
|
platform: linux/arm/v7
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository
|
- name: Check out repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Set up QEMU for ARM tests
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
with:
|
||||||
|
platforms: arm64,arm
|
||||||
|
|
||||||
|
- name: Build, configure, and test routes
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
NGINX_VERSION: ${{ matrix.nginx-version }}
|
||||||
|
TARGET_ARCH: ${{ matrix.target.name }}
|
||||||
|
DOCKER_PLATFORM: ${{ matrix.target.platform }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y --no-install-recommends \
|
docker run --rm -i \
|
||||||
|
--platform "$DOCKER_PLATFORM" \
|
||||||
|
-e NGINX_VERSION \
|
||||||
|
-e TARGET_ARCH \
|
||||||
|
-e NGINX_CONFIGURE_ARGS \
|
||||||
|
-v "$GITHUB_WORKSPACE:/work" \
|
||||||
|
-w /work \
|
||||||
|
"$TEST_BUILD_IMAGE" \
|
||||||
|
bash -s <<'CI_SCRIPT'
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
@@ -52,26 +86,32 @@ jobs:
|
|||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
zlib1g-dev
|
zlib1g-dev
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
- name: Download Nginx source
|
build_jobs="$(nproc)"
|
||||||
shell: bash
|
if [[ "$TARGET_ARCH" != "linux-x86_64" && "$build_jobs" -gt 2 ]]; then
|
||||||
env:
|
build_jobs=2
|
||||||
NGINX_VERSION: ${{ matrix.nginx-version }}
|
fi
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
mkdir -p "$GITHUB_WORKSPACE/.ci-nginx"
|
|
||||||
curl -fsSLo "$GITHUB_WORKSPACE/.ci-nginx/nginx-${NGINX_VERSION}.tar.gz" \
|
|
||||||
"https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz"
|
|
||||||
tar -xzf "$GITHUB_WORKSPACE/.ci-nginx/nginx-${NGINX_VERSION}.tar.gz" \
|
|
||||||
-C "$GITHUB_WORKSPACE/.ci-nginx"
|
|
||||||
|
|
||||||
- name: Configure Nginx with module
|
nginx_root="/work/.ci-nginx/${TARGET_ARCH}"
|
||||||
shell: bash
|
runtime_root="/work/.ci-runtime/${TARGET_ARCH}/nginx-${NGINX_VERSION}"
|
||||||
env:
|
archive="nginx-${NGINX_VERSION}.tar.gz"
|
||||||
NGINX_VERSION: ${{ matrix.nginx-version }}
|
archive_path="${nginx_root}/${archive}"
|
||||||
run: |
|
nginx_src="${nginx_root}/nginx-${NGINX_VERSION}"
|
||||||
set -euo pipefail
|
module_path="${nginx_src}/objs/ngx_http_monitoring_module.so"
|
||||||
cd "$GITHUB_WORKSPACE/.ci-nginx/nginx-${NGINX_VERSION}"
|
config_path="${runtime_root}/nginx.conf"
|
||||||
|
base_url="http://127.0.0.1:18080"
|
||||||
|
|
||||||
|
mkdir -p "$nginx_root"
|
||||||
|
if [[ ! -f "$archive_path" ]]; then
|
||||||
|
curl -fsSLo "$archive_path" \
|
||||||
|
"https://nginx.org/download/${archive}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$nginx_src" "$runtime_root"
|
||||||
|
tar -xzf "$archive_path" -C "$nginx_root"
|
||||||
|
|
||||||
|
cd "$nginx_src"
|
||||||
./configure \
|
./configure \
|
||||||
--prefix=/usr/local/nginx \
|
--prefix=/usr/local/nginx \
|
||||||
--sbin-path=/usr/local/sbin/nginx \
|
--sbin-path=/usr/local/sbin/nginx \
|
||||||
@@ -87,58 +127,39 @@ jobs:
|
|||||||
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
|
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
|
||||||
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
|
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
|
||||||
$NGINX_CONFIGURE_ARGS \
|
$NGINX_CONFIGURE_ARGS \
|
||||||
--add-dynamic-module="$GITHUB_WORKSPACE"
|
--add-dynamic-module=/work
|
||||||
|
|
||||||
- name: Build Nginx binary and dynamic module
|
make -j"$build_jobs"
|
||||||
shell: bash
|
make -j"$build_jobs" modules
|
||||||
env:
|
|
||||||
NGINX_VERSION: ${{ matrix.nginx-version }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
cd "$GITHUB_WORKSPACE/.ci-nginx/nginx-${NGINX_VERSION}"
|
|
||||||
make -j"$(nproc)"
|
|
||||||
make -j"$(nproc)" modules
|
|
||||||
test -x objs/nginx
|
test -x objs/nginx
|
||||||
test -f objs/ngx_http_monitoring_module.so
|
test -f "$module_path"
|
||||||
file objs/nginx
|
file objs/nginx
|
||||||
file objs/ngx_http_monitoring_module.so
|
file "$module_path"
|
||||||
|
|
||||||
- name: Validate generated Nginx configuration
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
NGINX_VERSION: ${{ matrix.nginx-version }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
nginx_src="$GITHUB_WORKSPACE/.ci-nginx/nginx-${NGINX_VERSION}"
|
|
||||||
run_root="$GITHUB_WORKSPACE/.ci-runtime/nginx-${NGINX_VERSION}"
|
|
||||||
module_path="$nginx_src/objs/ngx_http_monitoring_module.so"
|
|
||||||
config_path="$run_root/nginx.conf"
|
|
||||||
|
|
||||||
mkdir -p \
|
mkdir -p \
|
||||||
"$run_root/logs" \
|
"$runtime_root/logs" \
|
||||||
"$run_root/temp/client_body" \
|
"$runtime_root/temp/client_body" \
|
||||||
"$run_root/temp/proxy" \
|
"$runtime_root/temp/proxy" \
|
||||||
"$run_root/temp/fastcgi" \
|
"$runtime_root/temp/fastcgi" \
|
||||||
"$run_root/temp/uwsgi" \
|
"$runtime_root/temp/uwsgi" \
|
||||||
"$run_root/temp/scgi"
|
"$runtime_root/temp/scgi"
|
||||||
|
|
||||||
{
|
{
|
||||||
printf 'load_module %s;\n\n' "$module_path"
|
printf 'load_module %s;\n\n' "$module_path"
|
||||||
printf 'worker_processes 1;\n'
|
printf 'worker_processes 1;\n'
|
||||||
printf 'error_log %s/logs/error.log info;\n' "$run_root"
|
printf 'error_log %s/logs/error.log info;\n' "$runtime_root"
|
||||||
printf 'pid %s/logs/nginx.pid;\n\n' "$run_root"
|
printf 'pid %s/logs/nginx.pid;\n\n' "$runtime_root"
|
||||||
printf 'events {\n'
|
printf 'events {\n'
|
||||||
printf ' worker_connections 1024;\n'
|
printf ' worker_connections 1024;\n'
|
||||||
printf '}\n\n'
|
printf '}\n\n'
|
||||||
printf 'http {\n'
|
printf 'http {\n'
|
||||||
printf ' access_log off;\n'
|
printf ' access_log off;\n'
|
||||||
printf ' default_type application/octet-stream;\n'
|
printf ' default_type application/octet-stream;\n'
|
||||||
printf ' client_body_temp_path %s/temp/client_body;\n' "$run_root"
|
printf ' client_body_temp_path %s/temp/client_body;\n' "$runtime_root"
|
||||||
printf ' proxy_temp_path %s/temp/proxy;\n' "$run_root"
|
printf ' proxy_temp_path %s/temp/proxy;\n' "$runtime_root"
|
||||||
printf ' fastcgi_temp_path %s/temp/fastcgi;\n' "$run_root"
|
printf ' fastcgi_temp_path %s/temp/fastcgi;\n' "$runtime_root"
|
||||||
printf ' uwsgi_temp_path %s/temp/uwsgi;\n' "$run_root"
|
printf ' uwsgi_temp_path %s/temp/uwsgi;\n' "$runtime_root"
|
||||||
printf ' scgi_temp_path %s/temp/scgi;\n\n' "$run_root"
|
printf ' scgi_temp_path %s/temp/scgi;\n\n' "$runtime_root"
|
||||||
printf ' monitor_refresh_interval 1s;\n'
|
printf ' monitor_refresh_interval 1s;\n'
|
||||||
printf ' monitor_history 1m;\n'
|
printf ' monitor_history 1m;\n'
|
||||||
printf ' monitor_resolution 1s;\n'
|
printf ' monitor_resolution 1s;\n'
|
||||||
@@ -168,22 +189,14 @@ jobs:
|
|||||||
printf '}\n'
|
printf '}\n'
|
||||||
} > "$config_path"
|
} > "$config_path"
|
||||||
|
|
||||||
"$nginx_src/objs/nginx" -t -p "$run_root" -c "$config_path"
|
"$nginx_src/objs/nginx" -t -p "$runtime_root" -c "$config_path"
|
||||||
|
|
||||||
- name: Run Nginx and test monitoring routes
|
cleanup() {
|
||||||
shell: bash
|
"$nginx_src/objs/nginx" -p "$runtime_root" -c "$config_path" -s quit || true
|
||||||
env:
|
}
|
||||||
NGINX_VERSION: ${{ matrix.nginx-version }}
|
trap cleanup EXIT
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
nginx_src="$GITHUB_WORKSPACE/.ci-nginx/nginx-${NGINX_VERSION}"
|
"$nginx_src/objs/nginx" -p "$runtime_root" -c "$config_path"
|
||||||
run_root="$GITHUB_WORKSPACE/.ci-runtime/nginx-${NGINX_VERSION}"
|
|
||||||
config_path="$run_root/nginx.conf"
|
|
||||||
base_url="http://127.0.0.1:18080"
|
|
||||||
|
|
||||||
"$nginx_src/objs/nginx" -p "$run_root" -c "$config_path"
|
|
||||||
trap '"$nginx_src/objs/nginx" -p "$run_root" -c "$config_path" -s quit || true' EXIT
|
|
||||||
|
|
||||||
for attempt in $(seq 1 50); do
|
for attempt in $(seq 1 50); do
|
||||||
if curl -fsS "$base_url/" >/dev/null; then
|
if curl -fsS "$base_url/" >/dev/null; then
|
||||||
@@ -241,13 +254,77 @@ jobs:
|
|||||||
sse_output="$(timeout 5s curl -fsS -N "$base_url/monitor/live" || true)"
|
sse_output="$(timeout 5s curl -fsS -N "$base_url/monitor/live" || true)"
|
||||||
printf '%s' "$sse_output" | grep -q '^event: metrics'
|
printf '%s' "$sse_output" | grep -q '^event: metrics'
|
||||||
printf '%s' "$sse_output" | grep -q '^data: {'
|
printf '%s' "$sse_output" | grep -q '^data: {'
|
||||||
|
CI_SCRIPT
|
||||||
|
|
||||||
- name: Upload failed nginx logs
|
- name: Upload failed nginx logs
|
||||||
if: failure()
|
if: failure()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: nginx-${{ matrix.nginx-version }}-test-logs
|
name: nginx-${{ matrix.nginx-version }}-${{ matrix.target.name }}-test-logs
|
||||||
path: |
|
path: |
|
||||||
.ci-runtime/nginx-${{ matrix.nginx-version }}/logs/*.log
|
.ci-runtime/${{ matrix.target.name }}/nginx-${{ matrix.nginx-version }}/logs/*.log
|
||||||
.ci-runtime/nginx-${{ matrix.nginx-version }}/nginx.conf
|
.ci-runtime/${{ matrix.target.name }}/nginx-${{ matrix.nginx-version }}/nginx.conf
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
distro-package-compat:
|
||||||
|
name: Distro package compatibility (${{ matrix.distro-image }})
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 90
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
distro-image:
|
||||||
|
- ubuntu:24.04
|
||||||
|
- debian:12
|
||||||
|
- debian:13
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build package and validate against distro nginx
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
DEB_PACKAGE_NAME: libnginx-mod-http-monitoring
|
||||||
|
DISTRO_IMAGE: ${{ matrix.distro-image }}
|
||||||
|
DOCKER_PLATFORM: linux/amd64
|
||||||
|
LATEST_TAG: none
|
||||||
|
MODULE_NAME: ngx_http_monitoring_module
|
||||||
|
NEXT_TAG: ci
|
||||||
|
NEXT_VERSION: 0.0.0
|
||||||
|
TARGET_ARCH: linux-x86_64
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
mkdir -p "$GITHUB_WORKSPACE/.ci-distro-dist" "$GITHUB_WORKSPACE/.ci-distro-work"
|
||||||
|
|
||||||
|
docker run --rm -i \
|
||||||
|
--platform "$DOCKER_PLATFORM" \
|
||||||
|
-e DEB_PACKAGE_NAME \
|
||||||
|
-e DISTRO_IMAGE \
|
||||||
|
-e DIST_DIR=/work/.ci-distro-dist \
|
||||||
|
-e DOCKER_PLATFORM \
|
||||||
|
-e GITHUB_REF \
|
||||||
|
-e GITHUB_SHA \
|
||||||
|
-e LATEST_TAG \
|
||||||
|
-e MODULE_NAME \
|
||||||
|
-e NEXT_TAG \
|
||||||
|
-e NEXT_VERSION \
|
||||||
|
-e TARGET_ARCH \
|
||||||
|
-e WORK_ROOT=/work/.ci-distro-work \
|
||||||
|
-v "$GITHUB_WORKSPACE:/work" \
|
||||||
|
-w /work \
|
||||||
|
"$DISTRO_IMAGE" \
|
||||||
|
bash /work/scripts/build-distro-release.sh
|
||||||
|
|
||||||
|
- name: Upload distro compatibility artifacts
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: distro-package-compat-${{ strategy.job-index }}
|
||||||
|
path: |
|
||||||
|
.ci-distro-dist/*.deb
|
||||||
|
.ci-distro-dist/*.tar.gz
|
||||||
|
.ci-distro-dist/*.compatibility.json
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Meghdad
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -36,6 +36,8 @@ The compiled module is written to:
|
|||||||
build/ngx_http_monitoring_module.so
|
build/ngx_http_monitoring_module.so
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The source avoids glibc-only mount parsing APIs such as `getmntent()` and parses `/proc/mounts` directly, which keeps the collectors portable across Linux libc implementations that expose the required `/proc`, `statvfs()`, and `getifaddrs()` interfaces.
|
||||||
|
|
||||||
## Docker Image
|
## Docker Image
|
||||||
|
|
||||||
A Dockerized Nginx image is available in [dockerized](</dockerized>):
|
A Dockerized Nginx image is available in [dockerized](</dockerized>):
|
||||||
@@ -57,11 +59,11 @@ A reusable Codex skill for clients and agents is available at `skills/ngx-http-m
|
|||||||
|
|
||||||
## GitHub Releases
|
## GitHub Releases
|
||||||
|
|
||||||
The repository includes a manual GitHub Actions workflow at `.github/workflows/release.yml` that computes the next `vMAJOR.MINOR.PATCH` tag, builds Linux x86_64 dynamic module tarballs, pushes the tag, and publishes the release. See `docs/RELEASES.md` for release asset format and compatibility notes.
|
The repository includes a manual GitHub Actions workflow at `.github/workflows/release.yml` that computes the next `vMAJOR.MINOR.PATCH` tag, builds Ubuntu/Debian distro-specific module packages, keeps generic nginx.org source-version tarballs, validates the artifacts, pushes the tag, and publishes the release. Production installs on Ubuntu/Debian should prefer the generated `.deb` packages because they depend on the matching distro Nginx ABI and exact Nginx binary package revision. See `docs/RELEASES.md` for release asset format and compatibility notes.
|
||||||
|
|
||||||
## CI Tests
|
## CI Tests
|
||||||
|
|
||||||
`.github/workflows/test.yml` runs on pushes and pull requests. It builds Nginx and the dynamic module against common Nginx versions, runs `nginx -t`, starts the built server, and checks the dashboard, JSON API, health, Prometheus, and SSE routes.
|
`.github/workflows/test.yml` runs on pushes and pull requests. It builds Nginx and the dynamic module against common upstream Nginx versions as a broad source sanity check, and also builds distro packages against Ubuntu/Debian packaged Nginx on amd64. Both paths run `nginx -t`, start Nginx, and check the dashboard, JSON API, health, Prometheus, and SSE routes.
|
||||||
|
|
||||||
## Load The Module
|
## Load The Module
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ARG DEBIAN_VERSION=bookworm
|
ARG BASE_IMAGE=debian:bookworm-slim
|
||||||
ARG NGINX_VERSION=1.28.0
|
ARG NGINX_VERSION=1.28.0
|
||||||
|
|
||||||
FROM debian:${DEBIAN_VERSION}-slim AS builder
|
FROM ${BASE_IMAGE} AS builder
|
||||||
|
|
||||||
ARG NGINX_VERSION
|
ARG NGINX_VERSION
|
||||||
ARG NGINX_CONFIGURE_ARGS="--with-compat --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_v2_module --with-http_gzip_static_module"
|
ARG NGINX_CONFIGURE_ARGS="--with-compat --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_v2_module --with-http_gzip_static_module"
|
||||||
@@ -48,9 +48,10 @@ RUN ./configure \
|
|||||||
&& cp objs/ngx_http_monitoring_module.so /usr/local/nginx/modules/
|
&& cp objs/ngx_http_monitoring_module.so /usr/local/nginx/modules/
|
||||||
|
|
||||||
|
|
||||||
FROM debian:${DEBIAN_VERSION}-slim AS runtime
|
FROM ${BASE_IMAGE} AS runtime
|
||||||
|
|
||||||
ARG NGINX_VERSION
|
ARG NGINX_VERSION
|
||||||
|
ARG OPENSSL_RUNTIME_PACKAGE=libssl3
|
||||||
|
|
||||||
LABEL org.opencontainers.image.title="ngx_http_monitoring_module nginx image" \
|
LABEL org.opencontainers.image.title="ngx_http_monitoring_module nginx image" \
|
||||||
org.opencontainers.image.description="Nginx built with ngx_http_monitoring_module and embedded monitoring dashboard" \
|
org.opencontainers.image.description="Nginx built with ngx_http_monitoring_module and embedded monitoring dashboard" \
|
||||||
@@ -60,7 +61,7 @@ RUN apt-get update \
|
|||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
libpcre2-8-0 \
|
libpcre2-8-0 \
|
||||||
libssl3 \
|
${OPENSSL_RUNTIME_PACKAGE} \
|
||||||
zlib1g \
|
zlib1g \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& groupadd --system --gid 101 nginx \
|
&& groupadd --system --gid 101 nginx \
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ To choose a different Nginx version:
|
|||||||
NGINX_VERSION=1.28.0 sh dockerized/build.sh
|
NGINX_VERSION=1.28.0 sh dockerized/build.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To build the image on an Ubuntu Bionic 18.04 libc baseline:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
BASE_IMAGE=ubuntu:18.04 OPENSSL_RUNTIME_PACKAGE=libssl1.1 sh dockerized/build.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ set -eu
|
|||||||
|
|
||||||
IMAGE="${IMAGE:-ngx-http-monitoring-module:local}"
|
IMAGE="${IMAGE:-ngx-http-monitoring-module:local}"
|
||||||
NGINX_VERSION="${NGINX_VERSION:-1.28.0}"
|
NGINX_VERSION="${NGINX_VERSION:-1.28.0}"
|
||||||
|
BASE_IMAGE="${BASE_IMAGE:-debian:bookworm-slim}"
|
||||||
|
OPENSSL_RUNTIME_PACKAGE="${OPENSSL_RUNTIME_PACKAGE:-libssl3}"
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
docker build \
|
docker build \
|
||||||
--build-arg "NGINX_VERSION=$NGINX_VERSION" \
|
--build-arg "NGINX_VERSION=$NGINX_VERSION" \
|
||||||
|
--build-arg "BASE_IMAGE=$BASE_IMAGE" \
|
||||||
|
--build-arg "OPENSSL_RUNTIME_PACKAGE=$OPENSSL_RUNTIME_PACKAGE" \
|
||||||
-f dockerized/Dockerfile \
|
-f dockerized/Dockerfile \
|
||||||
-t "$IMAGE" \
|
-t "$IMAGE" \
|
||||||
.
|
.
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ services:
|
|||||||
context: ..
|
context: ..
|
||||||
dockerfile: dockerized/Dockerfile
|
dockerfile: dockerized/Dockerfile
|
||||||
args:
|
args:
|
||||||
|
BASE_IMAGE: "debian:bookworm-slim"
|
||||||
NGINX_VERSION: "1.28.0"
|
NGINX_VERSION: "1.28.0"
|
||||||
|
OPENSSL_RUNTIME_PACKAGE: "libssl3"
|
||||||
image: ngx-http-monitoring-module:local
|
image: ngx-http-monitoring-module:local
|
||||||
container_name: ngx-http-monitoring-module
|
container_name: ngx-http-monitoring-module
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -58,3 +58,5 @@ Upstream metrics are passively observed from Nginx upstream state recorded on pr
|
|||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
The module is designed as an Nginx dynamic HTTP module and should be built with `--with-compat` against the target Nginx source tree. Stub-status counters are compiled in when the target Nginx build includes `--with-http_stub_status_module`; otherwise request counters still work but low-level active/reading/writing/waiting counters remain zero.
|
The module is designed as an Nginx dynamic HTTP module and should be built with `--with-compat` against the target Nginx source tree. Stub-status counters are compiled in when the target Nginx build includes `--with-http_stub_status_module`; otherwise request counters still work but low-level active/reading/writing/waiting counters remain zero.
|
||||||
|
|
||||||
|
Collectors avoid glibc-only helpers such as `getmntent()` and parse `/proc/mounts` directly. This keeps filesystem collection compatible with libc implementations that do not ship `<mntent.h>`, while still requiring Linux `/proc`, `statvfs()`, and `getifaddrs()`.
|
||||||
|
|||||||
163
docs/RELEASES.md
163
docs/RELEASES.md
@@ -1,60 +1,165 @@
|
|||||||
# Release Builds
|
# Release Builds
|
||||||
|
|
||||||
The GitHub Actions workflow in `.github/workflows/release.yml` builds ready-to-use Linux x86_64 dynamic module packages.
|
The release workflow in `.github/workflows/release.yml` builds two artifact
|
||||||
|
tiers:
|
||||||
|
|
||||||
## Manual Release Only
|
- production-oriented `.deb` packages against Ubuntu and Debian Nginx source
|
||||||
|
packages
|
||||||
|
- generic `.tar.gz` packages against upstream `nginx.org` source versions
|
||||||
|
|
||||||
The workflow runs only through GitHub Actions `workflow_dispatch`. It does not run on pushes, pull requests, or tag pushes.
|
## Why Distro Builds
|
||||||
|
|
||||||
Open the workflow in GitHub Actions and choose:
|
Nginx dynamic modules are ABI-sensitive. `--with-compat` relaxes some module
|
||||||
|
signature checks, but it does not make a module independent from the Nginx
|
||||||
|
source package, distro patchset, compiler flags, configure arguments, or module
|
||||||
|
ABI selected by a Debian or Ubuntu package maintainer.
|
||||||
|
|
||||||
- `nginx_versions`: space-separated versions, for example `1.24.0 1.26.3 1.28.0`
|
An upstream `nginx-1.24.0` build can load into an Ubuntu `nginx` package and
|
||||||
|
still behave incorrectly at runtime after a package rebuild such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1.24.0-2ubuntu7.9 -> 1.24.0-2ubuntu7.10
|
||||||
|
```
|
||||||
|
|
||||||
|
The safer production rule is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
build from the distro source package that produced the nginx binary you run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Release
|
||||||
|
|
||||||
|
The workflow runs through GitHub Actions `workflow_dispatch`.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
|
||||||
|
- `distro_targets`: space-separated container images, default
|
||||||
|
`ubuntu:24.04 debian:12 debian:13`
|
||||||
|
- `upstream_nginx_versions`: space-separated upstream source versions, default
|
||||||
|
`1.22.1 1.24.0 1.26.3 1.28.0 1.30.0`
|
||||||
|
- `target_arches`: `linux-x86_64`, `linux-arm64`, and `linux-armv7`
|
||||||
- `version_bump`: `patch`, `minor`, or `major`
|
- `version_bump`: `patch`, `minor`, or `major`
|
||||||
- `push_image`: whether to push the Docker image to GHCR
|
- `push_image`: push the demo Docker image to GHCR
|
||||||
|
|
||||||
The workflow fetches existing `vMAJOR.MINOR.PATCH` tags, computes the next semantic version, creates an annotated tag, pushes it, and publishes the GitHub release.
|
The workflow fetches existing `vMAJOR.MINOR.PATCH` tags, computes the next tag,
|
||||||
|
builds all release artifacts, creates an annotated tag, pushes it, and publishes
|
||||||
|
the GitHub release.
|
||||||
|
|
||||||
Examples:
|
## Distro Build Strategy
|
||||||
|
|
||||||
|
For every distro and architecture, the workflow:
|
||||||
|
|
||||||
|
1. Starts the target distro container for the target CPU platform.
|
||||||
|
2. Enables `deb-src` repositories.
|
||||||
|
3. Installs the distro `nginx` package.
|
||||||
|
4. Downloads the exact matching `apt source nginx` package version.
|
||||||
|
5. Installs the Nginx source package build dependencies.
|
||||||
|
6. Replays the packaged `nginx -V` configure arguments, rewriting distro module
|
||||||
|
source paths into the downloaded source tree when needed.
|
||||||
|
7. Adds this repository as a dynamic module and builds with the distro Nginx
|
||||||
|
source package.
|
||||||
|
8. Builds a `.deb` package and a manual `.tar.gz` package.
|
||||||
|
9. Installs the `.deb` package in the same container.
|
||||||
|
10. Validates the installed module with packaged `nginx -t` and live endpoint
|
||||||
|
checks for `/monitor`, `/monitor/api`, `/monitor/health`,
|
||||||
|
`/monitor/metrics`, and `/monitor/live`.
|
||||||
|
|
||||||
|
## nginx.org Build Strategy
|
||||||
|
|
||||||
|
For every requested upstream Nginx version and architecture, the workflow:
|
||||||
|
|
||||||
|
1. Starts the baseline `ubuntu:18.04` build container for the target CPU
|
||||||
|
platform.
|
||||||
|
2. Downloads the requested `https://nginx.org/download/nginx-<version>.tar.gz`
|
||||||
|
source archive.
|
||||||
|
3. Configures Nginx with the repository's compatibility build flags and this
|
||||||
|
module as a dynamic module.
|
||||||
|
4. Builds the Nginx binary and module.
|
||||||
|
5. Validates the module with the just-built Nginx binary and live endpoint
|
||||||
|
checks for `/monitor`, `/monitor/api`, `/monitor/health`,
|
||||||
|
`/monitor/metrics`, and `/monitor/live`.
|
||||||
|
6. Publishes a tarball plus sidecar compatibility metadata.
|
||||||
|
|
||||||
|
These artifacts preserve upstream version coverage for users who run Nginx
|
||||||
|
built from nginx.org source. They are not the recommended artifact for Ubuntu or
|
||||||
|
Debian packaged Nginx.
|
||||||
|
|
||||||
|
## Release Assets
|
||||||
|
|
||||||
|
Recommended production asset:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
latest v1.2.3 + patch = v1.2.4
|
libnginx-mod-http-monitoring_<module-version>-1+<distro>.nginx<source-version>_<deb-arch>.deb
|
||||||
latest v1.2.3 + minor = v1.3.0
|
|
||||||
latest v1.2.3 + major = v2.0.0
|
|
||||||
no existing tag + patch = v0.0.1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Built Nginx Versions
|
Manual fallback asset:
|
||||||
|
|
||||||
By default, the workflow builds packages for:
|
|
||||||
|
|
||||||
- Nginx 1.24.0
|
|
||||||
- Nginx 1.26.3
|
|
||||||
- Nginx 1.28.0
|
|
||||||
|
|
||||||
Each release asset is named like:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ngx_http_monitoring_module-1.0.0-v1.0.0-nginx-1.28.0-linux-x86_64.tar.gz
|
ngx_http_monitoring_module-<tag>-<distro>-nginx-<source-version>-<deb-arch>.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
Each package contains:
|
Upstream nginx.org source asset:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ngx_http_monitoring_module-<tag>-nginxorg-<nginx-version>-<target-arch>.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
Metadata assets:
|
||||||
|
|
||||||
|
- `*.compatibility.json`: compatibility metadata for one artifact target
|
||||||
|
- `COMPATIBILITY-MATRIX.json`: combined release compatibility matrix
|
||||||
|
- `SHA256SUMS.txt`: checksums for release assets
|
||||||
|
|
||||||
|
Each tarball contains:
|
||||||
|
|
||||||
- `modules/ngx_http_monitoring_module.so`
|
- `modules/ngx_http_monitoring_module.so`
|
||||||
- `INSTALL.md`
|
- `INSTALL.md`
|
||||||
|
- `COMPATIBILITY.json`
|
||||||
- `METADATA.txt`
|
- `METADATA.txt`
|
||||||
|
- `NGINX-V.txt`
|
||||||
- `SHA256SUMS`
|
- `SHA256SUMS`
|
||||||
- example config
|
- example config and docs
|
||||||
- API/config/performance docs
|
|
||||||
|
## ABI Guard
|
||||||
|
|
||||||
|
The `.deb` package depends on the distro `nginx-abi-*` virtual package when the
|
||||||
|
distribution exposes it and always pins the exact Nginx binary package version
|
||||||
|
used for the build. If a later Nginx package update changes either the ABI or
|
||||||
|
the package revision, apt cannot silently keep the old module installed against
|
||||||
|
the new Nginx binary.
|
||||||
|
|
||||||
|
This is stricter than ABI-only packaging and can require rebuilding the module
|
||||||
|
for routine distro Nginx updates. That is intentional: it favors explicit
|
||||||
|
release artifacts over a module that loads and then fails at runtime.
|
||||||
|
|
||||||
|
## Tarball Warning
|
||||||
|
|
||||||
|
Tarballs are useful for inspection, non-dpkg deployments, upstream nginx.org
|
||||||
|
source builds, and emergency manual installs. They do not give apt a dependency
|
||||||
|
edge, so they cannot protect a host from an ABI-changing Nginx update.
|
||||||
|
|
||||||
|
Use a distro tarball only when all fields in `COMPATIBILITY.json` match the
|
||||||
|
target host:
|
||||||
|
|
||||||
|
- OS ID and version
|
||||||
|
- CPU architecture
|
||||||
|
- Nginx binary package and version
|
||||||
|
- Nginx source package and version
|
||||||
|
- Nginx ABI, when present
|
||||||
|
|
||||||
|
Use an nginx.org tarball only when your target Nginx binary was built from the
|
||||||
|
same upstream Nginx source version and materially compatible configure options.
|
||||||
|
For Ubuntu/Debian packaged Nginx, use the `.deb` artifacts instead.
|
||||||
|
|
||||||
## Docker Image
|
## Docker Image
|
||||||
|
|
||||||
When `push_image` is true, the workflow also builds and pushes:
|
When `push_image` is true, the workflow also builds a self-contained demo image:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ghcr.io/<owner>/<repo>:<tag>
|
ghcr.io/<owner>/<repo>:<tag>
|
||||||
ghcr.io/<owner>/<repo>:latest
|
ghcr.io/<owner>/<repo>:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## Compatibility Warning
|
The image is not the recommended artifact for installing the module into
|
||||||
|
distribution-packaged Nginx. It is a runnable demo image with Nginx and the
|
||||||
Nginx dynamic modules are ABI-sensitive. The release packages are built with `--with-compat` on Ubuntu 22.04 against the Nginx version in the filename. Use the matching Nginx version and a compatible Linux/glibc runtime. Rebuild locally when using a vendor Nginx package with materially different module ABI or hardening options.
|
module built together.
|
||||||
|
|||||||
595
scripts/build-distro-release.sh
Normal file
595
scripts/build-distro-release.sh
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
MODULE_NAME="${MODULE_NAME:-ngx_http_monitoring_module}"
|
||||||
|
DEB_PACKAGE_NAME="${DEB_PACKAGE_NAME:-libnginx-mod-http-monitoring}"
|
||||||
|
NGINX_INSTALL_PACKAGE="${NGINX_INSTALL_PACKAGE:-nginx}"
|
||||||
|
NEXT_VERSION="${NEXT_VERSION:-0.0.0}"
|
||||||
|
NEXT_TAG="${NEXT_TAG:-v${NEXT_VERSION}}"
|
||||||
|
LATEST_TAG="${LATEST_TAG:-none}"
|
||||||
|
TARGET_ARCH="${TARGET_ARCH:-$(uname -m)}"
|
||||||
|
DOCKER_PLATFORM="${DOCKER_PLATFORM:-unknown}"
|
||||||
|
DISTRO_IMAGE="${DISTRO_IMAGE:-unknown}"
|
||||||
|
DIST_DIR="${DIST_DIR:-/work/dist}"
|
||||||
|
WORK_ROOT="${WORK_ROOT:-/work/.release-work}"
|
||||||
|
VALIDATION_PORT="${VALIDATION_PORT:-18080}"
|
||||||
|
GITHUB_REF="${GITHUB_REF:-unknown}"
|
||||||
|
GITHUB_SHA="${GITHUB_SHA:-unknown}"
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
on_error() {
|
||||||
|
local status="$?"
|
||||||
|
local line="${BASH_LINENO[0]:-unknown}"
|
||||||
|
|
||||||
|
echo "::error::distro release build failed at line ${line} with exit code ${status}" >&2
|
||||||
|
|
||||||
|
if [[ -n "${WORK_ROOT:-}" && -d "$WORK_ROOT" ]]; then
|
||||||
|
find "$WORK_ROOT" -path '*/objs/autoconf.err' -type f -print -exec sed -n '1,160p' {} \; >&2 || true
|
||||||
|
find "$WORK_ROOT" -path '*/objs/*.log' -type f -print -exec sed -n '1,160p' {} \; >&2 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit "$status"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap on_error ERR
|
||||||
|
|
||||||
|
slugify() {
|
||||||
|
tr '[:upper:]' '[:lower:]' \
|
||||||
|
| sed -E 's/[^a-z0-9.+~]+/./g; s/^[.]+//; s/[.]+$//'
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "error: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
apt_install() {
|
||||||
|
apt-get install -y --no-install-recommends "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
find_nginx_abi() {
|
||||||
|
local pkg
|
||||||
|
local abi
|
||||||
|
|
||||||
|
for pkg in "$@"; do
|
||||||
|
abi="$(
|
||||||
|
apt-cache show "$pkg" 2>/dev/null \
|
||||||
|
| awk -F': ' '
|
||||||
|
/^Provides:/ {
|
||||||
|
n = split($2, provides, ",")
|
||||||
|
for (i = 1; i <= n; i++) {
|
||||||
|
gsub(/^[ \t]+|[ \t]+$/, "", provides[i])
|
||||||
|
if (provides[i] ~ /^nginx-abi-/) {
|
||||||
|
print provides[i]
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' \
|
||||||
|
| head -n 1 \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -n "$abi" ]]; then
|
||||||
|
printf '%s\n' "$abi"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_module() {
|
||||||
|
local module_path="$1"
|
||||||
|
local runtime_root="$2"
|
||||||
|
local config_path="$runtime_root/nginx.conf"
|
||||||
|
local base_url="http://127.0.0.1:${VALIDATION_PORT}"
|
||||||
|
local status
|
||||||
|
local dashboard
|
||||||
|
local sse_output
|
||||||
|
|
||||||
|
rm -rf "$runtime_root"
|
||||||
|
mkdir -p \
|
||||||
|
"$runtime_root/logs" \
|
||||||
|
"$runtime_root/temp/client_body" \
|
||||||
|
"$runtime_root/temp/proxy" \
|
||||||
|
"$runtime_root/temp/fastcgi" \
|
||||||
|
"$runtime_root/temp/uwsgi" \
|
||||||
|
"$runtime_root/temp/scgi"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf 'load_module %s;\n\n' "$module_path"
|
||||||
|
printf 'worker_processes 1;\n'
|
||||||
|
printf 'error_log %s/logs/error.log info;\n' "$runtime_root"
|
||||||
|
printf 'pid %s/logs/nginx.pid;\n\n' "$runtime_root"
|
||||||
|
printf 'events {\n'
|
||||||
|
printf ' worker_connections 1024;\n'
|
||||||
|
printf '}\n\n'
|
||||||
|
printf 'http {\n'
|
||||||
|
printf ' access_log off;\n'
|
||||||
|
printf ' default_type application/octet-stream;\n'
|
||||||
|
printf ' client_body_temp_path %s/temp/client_body;\n' "$runtime_root"
|
||||||
|
printf ' proxy_temp_path %s/temp/proxy;\n' "$runtime_root"
|
||||||
|
printf ' fastcgi_temp_path %s/temp/fastcgi;\n' "$runtime_root"
|
||||||
|
printf ' uwsgi_temp_path %s/temp/uwsgi;\n' "$runtime_root"
|
||||||
|
printf ' scgi_temp_path %s/temp/scgi;\n\n' "$runtime_root"
|
||||||
|
printf ' monitor_refresh_interval 1s;\n'
|
||||||
|
printf ' monitor_history 1m;\n'
|
||||||
|
printf ' monitor_resolution 1s;\n'
|
||||||
|
printf ' monitor_shm_size 8m;\n'
|
||||||
|
printf ' monitor_collect_system on;\n'
|
||||||
|
printf ' monitor_collect_nginx on;\n'
|
||||||
|
printf ' monitor_collect_network on;\n'
|
||||||
|
printf ' monitor_access_log on;\n'
|
||||||
|
printf ' monitor_max_top_urls 100;\n\n'
|
||||||
|
printf ' server {\n'
|
||||||
|
printf ' listen 127.0.0.1:%s;\n' "$VALIDATION_PORT"
|
||||||
|
printf ' server_name localhost;\n\n'
|
||||||
|
printf ' location /monitor {\n'
|
||||||
|
printf ' monitor on;\n'
|
||||||
|
printf ' monitor_dashboard on;\n'
|
||||||
|
printf ' monitor_api on;\n'
|
||||||
|
printf ' monitor_sse on;\n'
|
||||||
|
printf ' monitor_allow all;\n'
|
||||||
|
printf ' monitor_basic_auth off;\n'
|
||||||
|
printf ' monitor_cors off;\n'
|
||||||
|
printf ' monitor_rate_limit 120;\n'
|
||||||
|
printf ' }\n\n'
|
||||||
|
printf ' location / {\n'
|
||||||
|
printf ' return 200 "ok\\n";\n'
|
||||||
|
printf ' }\n'
|
||||||
|
printf ' }\n'
|
||||||
|
printf '}\n'
|
||||||
|
} > "$config_path"
|
||||||
|
|
||||||
|
nginx -t -p "$runtime_root" -c "$config_path"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
nginx -p "$runtime_root" -c "$config_path" -s quit >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup RETURN
|
||||||
|
|
||||||
|
nginx -p "$runtime_root" -c "$config_path"
|
||||||
|
|
||||||
|
for attempt in $(seq 1 50); do
|
||||||
|
if curl -fsS "$base_url/" >/dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$attempt" -eq 50 ]]; then
|
||||||
|
echo "Nginx did not become ready" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
|
||||||
|
curl -fsS "$base_url/" | grep -q '^ok'
|
||||||
|
|
||||||
|
status="$(curl -fsS -o "$runtime_root/dashboard.html" -w '%{http_code}' "$base_url/monitor")"
|
||||||
|
[[ "$status" == "200" ]] || fail "/monitor returned HTTP $status"
|
||||||
|
dashboard="$(cat "$runtime_root/dashboard.html")"
|
||||||
|
printf '%s' "$dashboard" | grep -q '<title>Nginx Monitor</title>'
|
||||||
|
printf '%s' "$dashboard" | grep -q '/monitor/api'
|
||||||
|
printf '%s' "$dashboard" | grep -q '/monitor/live'
|
||||||
|
|
||||||
|
curl -fsS "$base_url/monitor/api" \
|
||||||
|
| jq -e '.version == 1 and has("system") and has("nginx") and has("requests") and has("history")' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/api/system" \
|
||||||
|
| jq -e '.version == 1 and .scope == "system" and has("system")' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/api/nginx" \
|
||||||
|
| jq -e '.version == 1 and .scope == "nginx" and has("nginx")' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/health" \
|
||||||
|
| jq -e '.version == 1 and .scope == "health" and .status == "ok"' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/metrics" \
|
||||||
|
| grep -q '^nginx_monitor_requests_total '
|
||||||
|
|
||||||
|
sse_output="$(timeout 5s curl -fsS -N "$base_url/monitor/live" || true)"
|
||||||
|
printf '%s' "$sse_output" | grep -q '^event: metrics'
|
||||||
|
printf '%s' "$sse_output" | grep -q '^data: {'
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
trap - RETURN
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "$DIST_DIR" "$WORK_ROOT"
|
||||||
|
|
||||||
|
printf '#!/bin/sh\nexit 101\n' > /usr/sbin/policy-rc.d
|
||||||
|
chmod +x /usr/sbin/policy-rc.d
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt_install ca-certificates curl gnupg
|
||||||
|
bash /work/scripts/enable-apt-source-repos.sh
|
||||||
|
apt-get update
|
||||||
|
apt_install \
|
||||||
|
bash \
|
||||||
|
binutils \
|
||||||
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
dpkg-dev \
|
||||||
|
file \
|
||||||
|
findutils \
|
||||||
|
jq \
|
||||||
|
libpcre2-dev \
|
||||||
|
libssl-dev \
|
||||||
|
python3 \
|
||||||
|
xz-utils \
|
||||||
|
zlib1g-dev \
|
||||||
|
"$NGINX_INSTALL_PACKAGE"
|
||||||
|
|
||||||
|
. /etc/os-release
|
||||||
|
distro_id="${ID:-unknown}"
|
||||||
|
distro_version="${VERSION_ID:-unknown}"
|
||||||
|
distro_codename="${VERSION_CODENAME:-${UBUNTU_CODENAME:-unknown}}"
|
||||||
|
distro_slug="$(printf '%s-%s-%s' "$distro_id" "$distro_version" "$distro_codename" | slugify)"
|
||||||
|
|
||||||
|
deb_arch="$(dpkg --print-architecture)"
|
||||||
|
nginx_bin="$(command -v nginx)"
|
||||||
|
nginx_binary_package="$(dpkg-query -S "$nginx_bin" | head -n 1 | cut -d: -f1)"
|
||||||
|
nginx_binary_version="$(dpkg-query -W -f='${Version}' "$nginx_binary_package")"
|
||||||
|
source_package="$(dpkg-query -W -f='${source:Package}' "$nginx_binary_package" 2>/dev/null || true)"
|
||||||
|
source_version="$(dpkg-query -W -f='${source:Version}' "$nginx_binary_package" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ -z "$source_package" || "$source_package" == *'{'* ]]; then
|
||||||
|
source_package="$nginx_binary_package"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$source_version" || "$source_version" == *'{'* ]]; then
|
||||||
|
source_version="$nginx_binary_version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
nginx_abi="$(find_nginx_abi "$nginx_binary_package" "$NGINX_INSTALL_PACKAGE" nginx-core nginx-full nginx-light nginx-extras nginx)"
|
||||||
|
|
||||||
|
echo "Distro: ${distro_id} ${distro_version} (${distro_codename}), arch ${deb_arch}"
|
||||||
|
echo "Nginx binary package: ${nginx_binary_package} ${nginx_binary_version}"
|
||||||
|
echo "Nginx source package: ${source_package} ${source_version}"
|
||||||
|
echo "Nginx ABI package: ${nginx_abi:-none found}"
|
||||||
|
|
||||||
|
src_root="$WORK_ROOT/${distro_slug}/${deb_arch}/source"
|
||||||
|
build_root="$WORK_ROOT/${distro_slug}/${deb_arch}/build"
|
||||||
|
rm -rf "$src_root" "$build_root"
|
||||||
|
mkdir -p "$src_root" "$build_root"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$src_root"
|
||||||
|
apt-get source "${source_package}=${source_version}" \
|
||||||
|
|| apt-get source "$source_package"
|
||||||
|
)
|
||||||
|
|
||||||
|
dsc_path="$(find "$src_root" -maxdepth 1 -name '*.dsc' | head -n 1)"
|
||||||
|
[[ -n "$dsc_path" ]] || fail "apt-get source did not download a .dsc file"
|
||||||
|
|
||||||
|
dsc_version="$(awk '/^Version:/ { print $2; exit }' "$dsc_path")"
|
||||||
|
if [[ "$dsc_version" != "$source_version" ]]; then
|
||||||
|
fail "downloaded source version ${dsc_version}, expected ${source_version}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
nginx_src="$(find "$src_root" -mindepth 1 -maxdepth 1 -type d | head -n 1)"
|
||||||
|
[[ -n "$nginx_src" ]] || fail "apt-get source did not unpack an nginx source directory"
|
||||||
|
|
||||||
|
apt-get build-dep -y --no-install-recommends "${source_package}=${source_version}" \
|
||||||
|
|| apt-get build-dep -y --no-install-recommends "$source_package"
|
||||||
|
|
||||||
|
nginx_build_info="$build_root/nginx-build-info.json"
|
||||||
|
modules_path="$(python3 /work/scripts/nginx-build-info.py --modules-path)"
|
||||||
|
|
||||||
|
case "$modules_path" in
|
||||||
|
/*) ;;
|
||||||
|
*) modules_path="/${modules_path}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
source_version_slug="$(printf '%s' "$source_version" | slugify)"
|
||||||
|
deb_revision_slug="$(printf '%s%s.nginx%s' "$distro_id" "$distro_version" "$source_version" | slugify)"
|
||||||
|
deb_version="${NEXT_VERSION}-1+${deb_revision_slug}"
|
||||||
|
build_metadata="$(printf '%s.%s.%s' "$distro_slug" "$deb_arch" "$source_version_slug" | slugify)"
|
||||||
|
module_cflags="-DNGX_HTTP_MONITORING_MODULE_VERSION=\\\"${NEXT_VERSION}\\\" -DNGX_HTTP_MONITORING_BUILD_METADATA=\\\"${build_metadata}\\\""
|
||||||
|
python3 /work/scripts/nginx-build-info.py \
|
||||||
|
--nginx-src "$nginx_src" \
|
||||||
|
--extra-cc-opt "$module_cflags" \
|
||||||
|
--json > "$nginx_build_info"
|
||||||
|
configure_line="$(python3 /work/scripts/nginx-build-info.py \
|
||||||
|
--nginx-src "$nginx_src" \
|
||||||
|
--extra-cc-opt "$module_cflags" \
|
||||||
|
--configure-line)"
|
||||||
|
|
||||||
|
cd "$nginx_src"
|
||||||
|
eval "./configure ${configure_line} --add-dynamic-module=/work"
|
||||||
|
|
||||||
|
build_jobs="$(nproc)"
|
||||||
|
if [[ "$TARGET_ARCH" != "linux-x86_64" && "$build_jobs" -gt 2 ]]; then
|
||||||
|
build_jobs=2
|
||||||
|
fi
|
||||||
|
|
||||||
|
make -j"$build_jobs" modules
|
||||||
|
|
||||||
|
built_module="$nginx_src/objs/${MODULE_NAME}.so"
|
||||||
|
[[ -f "$built_module" ]] || fail "module was not built at ${built_module}"
|
||||||
|
|
||||||
|
artifact_base="${MODULE_NAME}-${NEXT_TAG}-${distro_slug}-nginx-${source_version_slug}-${deb_arch}"
|
||||||
|
deb_file="${DEB_PACKAGE_NAME}_${deb_version}_${deb_arch}.deb"
|
||||||
|
tarball_file="${artifact_base}.tar.gz"
|
||||||
|
package_root="$build_root/package-root"
|
||||||
|
doc_dir="$package_root/usr/share/doc/$DEB_PACKAGE_NAME"
|
||||||
|
module_install_dir="$package_root/${modules_path#/}"
|
||||||
|
modules_available_dir="$package_root/usr/share/nginx/modules-available"
|
||||||
|
metadata_dir="$doc_dir/metadata"
|
||||||
|
|
||||||
|
rm -rf "$package_root"
|
||||||
|
mkdir -p "$module_install_dir" "$modules_available_dir" "$metadata_dir" "$package_root/DEBIAN"
|
||||||
|
|
||||||
|
cp "$built_module" "$module_install_dir/${MODULE_NAME}.so"
|
||||||
|
strip --strip-unneeded "$module_install_dir/${MODULE_NAME}.so" || true
|
||||||
|
|
||||||
|
cat > "$modules_available_dir/mod-http-monitoring.conf" <<EOF
|
||||||
|
load_module ${modules_path}/${MODULE_NAME}.so;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cp /work/README.md "$doc_dir/README.md"
|
||||||
|
cp /work/LICENSE "$doc_dir/copyright"
|
||||||
|
cp /work/docs/API.md "$doc_dir/API.md"
|
||||||
|
cp /work/docs/CONFIGURATION.md "$doc_dir/CONFIGURATION.md"
|
||||||
|
cp /work/docs/PERFORMANCE.md "$doc_dir/PERFORMANCE.md"
|
||||||
|
cp /work/examples/monitoring-site.conf "$doc_dir/monitoring-site.conf.example"
|
||||||
|
|
||||||
|
cat > "$doc_dir/changelog.Debian" <<EOF
|
||||||
|
$DEB_PACKAGE_NAME (${deb_version}) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Build ${NEXT_TAG} against ${distro_id} ${distro_version} nginx source ${source_version}.
|
||||||
|
|
||||||
|
-- GitHub Actions <actions@github.com> $(date -Ru)
|
||||||
|
EOF
|
||||||
|
gzip -9n "$doc_dir/changelog.Debian"
|
||||||
|
|
||||||
|
module_sha256="$(sha256sum "$module_install_dir/${MODULE_NAME}.so" | awk '{ print $1 }')"
|
||||||
|
dsc_sha256="$(sha256sum "$dsc_path" | awk '{ print $1 }')"
|
||||||
|
nginx_v_text="$metadata_dir/nginx-V.txt"
|
||||||
|
nginx -V > "$nginx_v_text" 2>&1
|
||||||
|
|
||||||
|
if [[ -n "$nginx_abi" ]]; then
|
||||||
|
deb_depends_json="$(
|
||||||
|
python3 -c 'import json, sys; print(json.dumps(sys.argv[1:]))' \
|
||||||
|
"$nginx_abi" \
|
||||||
|
"$nginx_binary_package (= $nginx_binary_version)" \
|
||||||
|
"libc6"
|
||||||
|
)"
|
||||||
|
else
|
||||||
|
deb_depends_json="$(
|
||||||
|
python3 -c 'import json, sys; print(json.dumps(sys.argv[1:]))' \
|
||||||
|
"$nginx_binary_package (= $nginx_binary_version)" \
|
||||||
|
"libc6"
|
||||||
|
)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
compatibility_json="$metadata_dir/compatibility.json"
|
||||||
|
COMPATIBILITY_JSON="$compatibility_json" \
|
||||||
|
NGINX_BUILD_INFO="$nginx_build_info" \
|
||||||
|
SCHEMA=1 \
|
||||||
|
MODULE_NAME="$MODULE_NAME" \
|
||||||
|
DEB_PACKAGE_NAME="$DEB_PACKAGE_NAME" \
|
||||||
|
DEB_VERSION="$deb_version" \
|
||||||
|
NEXT_TAG="$NEXT_TAG" \
|
||||||
|
NEXT_VERSION="$NEXT_VERSION" \
|
||||||
|
LATEST_TAG="$LATEST_TAG" \
|
||||||
|
GITHUB_REF="$GITHUB_REF" \
|
||||||
|
GITHUB_SHA="$GITHUB_SHA" \
|
||||||
|
DISTRO_IMAGE="$DISTRO_IMAGE" \
|
||||||
|
DOCKER_PLATFORM="$DOCKER_PLATFORM" \
|
||||||
|
TARGET_ARCH="$TARGET_ARCH" \
|
||||||
|
OS_ID="$distro_id" \
|
||||||
|
OS_VERSION="$distro_version" \
|
||||||
|
OS_CODENAME="$distro_codename" \
|
||||||
|
DEB_ARCH="$deb_arch" \
|
||||||
|
NGINX_BINARY_PACKAGE="$nginx_binary_package" \
|
||||||
|
NGINX_BINARY_VERSION="$nginx_binary_version" \
|
||||||
|
NGINX_SOURCE_PACKAGE="$source_package" \
|
||||||
|
NGINX_SOURCE_VERSION="$source_version" \
|
||||||
|
NGINX_ABI="$nginx_abi" \
|
||||||
|
MODULES_PATH="$modules_path" \
|
||||||
|
DSC_PATH="$(basename "$dsc_path")" \
|
||||||
|
DSC_SHA256="$dsc_sha256" \
|
||||||
|
MODULE_SHA256="$module_sha256" \
|
||||||
|
BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
|
ARTIFACT_BASE="$artifact_base" \
|
||||||
|
DEB_FILE="$deb_file" \
|
||||||
|
TARBALL_FILE="$tarball_file" \
|
||||||
|
DEB_DEPENDS_JSON="$deb_depends_json" \
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
with open(os.environ["NGINX_BUILD_INFO"], "r", encoding="utf-8") as f:
|
||||||
|
nginx_build = json.load(f)
|
||||||
|
|
||||||
|
artifact_base = os.environ["ARTIFACT_BASE"]
|
||||||
|
data = {
|
||||||
|
"schema": int(os.environ["SCHEMA"]),
|
||||||
|
"module": os.environ["MODULE_NAME"],
|
||||||
|
"release": {
|
||||||
|
"tag": os.environ["NEXT_TAG"],
|
||||||
|
"version": os.environ["NEXT_VERSION"],
|
||||||
|
"previous_tag": os.environ["LATEST_TAG"],
|
||||||
|
"git_ref": os.environ["GITHUB_REF"],
|
||||||
|
"git_sha": os.environ["GITHUB_SHA"],
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"os_id": os.environ["OS_ID"],
|
||||||
|
"os_version": os.environ["OS_VERSION"],
|
||||||
|
"os_codename": os.environ["OS_CODENAME"],
|
||||||
|
"architecture": os.environ["DEB_ARCH"],
|
||||||
|
"requested_target": os.environ["TARGET_ARCH"],
|
||||||
|
"docker_image": os.environ["DISTRO_IMAGE"],
|
||||||
|
"docker_platform": os.environ["DOCKER_PLATFORM"],
|
||||||
|
},
|
||||||
|
"nginx": {
|
||||||
|
"binary_package": os.environ["NGINX_BINARY_PACKAGE"],
|
||||||
|
"binary_version": os.environ["NGINX_BINARY_VERSION"],
|
||||||
|
"source_package": os.environ["NGINX_SOURCE_PACKAGE"],
|
||||||
|
"source_version": os.environ["NGINX_SOURCE_VERSION"],
|
||||||
|
"abi": os.environ["NGINX_ABI"],
|
||||||
|
"modules_path": os.environ["MODULES_PATH"],
|
||||||
|
"build": nginx_build,
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"method": "distro-source-package",
|
||||||
|
"dsc_file": os.environ["DSC_PATH"],
|
||||||
|
"dsc_sha256": os.environ["DSC_SHA256"],
|
||||||
|
"module_sha256": os.environ["MODULE_SHA256"],
|
||||||
|
"built_at": os.environ["BUILT_AT"],
|
||||||
|
},
|
||||||
|
"artifacts": {
|
||||||
|
"base_name": artifact_base,
|
||||||
|
"deb_package": os.environ["DEB_PACKAGE_NAME"],
|
||||||
|
"deb_version": os.environ["DEB_VERSION"],
|
||||||
|
"deb_file": os.environ["DEB_FILE"],
|
||||||
|
"tarball_file": os.environ["TARBALL_FILE"],
|
||||||
|
"compatibility_file": artifact_base + ".compatibility.json",
|
||||||
|
},
|
||||||
|
"packaging": {
|
||||||
|
"deb_depends": json.loads(os.environ["DEB_DEPENDS_JSON"]),
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"packaged_nginx_config_test": False,
|
||||||
|
"packaged_nginx_endpoint_smoke": False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(os.environ["COMPATIBILITY_JSON"], "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2, sort_keys=True)
|
||||||
|
f.write("\n")
|
||||||
|
PY
|
||||||
|
|
||||||
|
cat > "$doc_dir/INSTALL.md" <<EOF
|
||||||
|
# ${MODULE_NAME} for ${distro_id} ${distro_version} ${deb_arch}
|
||||||
|
|
||||||
|
This package was built from the ${source_package} source package version
|
||||||
|
${source_version} and validated against the packaged ${nginx_binary_package}
|
||||||
|
binary version ${nginx_binary_version}.
|
||||||
|
|
||||||
|
Recommended install:
|
||||||
|
|
||||||
|
\`\`\`sh
|
||||||
|
sudo apt install ./$(printf '%s_%s_%s.deb' "$DEB_PACKAGE_NAME" "$deb_version" "$deb_arch")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
The package installs the module at:
|
||||||
|
|
||||||
|
\`\`\`text
|
||||||
|
${modules_path}/${MODULE_NAME}.so
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Load it near the top of nginx.conf:
|
||||||
|
|
||||||
|
\`\`\`nginx
|
||||||
|
load_module ${modules_path}/${MODULE_NAME}.so;
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Compatibility guard:
|
||||||
|
|
||||||
|
\`\`\`text
|
||||||
|
$(if [[ -n "$nginx_abi" ]]; then printf 'Depends: %s\nDepends: %s (= %s)\n' "$nginx_abi" "$nginx_binary_package" "$nginx_binary_version"; else printf 'Depends: %s (= %s)\n' "$nginx_binary_package" "$nginx_binary_version"; fi)
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
If a distro nginx update changes this ABI, apt will refuse to keep this module
|
||||||
|
installed with the new nginx package. Rebuild and publish a new package for the
|
||||||
|
new distro nginx package instead of reusing this binary.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
depends=""
|
||||||
|
if [[ -n "$nginx_abi" ]]; then
|
||||||
|
depends="$nginx_abi, $nginx_binary_package (= $nginx_binary_version), libc6"
|
||||||
|
else
|
||||||
|
depends="$nginx_binary_package (= $nginx_binary_version), libc6"
|
||||||
|
fi
|
||||||
|
|
||||||
|
installed_size="$(du -ks "$package_root" | awk '{ print $1 }')"
|
||||||
|
cat > "$package_root/DEBIAN/control" <<EOF
|
||||||
|
Package: $DEB_PACKAGE_NAME
|
||||||
|
Version: $deb_version
|
||||||
|
Section: httpd
|
||||||
|
Priority: optional
|
||||||
|
Architecture: $deb_arch
|
||||||
|
Maintainer: ngx_http_monitoring_module maintainers <noreply@github.com>
|
||||||
|
Depends: $depends
|
||||||
|
Installed-Size: $installed_size
|
||||||
|
Description: Nginx HTTP monitoring dynamic module
|
||||||
|
Distro-built dynamic module for live Nginx monitoring, JSON APIs,
|
||||||
|
Server-Sent Events, Prometheus metrics, and an embedded dashboard.
|
||||||
|
This package is built against the target distribution nginx source package
|
||||||
|
and depends on the matching nginx module ABI.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
deb_path="$DIST_DIR/$deb_file"
|
||||||
|
dpkg-deb --build --root-owner-group "$package_root" "$deb_path"
|
||||||
|
|
||||||
|
apt-get install -y --no-install-recommends "$deb_path"
|
||||||
|
validate_module "${modules_path}/${MODULE_NAME}.so" "$build_root/runtime"
|
||||||
|
|
||||||
|
COMPATIBILITY_JSON="$compatibility_json" \
|
||||||
|
DEB_FILE="$deb_file" \
|
||||||
|
VALIDATION_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
path = os.environ["COMPATIBILITY_JSON"]
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
data["artifacts"]["deb_file"] = os.environ["DEB_FILE"]
|
||||||
|
data["validation"]["packaged_nginx_config_test"] = True
|
||||||
|
data["validation"]["packaged_nginx_endpoint_smoke"] = True
|
||||||
|
data["validation"]["validated_at"] = os.environ["VALIDATION_TIME"]
|
||||||
|
|
||||||
|
tmp = path + ".tmp"
|
||||||
|
with open(tmp, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2, sort_keys=True)
|
||||||
|
f.write("\n")
|
||||||
|
os.replace(tmp, path)
|
||||||
|
PY
|
||||||
|
dpkg-deb --build --root-owner-group "$package_root" "$deb_path"
|
||||||
|
|
||||||
|
tar_root="$build_root/tar"
|
||||||
|
tar_dir="$tar_root/$artifact_base"
|
||||||
|
rm -rf "$tar_root"
|
||||||
|
mkdir -p "$tar_dir/modules" "$tar_dir/docs" "$tar_dir/examples"
|
||||||
|
cp "${modules_path}/${MODULE_NAME}.so" "$tar_dir/modules/${MODULE_NAME}.so"
|
||||||
|
cp "$compatibility_json" "$tar_dir/COMPATIBILITY.json"
|
||||||
|
cp "$doc_dir/INSTALL.md" "$tar_dir/INSTALL.md"
|
||||||
|
cp "$doc_dir/README.md" "$tar_dir/README.md"
|
||||||
|
cp "$doc_dir/API.md" "$tar_dir/docs/API.md"
|
||||||
|
cp "$doc_dir/CONFIGURATION.md" "$tar_dir/docs/CONFIGURATION.md"
|
||||||
|
cp "$doc_dir/PERFORMANCE.md" "$tar_dir/docs/PERFORMANCE.md"
|
||||||
|
cp "$doc_dir/monitoring-site.conf.example" "$tar_dir/examples/monitoring-site.conf"
|
||||||
|
cp "$nginx_v_text" "$tar_dir/NGINX-V.txt"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf 'module=%s\n' "$MODULE_NAME"
|
||||||
|
printf 'module_release=%s\n' "$NEXT_TAG"
|
||||||
|
printf 'module_version=%s\n' "$NEXT_VERSION"
|
||||||
|
printf 'module_build_metadata=%s\n' "$build_metadata"
|
||||||
|
printf 'distro=%s\n' "$distro_slug"
|
||||||
|
printf 'architecture=%s\n' "$deb_arch"
|
||||||
|
printf 'nginx_binary_package=%s\n' "$nginx_binary_package"
|
||||||
|
printf 'nginx_binary_version=%s\n' "$nginx_binary_version"
|
||||||
|
printf 'nginx_source_package=%s\n' "$source_package"
|
||||||
|
printf 'nginx_source_version=%s\n' "$source_version"
|
||||||
|
printf 'nginx_abi=%s\n' "$nginx_abi"
|
||||||
|
printf 'modules_path=%s\n' "$modules_path"
|
||||||
|
printf 'git_ref=%s\n' "$GITHUB_REF"
|
||||||
|
printf 'git_sha=%s\n' "$GITHUB_SHA"
|
||||||
|
printf 'built_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
} > "$tar_dir/METADATA.txt"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$tar_dir"
|
||||||
|
sha256sum "modules/${MODULE_NAME}.so" COMPATIBILITY.json > SHA256SUMS
|
||||||
|
file "modules/${MODULE_NAME}.so" > FILE.txt
|
||||||
|
)
|
||||||
|
|
||||||
|
tar -czf "$DIST_DIR/$tarball_file" -C "$tar_root" "$artifact_base"
|
||||||
|
|
||||||
|
cp "$compatibility_json" "$DIST_DIR/${artifact_base}.compatibility.json"
|
||||||
|
|
||||||
|
echo "Built $deb_path"
|
||||||
|
echo "Built $DIST_DIR/$tarball_file"
|
||||||
|
echo "Built $DIST_DIR/${artifact_base}.compatibility.json"
|
||||||
359
scripts/build-upstream-release.sh
Normal file
359
scripts/build-upstream-release.sh
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MODULE_NAME="${MODULE_NAME:-ngx_http_monitoring_module}"
|
||||||
|
BUILD_NGINX_VERSIONS="${BUILD_NGINX_VERSIONS:-}"
|
||||||
|
NEXT_VERSION="${NEXT_VERSION:-0.0.0}"
|
||||||
|
NEXT_TAG="${NEXT_TAG:-v${NEXT_VERSION}}"
|
||||||
|
LATEST_TAG="${LATEST_TAG:-none}"
|
||||||
|
TARGET_ARCH="${TARGET_ARCH:-$(uname -m)}"
|
||||||
|
DOCKER_PLATFORM="${DOCKER_PLATFORM:-unknown}"
|
||||||
|
RELEASE_BUILD_IMAGE="${RELEASE_BUILD_IMAGE:-unknown}"
|
||||||
|
RELEASE_LIBC_BASELINE="${RELEASE_LIBC_BASELINE:-unknown}"
|
||||||
|
NGINX_CONFIGURE_ARGS="${NGINX_CONFIGURE_ARGS:---with-compat --with-http_ssl_module --with-http_stub_status_module}"
|
||||||
|
DIST_DIR="${DIST_DIR:-/work/dist}"
|
||||||
|
WORK_ROOT="${WORK_ROOT:-/work/.release-work/upstream}"
|
||||||
|
VALIDATION_PORT="${VALIDATION_PORT:-18080}"
|
||||||
|
GITHUB_REF="${GITHUB_REF:-unknown}"
|
||||||
|
GITHUB_SHA="${GITHUB_SHA:-unknown}"
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "error: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
slugify() {
|
||||||
|
tr '[:upper:]' '[:lower:]' \
|
||||||
|
| sed -E 's/[^a-z0-9.+~]+/./g; s/^[.]+//; s/[.]+$//'
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_module() {
|
||||||
|
local nginx_bin="$1"
|
||||||
|
local module_path="$2"
|
||||||
|
local runtime_root="$3"
|
||||||
|
local config_path="$runtime_root/nginx.conf"
|
||||||
|
local base_url="http://127.0.0.1:${VALIDATION_PORT}"
|
||||||
|
local status
|
||||||
|
local dashboard
|
||||||
|
local sse_output
|
||||||
|
|
||||||
|
rm -rf "$runtime_root"
|
||||||
|
mkdir -p \
|
||||||
|
"$runtime_root/logs" \
|
||||||
|
"$runtime_root/temp/client_body" \
|
||||||
|
"$runtime_root/temp/proxy" \
|
||||||
|
"$runtime_root/temp/fastcgi" \
|
||||||
|
"$runtime_root/temp/uwsgi" \
|
||||||
|
"$runtime_root/temp/scgi"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf 'load_module %s;\n\n' "$module_path"
|
||||||
|
printf 'worker_processes 1;\n'
|
||||||
|
printf 'error_log %s/logs/error.log info;\n' "$runtime_root"
|
||||||
|
printf 'pid %s/logs/nginx.pid;\n\n' "$runtime_root"
|
||||||
|
printf 'events {\n'
|
||||||
|
printf ' worker_connections 1024;\n'
|
||||||
|
printf '}\n\n'
|
||||||
|
printf 'http {\n'
|
||||||
|
printf ' access_log off;\n'
|
||||||
|
printf ' default_type application/octet-stream;\n'
|
||||||
|
printf ' client_body_temp_path %s/temp/client_body;\n' "$runtime_root"
|
||||||
|
printf ' proxy_temp_path %s/temp/proxy;\n' "$runtime_root"
|
||||||
|
printf ' fastcgi_temp_path %s/temp/fastcgi;\n' "$runtime_root"
|
||||||
|
printf ' uwsgi_temp_path %s/temp/uwsgi;\n' "$runtime_root"
|
||||||
|
printf ' scgi_temp_path %s/temp/scgi;\n\n' "$runtime_root"
|
||||||
|
printf ' monitor_refresh_interval 1s;\n'
|
||||||
|
printf ' monitor_history 1m;\n'
|
||||||
|
printf ' monitor_resolution 1s;\n'
|
||||||
|
printf ' monitor_shm_size 8m;\n'
|
||||||
|
printf ' monitor_collect_system on;\n'
|
||||||
|
printf ' monitor_collect_nginx on;\n'
|
||||||
|
printf ' monitor_collect_network on;\n'
|
||||||
|
printf ' monitor_access_log on;\n'
|
||||||
|
printf ' monitor_max_top_urls 100;\n\n'
|
||||||
|
printf ' server {\n'
|
||||||
|
printf ' listen 127.0.0.1:%s;\n' "$VALIDATION_PORT"
|
||||||
|
printf ' server_name localhost;\n\n'
|
||||||
|
printf ' location /monitor {\n'
|
||||||
|
printf ' monitor on;\n'
|
||||||
|
printf ' monitor_dashboard on;\n'
|
||||||
|
printf ' monitor_api on;\n'
|
||||||
|
printf ' monitor_sse on;\n'
|
||||||
|
printf ' monitor_allow all;\n'
|
||||||
|
printf ' monitor_basic_auth off;\n'
|
||||||
|
printf ' monitor_cors off;\n'
|
||||||
|
printf ' monitor_rate_limit 120;\n'
|
||||||
|
printf ' }\n\n'
|
||||||
|
printf ' location / {\n'
|
||||||
|
printf ' return 200 "ok\\n";\n'
|
||||||
|
printf ' }\n'
|
||||||
|
printf ' }\n'
|
||||||
|
printf '}\n'
|
||||||
|
} > "$config_path"
|
||||||
|
|
||||||
|
"$nginx_bin" -t -p "$runtime_root" -c "$config_path"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
"$nginx_bin" -p "$runtime_root" -c "$config_path" -s quit >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup RETURN
|
||||||
|
|
||||||
|
"$nginx_bin" -p "$runtime_root" -c "$config_path"
|
||||||
|
|
||||||
|
for attempt in $(seq 1 50); do
|
||||||
|
if curl -fsS "$base_url/" >/dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$attempt" -eq 50 ]]; then
|
||||||
|
echo "Nginx did not become ready" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
|
||||||
|
curl -fsS "$base_url/" | grep -q '^ok'
|
||||||
|
|
||||||
|
status="$(curl -fsS -o "$runtime_root/dashboard.html" -w '%{http_code}' "$base_url/monitor")"
|
||||||
|
[[ "$status" == "200" ]] || fail "/monitor returned HTTP $status"
|
||||||
|
dashboard="$(cat "$runtime_root/dashboard.html")"
|
||||||
|
printf '%s' "$dashboard" | grep -q '<title>Nginx Monitor</title>'
|
||||||
|
printf '%s' "$dashboard" | grep -q '/monitor/api'
|
||||||
|
printf '%s' "$dashboard" | grep -q '/monitor/live'
|
||||||
|
|
||||||
|
curl -fsS "$base_url/monitor/api" \
|
||||||
|
| jq -e '.version == 1 and has("system") and has("nginx") and has("requests") and has("history")' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/api/system" \
|
||||||
|
| jq -e '.version == 1 and .scope == "system" and has("system")' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/api/nginx" \
|
||||||
|
| jq -e '.version == 1 and .scope == "nginx" and has("nginx")' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/health" \
|
||||||
|
| jq -e '.version == 1 and .scope == "health" and .status == "ok"' >/dev/null
|
||||||
|
curl -fsS "$base_url/monitor/metrics" \
|
||||||
|
| grep -q '^nginx_monitor_requests_total '
|
||||||
|
|
||||||
|
sse_output="$(timeout 5s curl -fsS -N "$base_url/monitor/live" || true)"
|
||||||
|
printf '%s' "$sse_output" | grep -q '^event: metrics'
|
||||||
|
printf '%s' "$sse_output" | grep -q '^data: {'
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
trap - RETURN
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -n "$BUILD_NGINX_VERSIONS" ]] || fail "BUILD_NGINX_VERSIONS is required"
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
binutils \
|
||||||
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
file \
|
||||||
|
jq \
|
||||||
|
libpcre2-dev \
|
||||||
|
libssl-dev \
|
||||||
|
zlib1g-dev
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
mkdir -p "$DIST_DIR" "$WORK_ROOT"
|
||||||
|
|
||||||
|
for nginx_version in $BUILD_NGINX_VERSIONS; do
|
||||||
|
archive="nginx-${nginx_version}.tar.gz"
|
||||||
|
archive_path="${WORK_ROOT}/${archive}"
|
||||||
|
workdir="${WORK_ROOT}/nginx-${nginx_version}"
|
||||||
|
runtime_root="${WORK_ROOT}/runtime/nginx-${nginx_version}"
|
||||||
|
|
||||||
|
if [[ ! -f "$archive_path" ]]; then
|
||||||
|
curl -fsSLo "$archive_path" "https://nginx.org/download/${archive}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$workdir"
|
||||||
|
tar -xzf "$archive_path" -C "$WORK_ROOT"
|
||||||
|
|
||||||
|
nginx_version_slug="$(printf '%s' "$nginx_version" | slugify)"
|
||||||
|
build_metadata="$(printf 'nginxorg.%s.%s.%s' "$nginx_version_slug" "$TARGET_ARCH" "$RELEASE_LIBC_BASELINE" | slugify)"
|
||||||
|
module_cflags="-DNGX_HTTP_MONITORING_MODULE_VERSION=\\\"${NEXT_VERSION}\\\" -DNGX_HTTP_MONITORING_BUILD_METADATA=\\\"${build_metadata}\\\""
|
||||||
|
|
||||||
|
cd "$workdir"
|
||||||
|
./configure \
|
||||||
|
--prefix=/usr/local/nginx \
|
||||||
|
--sbin-path=/usr/local/sbin/nginx \
|
||||||
|
--modules-path=/usr/local/nginx/modules \
|
||||||
|
--conf-path=/etc/nginx/nginx.conf \
|
||||||
|
--error-log-path=/var/log/nginx/error.log \
|
||||||
|
--http-log-path=/var/log/nginx/access.log \
|
||||||
|
--pid-path=/var/run/nginx.pid \
|
||||||
|
--lock-path=/var/run/nginx.lock \
|
||||||
|
--http-client-body-temp-path=/var/cache/nginx/client_temp \
|
||||||
|
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
|
||||||
|
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
|
||||||
|
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
|
||||||
|
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
|
||||||
|
--with-cc-opt="$module_cflags" \
|
||||||
|
$NGINX_CONFIGURE_ARGS \
|
||||||
|
--add-dynamic-module=/work
|
||||||
|
|
||||||
|
build_jobs="$(nproc)"
|
||||||
|
if [[ "$TARGET_ARCH" != "linux-x86_64" && "$build_jobs" -gt 2 ]]; then
|
||||||
|
build_jobs=2
|
||||||
|
fi
|
||||||
|
|
||||||
|
make -j"$build_jobs"
|
||||||
|
make -j"$build_jobs" modules
|
||||||
|
|
||||||
|
module_path="$workdir/objs/${MODULE_NAME}.so"
|
||||||
|
nginx_bin="$workdir/objs/nginx"
|
||||||
|
[[ -x "$nginx_bin" ]] || fail "nginx binary was not built at ${nginx_bin}"
|
||||||
|
[[ -f "$module_path" ]] || fail "module was not built at ${module_path}"
|
||||||
|
|
||||||
|
strip --strip-unneeded "$module_path" || true
|
||||||
|
validate_module "$nginx_bin" "$module_path" "$runtime_root"
|
||||||
|
|
||||||
|
module_sha256="$(sha256sum "$module_path" | awk '{ print $1 }')"
|
||||||
|
archive_sha256="$(sha256sum "$archive_path" | awk '{ print $1 }')"
|
||||||
|
artifact_base="${MODULE_NAME}-${NEXT_TAG}-nginxorg-${nginx_version_slug}-${TARGET_ARCH}"
|
||||||
|
package_root="${WORK_ROOT}/packages/${artifact_base}"
|
||||||
|
compatibility_json="${package_root}/COMPATIBILITY.json"
|
||||||
|
|
||||||
|
rm -rf "$package_root"
|
||||||
|
mkdir -p "$package_root/modules" "$package_root/docs" "$package_root/examples"
|
||||||
|
|
||||||
|
cp "$module_path" "$package_root/modules/${MODULE_NAME}.so"
|
||||||
|
cp /work/examples/nginx.conf "$package_root/examples/nginx.conf"
|
||||||
|
cp /work/examples/monitoring-site.conf "$package_root/examples/monitoring-site.conf"
|
||||||
|
cp /work/README.md "$package_root/README.md"
|
||||||
|
cp /work/docs/API.md "$package_root/docs/API.md"
|
||||||
|
cp /work/docs/CONFIGURATION.md "$package_root/docs/CONFIGURATION.md"
|
||||||
|
cp /work/docs/PERFORMANCE.md "$package_root/docs/PERFORMANCE.md"
|
||||||
|
"$nginx_bin" -V > "$package_root/NGINX-V.txt" 2>&1
|
||||||
|
|
||||||
|
tarball_file="${artifact_base}.tar.gz"
|
||||||
|
sidecar_file="${artifact_base}.compatibility.json"
|
||||||
|
|
||||||
|
jq -n \
|
||||||
|
--arg schema "1" \
|
||||||
|
--arg module "$MODULE_NAME" \
|
||||||
|
--arg releaseTag "$NEXT_TAG" \
|
||||||
|
--arg releaseVersion "$NEXT_VERSION" \
|
||||||
|
--arg previousTag "$LATEST_TAG" \
|
||||||
|
--arg gitRef "$GITHUB_REF" \
|
||||||
|
--arg gitSha "$GITHUB_SHA" \
|
||||||
|
--arg buildImage "$RELEASE_BUILD_IMAGE" \
|
||||||
|
--arg libcBaseline "$RELEASE_LIBC_BASELINE" \
|
||||||
|
--arg dockerPlatform "$DOCKER_PLATFORM" \
|
||||||
|
--arg targetArch "$TARGET_ARCH" \
|
||||||
|
--arg nginxVersion "$nginx_version" \
|
||||||
|
--arg configureArgs "$NGINX_CONFIGURE_ARGS" \
|
||||||
|
--arg archive "$archive" \
|
||||||
|
--arg archiveSha256 "$archive_sha256" \
|
||||||
|
--arg moduleSha256 "$module_sha256" \
|
||||||
|
--arg builtAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
|
--arg artifactBase "$artifact_base" \
|
||||||
|
--arg tarballFile "$tarball_file" \
|
||||||
|
--arg sidecarFile "$sidecar_file" \
|
||||||
|
--arg validationTime "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
|
'{
|
||||||
|
schema: ($schema | tonumber),
|
||||||
|
module: $module,
|
||||||
|
release: {
|
||||||
|
tag: $releaseTag,
|
||||||
|
version: $releaseVersion,
|
||||||
|
previous_tag: $previousTag,
|
||||||
|
git_ref: $gitRef,
|
||||||
|
git_sha: $gitSha
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
os_id: "nginx.org-source",
|
||||||
|
os_version: $nginxVersion,
|
||||||
|
os_codename: "upstream",
|
||||||
|
architecture: $targetArch,
|
||||||
|
requested_target: $targetArch,
|
||||||
|
docker_image: $buildImage,
|
||||||
|
docker_platform: $dockerPlatform,
|
||||||
|
libc_baseline: $libcBaseline
|
||||||
|
},
|
||||||
|
nginx: {
|
||||||
|
binary_package: "nginx.org-source-build",
|
||||||
|
binary_version: $nginxVersion,
|
||||||
|
source_package: "nginx.org",
|
||||||
|
source_version: $nginxVersion,
|
||||||
|
abi: "",
|
||||||
|
modules_path: "/usr/local/nginx/modules",
|
||||||
|
build: {
|
||||||
|
version: ("nginx/" + $nginxVersion),
|
||||||
|
configure_args: ($configureArgs | split(" ")),
|
||||||
|
configure_line: $configureArgs
|
||||||
|
}
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
method: "upstream-nginxorg-source",
|
||||||
|
source_archive: $archive,
|
||||||
|
source_archive_sha256: $archiveSha256,
|
||||||
|
module_sha256: $moduleSha256,
|
||||||
|
built_at: $builtAt
|
||||||
|
},
|
||||||
|
artifacts: {
|
||||||
|
base_name: $artifactBase,
|
||||||
|
deb_package: "",
|
||||||
|
deb_version: "",
|
||||||
|
deb_file: "",
|
||||||
|
tarball_file: $tarballFile,
|
||||||
|
compatibility_file: $sidecarFile
|
||||||
|
},
|
||||||
|
packaging: {
|
||||||
|
deb_depends: []
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
packaged_nginx_config_test: false,
|
||||||
|
packaged_nginx_endpoint_smoke: false,
|
||||||
|
built_nginx_config_test: true,
|
||||||
|
built_nginx_endpoint_smoke: true,
|
||||||
|
validated_at: $validationTime
|
||||||
|
}
|
||||||
|
}' > "$compatibility_json"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf '# %s for nginx.org %s\n\n' "$MODULE_NAME" "$nginx_version"
|
||||||
|
printf 'Release: `%s`\n\n' "$NEXT_TAG"
|
||||||
|
printf 'Target: `%s` (`%s`)\n\n' "$TARGET_ARCH" "$DOCKER_PLATFORM"
|
||||||
|
printf 'Build baseline: `%s`\n\n' "$RELEASE_LIBC_BASELINE"
|
||||||
|
printf 'This tarball is built against upstream nginx.org source, not a Debian or Ubuntu source package.\n\n'
|
||||||
|
printf 'Use this artifact for Nginx binaries built from the same upstream source version and compatible configure options. For distribution-packaged Nginx, prefer the distro `.deb` release artifacts.\n\n'
|
||||||
|
printf 'Load it near the top of `nginx.conf`:\n\n'
|
||||||
|
printf '```nginx\n'
|
||||||
|
printf 'load_module modules/%s.so;\n' "$MODULE_NAME"
|
||||||
|
printf '```\n'
|
||||||
|
} > "$package_root/INSTALL.md"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf 'module=%s\n' "$MODULE_NAME"
|
||||||
|
printf 'module_release=%s\n' "$NEXT_TAG"
|
||||||
|
printf 'module_version=%s\n' "$NEXT_VERSION"
|
||||||
|
printf 'module_build_metadata=%s\n' "$build_metadata"
|
||||||
|
printf 'nginx_source=nginx.org\n'
|
||||||
|
printf 'nginx_version=%s\n' "$nginx_version"
|
||||||
|
printf 'target=%s\n' "$TARGET_ARCH"
|
||||||
|
printf 'docker_platform=%s\n' "$DOCKER_PLATFORM"
|
||||||
|
printf 'build_image=%s\n' "$RELEASE_BUILD_IMAGE"
|
||||||
|
printf 'libc_baseline=%s\n' "$RELEASE_LIBC_BASELINE"
|
||||||
|
printf 'configure_args=%s\n' "$NGINX_CONFIGURE_ARGS"
|
||||||
|
printf 'git_ref=%s\n' "$GITHUB_REF"
|
||||||
|
printf 'git_sha=%s\n' "$GITHUB_SHA"
|
||||||
|
printf 'built_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
} > "$package_root/METADATA.txt"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$package_root"
|
||||||
|
sha256sum "modules/${MODULE_NAME}.so" COMPATIBILITY.json > SHA256SUMS
|
||||||
|
file "modules/${MODULE_NAME}.so" > FILE.txt
|
||||||
|
)
|
||||||
|
|
||||||
|
tar -czf "$DIST_DIR/$tarball_file" -C "$(dirname "$package_root")" "$(basename "$package_root")"
|
||||||
|
cp "$compatibility_json" "$DIST_DIR/$sidecar_file"
|
||||||
|
|
||||||
|
echo "Built $DIST_DIR/$tarball_file"
|
||||||
|
echo "Built $DIST_DIR/$sidecar_file"
|
||||||
|
done
|
||||||
53
scripts/enable-apt-source-repos.sh
Normal file
53
scripts/enable-apt-source-repos.sh
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
enable_deb822_sources() {
|
||||||
|
local file="$1"
|
||||||
|
local tmp
|
||||||
|
|
||||||
|
tmp="$(mktemp)"
|
||||||
|
awk '
|
||||||
|
/^Types:/ {
|
||||||
|
if ($0 !~ /(^|[[:space:]])deb-src($|[[:space:]])/) {
|
||||||
|
print $0 " deb-src"
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ print }
|
||||||
|
' "$file" > "$tmp"
|
||||||
|
cat "$tmp" > "$file"
|
||||||
|
rm -f "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_legacy_sources() {
|
||||||
|
local file="$1"
|
||||||
|
local output="$2"
|
||||||
|
|
||||||
|
awk '
|
||||||
|
/^[[:space:]]*deb[[:space:]]/ {
|
||||||
|
line = $0
|
||||||
|
sub(/^[[:space:]]*deb[[:space:]]+/, "deb-src ", line)
|
||||||
|
print line
|
||||||
|
}
|
||||||
|
' "$file" > "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
found_sources=0
|
||||||
|
|
||||||
|
for sources_file in /etc/apt/sources.list.d/*.sources; do
|
||||||
|
if [[ -f "$sources_file" ]]; then
|
||||||
|
found_sources=1
|
||||||
|
enable_deb822_sources "$sources_file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -f /etc/apt/sources.list ]]; then
|
||||||
|
found_sources=1
|
||||||
|
enable_legacy_sources /etc/apt/sources.list \
|
||||||
|
/etc/apt/sources.list.d/codex-deb-src.list
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$found_sources" -eq 0 ]]; then
|
||||||
|
echo "No apt source list files were found under /etc/apt" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
150
scripts/nginx-build-info.py
Normal file
150
scripts/nginx-build-info.py
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def nginx_v_output(nginx_bin):
|
||||||
|
proc = subprocess.run(
|
||||||
|
[nginx_bin, "-V"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
return proc.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def parse_nginx_v(output):
|
||||||
|
version = ""
|
||||||
|
configure_line = ""
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith("nginx version:"):
|
||||||
|
version = line.split(":", 1)[1].strip()
|
||||||
|
elif line.startswith("configure arguments:"):
|
||||||
|
configure_line = line.split(":", 1)[1].strip()
|
||||||
|
|
||||||
|
if not configure_line:
|
||||||
|
raise RuntimeError("nginx -V did not include configure arguments")
|
||||||
|
|
||||||
|
return version, configure_line, shlex.split(configure_line)
|
||||||
|
|
||||||
|
|
||||||
|
def option_value(args, option):
|
||||||
|
prefix = option + "="
|
||||||
|
for arg in args:
|
||||||
|
if arg.startswith(prefix):
|
||||||
|
return arg[len(prefix) :]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_module_path(arg, nginx_src):
|
||||||
|
for option in ("--add-module=", "--add-dynamic-module="):
|
||||||
|
if not arg.startswith(option):
|
||||||
|
continue
|
||||||
|
|
||||||
|
module_path = arg[len(option) :]
|
||||||
|
if os.path.exists(module_path):
|
||||||
|
return arg
|
||||||
|
|
||||||
|
marker = "/debian/modules/"
|
||||||
|
if marker in module_path:
|
||||||
|
suffix = module_path.split(marker, 1)[1]
|
||||||
|
candidate = os.path.join(nginx_src, "debian", "modules", suffix)
|
||||||
|
if os.path.exists(candidate):
|
||||||
|
return option + candidate
|
||||||
|
|
||||||
|
marker = "/debian/"
|
||||||
|
if marker in module_path:
|
||||||
|
suffix = module_path.split(marker, 1)[1]
|
||||||
|
candidate = os.path.join(nginx_src, "debian", suffix)
|
||||||
|
if os.path.exists(candidate):
|
||||||
|
return option + candidate
|
||||||
|
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
def add_extra_cc_opt(args, extra_cc_opt):
|
||||||
|
if not extra_cc_opt:
|
||||||
|
return args
|
||||||
|
|
||||||
|
rewritten = []
|
||||||
|
found = False
|
||||||
|
|
||||||
|
for arg in args:
|
||||||
|
if arg.startswith("--with-cc-opt="):
|
||||||
|
found = True
|
||||||
|
rewritten.append(arg + " " + extra_cc_opt)
|
||||||
|
else:
|
||||||
|
rewritten.append(arg)
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
rewritten.append("--with-cc-opt=" + extra_cc_opt)
|
||||||
|
|
||||||
|
return rewritten
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_configure_args(args, nginx_src, extra_cc_opt):
|
||||||
|
if not nginx_src:
|
||||||
|
return add_extra_cc_opt(args, extra_cc_opt)
|
||||||
|
|
||||||
|
rewritten = [rewrite_module_path(arg, nginx_src) for arg in args]
|
||||||
|
return add_extra_cc_opt(rewritten, extra_cc_opt)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--nginx-bin", default="nginx")
|
||||||
|
parser.add_argument("--nginx-src", default="")
|
||||||
|
parser.add_argument("--extra-cc-opt", default="")
|
||||||
|
parser.add_argument("--json", action="store_true")
|
||||||
|
parser.add_argument("--configure-line", action="store_true")
|
||||||
|
parser.add_argument("--modules-path", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
output = nginx_v_output(args.nginx_bin)
|
||||||
|
version, configure_line, configure_args = parse_nginx_v(output)
|
||||||
|
rewritten_args = rewrite_configure_args(
|
||||||
|
configure_args,
|
||||||
|
args.nginx_src,
|
||||||
|
args.extra_cc_opt,
|
||||||
|
)
|
||||||
|
|
||||||
|
modules_path = option_value(configure_args, "--modules-path")
|
||||||
|
if not modules_path:
|
||||||
|
prefix = option_value(configure_args, "--prefix") or "/usr/share/nginx"
|
||||||
|
modules_path = os.path.join(prefix, "modules")
|
||||||
|
|
||||||
|
if args.configure_line:
|
||||||
|
print(shlex.join(rewritten_args))
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.modules_path:
|
||||||
|
print(modules_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.json:
|
||||||
|
json.dump(
|
||||||
|
{
|
||||||
|
"version": version,
|
||||||
|
"configure_line": configure_line,
|
||||||
|
"configure_args": configure_args,
|
||||||
|
"rewritten_configure_args": rewritten_args,
|
||||||
|
"modules_path": modules_path,
|
||||||
|
},
|
||||||
|
sys.stdout,
|
||||||
|
indent=2,
|
||||||
|
sort_keys=True,
|
||||||
|
)
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
parser.error("choose one of --json, --configure-line, or --modules-path")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: ngx-http-monitoring-client
|
name: ngx-http-monitoring-client
|
||||||
description: Use this skill when connecting clients or agents to an ngx_http_monitoring_module service, querying its JSON/SSE/Prometheus endpoints, debugging access problems, or integrating dashboards/health checks. Supports deployments protected by Nginx Basic Auth and/or monitor_api_token.
|
description: Use this skill when connecting clients or agents to an ngx_http_monitoring_module service, querying its JSON/SSE/Prometheus endpoints, reading API response structures, debugging access problems, or integrating dashboards/health checks. Supports deployments protected by Nginx Basic Auth and/or monitor_api_token.
|
||||||
---
|
---
|
||||||
|
|
||||||
# ngx_http_monitoring_module Client
|
# ngx_http_monitoring_module Client
|
||||||
@@ -42,6 +42,400 @@ MONITOR_BASIC_AUTH=user:password
|
|||||||
- `GET /monitor/metrics` - Prometheus text format
|
- `GET /monitor/metrics` - Prometheus text format
|
||||||
- `GET /monitor/health` - lightweight JSON health check
|
- `GET /monitor/health` - lightweight JSON health check
|
||||||
|
|
||||||
|
## API Structures
|
||||||
|
|
||||||
|
All JSON API responses are compact, versioned, and timestamped. Treat unknown fields as forward-compatible additions, and treat missing sections as disabled collectors, unsupported Nginx build options, or unavailable Linux `/proc` data.
|
||||||
|
|
||||||
|
Common envelope:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"module": "1.0.0",
|
||||||
|
"timestamp": 1710000000,
|
||||||
|
"msec": 123,
|
||||||
|
"scope": "full",
|
||||||
|
"pid": 12345
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Endpoint-specific API routes such as `/monitor/api/system` return the common envelope plus one top-level field matching the route. The examples below show that section's value unless explicitly shown as a complete response.
|
||||||
|
|
||||||
|
`GET /monitor/api` returns the envelope plus:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"system": {},
|
||||||
|
"nginx": {},
|
||||||
|
"network": {},
|
||||||
|
"disk": {},
|
||||||
|
"processes": {},
|
||||||
|
"upstreams": [],
|
||||||
|
"connections": {},
|
||||||
|
"requests": {},
|
||||||
|
"history": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/system`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cpu": {
|
||||||
|
"usage": 12.5,
|
||||||
|
"cores": 8,
|
||||||
|
"load": [0.12, 0.20, 0.30]
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"total": 16777216,
|
||||||
|
"available": 8388608,
|
||||||
|
"free": 4194304,
|
||||||
|
"used": 8388608,
|
||||||
|
"used_pct": 50.0
|
||||||
|
},
|
||||||
|
"swap": {
|
||||||
|
"total": 2097152,
|
||||||
|
"free": 1048576,
|
||||||
|
"used_pct": 50.0
|
||||||
|
},
|
||||||
|
"uptime": 123456
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/nginx`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"connections": {
|
||||||
|
"accepted": 1000,
|
||||||
|
"handled": 1000,
|
||||||
|
"active": 20,
|
||||||
|
"reading": 1,
|
||||||
|
"writing": 4,
|
||||||
|
"waiting": 15
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"total": 50000,
|
||||||
|
"responses": 49990,
|
||||||
|
"requests_per_sec": 125.5,
|
||||||
|
"responses_per_sec": 125.3
|
||||||
|
},
|
||||||
|
"ssl": {
|
||||||
|
"requests": 100,
|
||||||
|
"handshakes": 20
|
||||||
|
},
|
||||||
|
"keepalive": {
|
||||||
|
"requests": 250
|
||||||
|
},
|
||||||
|
"workers": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/network`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"rx_bytes": 123456789,
|
||||||
|
"tx_bytes": 987654321,
|
||||||
|
"rx_packets": 10000,
|
||||||
|
"tx_packets": 12000,
|
||||||
|
"rx_errors": 0,
|
||||||
|
"tx_errors": 0,
|
||||||
|
"interfaces": [
|
||||||
|
{
|
||||||
|
"name": "eth0",
|
||||||
|
"rx_bytes": 123456789,
|
||||||
|
"tx_bytes": 987654321,
|
||||||
|
"rx_packets": 10000,
|
||||||
|
"tx_packets": 12000,
|
||||||
|
"rx_errors": 0,
|
||||||
|
"tx_errors": 0,
|
||||||
|
"up": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/disk`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"reads": 1000,
|
||||||
|
"writes": 2000,
|
||||||
|
"read_bytes": 4096000,
|
||||||
|
"write_bytes": 8192000,
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"name": "sda",
|
||||||
|
"reads": 1000,
|
||||||
|
"writes": 2000,
|
||||||
|
"read_bytes": 4096000,
|
||||||
|
"write_bytes": 8192000,
|
||||||
|
"io_ms": 120
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filesystems": [
|
||||||
|
{
|
||||||
|
"path": "/",
|
||||||
|
"type": "ext4",
|
||||||
|
"total": 107374182400,
|
||||||
|
"used": 53687091200,
|
||||||
|
"free": 53687091200,
|
||||||
|
"avail": 53687091200,
|
||||||
|
"files": 1000000,
|
||||||
|
"files_free": 750000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/processes`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"process_count": 140,
|
||||||
|
"tcp": {
|
||||||
|
"established": 20,
|
||||||
|
"listen": 8
|
||||||
|
},
|
||||||
|
"sockets": {
|
||||||
|
"used": 512,
|
||||||
|
"tcp": 120,
|
||||||
|
"udp": 16
|
||||||
|
},
|
||||||
|
"workers": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/connections`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"accepted": 1000,
|
||||||
|
"handled": 1000,
|
||||||
|
"active": 20,
|
||||||
|
"reading": 1,
|
||||||
|
"writing": 4,
|
||||||
|
"waiting": 15,
|
||||||
|
"ssl_requests": 100,
|
||||||
|
"ssl_handshakes": 20,
|
||||||
|
"keepalive_requests": 250,
|
||||||
|
"sse_clients": 2,
|
||||||
|
"sse_events": 500,
|
||||||
|
"rate_limited": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/requests`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"total": 50000,
|
||||||
|
"responses": 49990,
|
||||||
|
"bytes": 104857600,
|
||||||
|
"requests_per_sec": 125.5,
|
||||||
|
"responses_per_sec": 125.3,
|
||||||
|
"error_rate": 0.0078,
|
||||||
|
"latency": {
|
||||||
|
"avg": 8.4,
|
||||||
|
"p50": 4,
|
||||||
|
"p90": 15,
|
||||||
|
"p95": 22,
|
||||||
|
"p99": 80
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"1xx": 0,
|
||||||
|
"2xx": 49000,
|
||||||
|
"3xx": 600,
|
||||||
|
"4xx": 300,
|
||||||
|
"5xx": 90
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"GET": 45000,
|
||||||
|
"POST": 4000,
|
||||||
|
"PUT": 100,
|
||||||
|
"DELETE": 50,
|
||||||
|
"HEAD": 500,
|
||||||
|
"OPTIONS": 200,
|
||||||
|
"PATCH": 50,
|
||||||
|
"OTHER": 100
|
||||||
|
},
|
||||||
|
"latency_histogram": [
|
||||||
|
{
|
||||||
|
"le": 1,
|
||||||
|
"count": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"le": "+Inf",
|
||||||
|
"count": 50000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"size_histogram": [
|
||||||
|
{
|
||||||
|
"le": 512,
|
||||||
|
"count": 10000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"le": "+Inf",
|
||||||
|
"count": 50000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_urls": [],
|
||||||
|
"user_agents": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/api/upstreams`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"peer": "127.0.0.1:9000",
|
||||||
|
"requests": 1000,
|
||||||
|
"failures": 2,
|
||||||
|
"status_4xx": 10,
|
||||||
|
"status_5xx": 2,
|
||||||
|
"avg_latency": 12.5,
|
||||||
|
"last_seen": 1710000000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
History samples in `GET /monitor/api`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1710000000,
|
||||||
|
"cpu": 12.5,
|
||||||
|
"memory": 50.0,
|
||||||
|
"swap": 0.0,
|
||||||
|
"rps": 125.5,
|
||||||
|
"responses_per_sec": 125.3,
|
||||||
|
"network_rx_per_sec": 1024,
|
||||||
|
"network_tx_per_sec": 2048,
|
||||||
|
"disk_read_per_sec": 0,
|
||||||
|
"disk_write_per_sec": 4096,
|
||||||
|
"latency_p95": 22,
|
||||||
|
"requests_total": 50000,
|
||||||
|
"status_4xx": 300,
|
||||||
|
"status_5xx": 90
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Shared nested structures:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"worker": {
|
||||||
|
"slot": 0,
|
||||||
|
"pid": 12345,
|
||||||
|
"active": true,
|
||||||
|
"requests": 10000,
|
||||||
|
"bytes": 10485760,
|
||||||
|
"errors": 3,
|
||||||
|
"vm_size": 102400,
|
||||||
|
"vm_rss": 20480,
|
||||||
|
"voluntary_ctxt": 100,
|
||||||
|
"nonvoluntary_ctxt": 5,
|
||||||
|
"last_seen": 1710000000
|
||||||
|
},
|
||||||
|
"top_url": {
|
||||||
|
"url": "/api/orders",
|
||||||
|
"hits": 1000,
|
||||||
|
"errors": 3,
|
||||||
|
"bytes": 1048576,
|
||||||
|
"avg_latency": 7.8,
|
||||||
|
"last_seen": 1710000000
|
||||||
|
},
|
||||||
|
"user_agent": {
|
||||||
|
"user_agent": "curl/8.0.1",
|
||||||
|
"hits": 100,
|
||||||
|
"errors": 0,
|
||||||
|
"bytes": 20480,
|
||||||
|
"avg_latency": 2.1,
|
||||||
|
"last_seen": 1710000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SSE `GET /monitor/live` sends `metrics` events whose `data` payload has this structure:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"timestamp": 1710000000,
|
||||||
|
"sequence": 42,
|
||||||
|
"system": {
|
||||||
|
"cpu": {
|
||||||
|
"usage": 12.5,
|
||||||
|
"cores": 8
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"used_pct": 50.0,
|
||||||
|
"total": 16777216,
|
||||||
|
"available": 8388608
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"total": 50000,
|
||||||
|
"requests_per_sec": 125.5,
|
||||||
|
"responses_per_sec": 125.3,
|
||||||
|
"latency": {
|
||||||
|
"p95": 22,
|
||||||
|
"p99": 80
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"4xx": 300,
|
||||||
|
"5xx": 90
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"active": 20,
|
||||||
|
"reading": 1,
|
||||||
|
"writing": 4,
|
||||||
|
"waiting": 15,
|
||||||
|
"sse_clients": 2,
|
||||||
|
"keepalive_requests": 250
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"rx_bytes": 123456789,
|
||||||
|
"tx_bytes": 987654321
|
||||||
|
},
|
||||||
|
"disk": {
|
||||||
|
"read_bytes": 4096000,
|
||||||
|
"write_bytes": 8192000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/metrics` exposes Prometheus text metrics including:
|
||||||
|
|
||||||
|
```text
|
||||||
|
nginx_monitor_requests_total
|
||||||
|
nginx_monitor_active_connections
|
||||||
|
nginx_monitor_cpu_usage_ratio
|
||||||
|
nginx_monitor_memory_used_ratio
|
||||||
|
nginx_monitor_latency_p95_ms
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /monitor/health` returns a complete health response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"module": "1.0.0",
|
||||||
|
"timestamp": 1710000000,
|
||||||
|
"msec": 123,
|
||||||
|
"scope": "health",
|
||||||
|
"pid": 12345,
|
||||||
|
"status": "ok",
|
||||||
|
"generation": 100,
|
||||||
|
"sse_clients": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Auth Rules
|
## Auth Rules
|
||||||
|
|
||||||
If the service is behind Nginx Basic Auth, include Basic Auth on every request, including `/monitor`, `/monitor/live`, and `/monitor/metrics`.
|
If the service is behind Nginx Basic Auth, include Basic Auth on every request, including `/monitor`, `/monitor/live`, and `/monitor/metrics`.
|
||||||
|
|||||||
@@ -9,7 +9,14 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define NGX_HTTP_MONITORING_VERSION 1
|
#define NGX_HTTP_MONITORING_VERSION 1
|
||||||
#define NGX_HTTP_MONITORING_MODULE_VERSION "1.0.0"
|
|
||||||
|
#ifndef NGX_HTTP_MONITORING_MODULE_VERSION
|
||||||
|
#define NGX_HTTP_MONITORING_MODULE_VERSION "0.0.0-dev"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NGX_HTTP_MONITORING_BUILD_METADATA
|
||||||
|
#define NGX_HTTP_MONITORING_BUILD_METADATA "source"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define NGX_HTTP_MONITORING_DEFAULT_SHM_SIZE (8 * 1024 * 1024)
|
#define NGX_HTTP_MONITORING_DEFAULT_SHM_SIZE (8 * 1024 * 1024)
|
||||||
#define NGX_HTTP_MONITORING_MIN_INTERVAL 100
|
#define NGX_HTTP_MONITORING_MIN_INTERVAL 100
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
#include <mntent.h>
|
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -41,6 +40,9 @@ static ngx_atomic_uint_t ngx_http_monitoring_sum_disk(
|
|||||||
static ngx_atomic_uint_t ngx_http_monitoring_delta(ngx_atomic_uint_t now,
|
static ngx_atomic_uint_t ngx_http_monitoring_delta(ngx_atomic_uint_t now,
|
||||||
ngx_atomic_uint_t prev);
|
ngx_atomic_uint_t prev);
|
||||||
static ngx_uint_t ngx_http_monitoring_skip_fs(const char *type);
|
static ngx_uint_t ngx_http_monitoring_skip_fs(const char *type);
|
||||||
|
static char *ngx_http_monitoring_next_mount_field(char **cursor);
|
||||||
|
static void ngx_http_monitoring_copy_mount_field(u_char *dst, size_t size,
|
||||||
|
char *src);
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
@@ -599,25 +601,45 @@ static void
|
|||||||
ngx_http_monitoring_collect_filesystems(ngx_http_monitoring_shctx_t *sh)
|
ngx_http_monitoring_collect_filesystems(ngx_http_monitoring_shctx_t *sh)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
struct mntent *mnt;
|
char line[4096], *cursor, *source, *mount, *type;
|
||||||
|
u_char mount_path[4096], fs_type[NGX_HTTP_MONITORING_NAME_LEN];
|
||||||
struct statvfs st;
|
struct statvfs st;
|
||||||
ngx_uint_t count;
|
ngx_uint_t count;
|
||||||
uint64_t total, free_bytes, avail, used;
|
uint64_t total, free_bytes, avail, used;
|
||||||
|
|
||||||
fp = setmntent("/proc/mounts", "r");
|
fp = fopen("/proc/mounts", "r");
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
while ((mnt = getmntent(fp)) != NULL
|
while (count < NGX_HTTP_MONITORING_FILESYSTEMS_MAX
|
||||||
&& count < NGX_HTTP_MONITORING_FILESYSTEMS_MAX)
|
&& fgets(line, sizeof(line), fp) != NULL)
|
||||||
{
|
{
|
||||||
if (ngx_http_monitoring_skip_fs(mnt->mnt_type)) {
|
cursor = line;
|
||||||
|
source = ngx_http_monitoring_next_mount_field(&cursor);
|
||||||
|
mount = ngx_http_monitoring_next_mount_field(&cursor);
|
||||||
|
type = ngx_http_monitoring_next_mount_field(&cursor);
|
||||||
|
|
||||||
|
if (source == NULL || mount == NULL || type == NULL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statvfs(mnt->mnt_dir, &st) != 0 || st.f_blocks == 0) {
|
(void) source;
|
||||||
|
|
||||||
|
ngx_http_monitoring_copy_mount_field(mount_path, sizeof(mount_path),
|
||||||
|
mount);
|
||||||
|
ngx_http_monitoring_copy_mount_field(fs_type, sizeof(fs_type), type);
|
||||||
|
|
||||||
|
if (mount_path[0] == '\0' || fs_type[0] == '\0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngx_http_monitoring_skip_fs((char *) fs_type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statvfs((char *) mount_path, &st) != 0 || st.f_blocks == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,9 +648,9 @@ ngx_http_monitoring_collect_filesystems(ngx_http_monitoring_shctx_t *sh)
|
|||||||
avail = (uint64_t) st.f_bavail * st.f_frsize;
|
avail = (uint64_t) st.f_bavail * st.f_frsize;
|
||||||
used = total - free_bytes;
|
used = total - free_bytes;
|
||||||
|
|
||||||
ngx_cpystrn(sh->filesystems[count].path, (u_char *) mnt->mnt_dir,
|
ngx_cpystrn(sh->filesystems[count].path, mount_path,
|
||||||
NGX_HTTP_MONITORING_KEY_LEN);
|
NGX_HTTP_MONITORING_KEY_LEN);
|
||||||
ngx_cpystrn(sh->filesystems[count].type, (u_char *) mnt->mnt_type,
|
ngx_cpystrn(sh->filesystems[count].type, fs_type,
|
||||||
NGX_HTTP_MONITORING_NAME_LEN);
|
NGX_HTTP_MONITORING_NAME_LEN);
|
||||||
sh->filesystems[count].total = (ngx_atomic_uint_t) total;
|
sh->filesystems[count].total = (ngx_atomic_uint_t) total;
|
||||||
sh->filesystems[count].used = (ngx_atomic_uint_t) used;
|
sh->filesystems[count].used = (ngx_atomic_uint_t) used;
|
||||||
@@ -640,7 +662,7 @@ ngx_http_monitoring_collect_filesystems(ngx_http_monitoring_shctx_t *sh)
|
|||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
endmntent(fp);
|
fclose(fp);
|
||||||
sh->fs_count = count;
|
sh->fs_count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,6 +844,72 @@ ngx_http_monitoring_delta(ngx_atomic_uint_t now, ngx_atomic_uint_t prev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_http_monitoring_next_mount_field(char **cursor)
|
||||||
|
{
|
||||||
|
char *p, *start;
|
||||||
|
|
||||||
|
p = *cursor;
|
||||||
|
|
||||||
|
while (*p == ' ' || *p == '\t') {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '\0' || *p == '\n') {
|
||||||
|
*cursor = p;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = p;
|
||||||
|
|
||||||
|
while (*p != '\0' && *p != '\n' && *p != ' ' && *p != '\t') {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p != '\0') {
|
||||||
|
*p++ = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
*cursor = p;
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ngx_http_monitoring_copy_mount_field(u_char *dst, size_t size, char *src)
|
||||||
|
{
|
||||||
|
u_char *p, *last;
|
||||||
|
ngx_uint_t value;
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = dst;
|
||||||
|
last = dst + size - 1;
|
||||||
|
|
||||||
|
while (*src != '\0' && p < last) {
|
||||||
|
if (src[0] == '\\'
|
||||||
|
&& src[1] >= '0' && src[1] <= '7'
|
||||||
|
&& src[2] >= '0' && src[2] <= '7'
|
||||||
|
&& src[3] >= '0' && src[3] <= '7')
|
||||||
|
{
|
||||||
|
value = (ngx_uint_t) ((src[1] - '0') * 64
|
||||||
|
+ (src[2] - '0') * 8
|
||||||
|
+ (src[3] - '0'));
|
||||||
|
*p++ = (u_char) value;
|
||||||
|
src += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p++ = (u_char) *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_uint_t
|
static ngx_uint_t
|
||||||
ngx_http_monitoring_skip_fs(const char *type)
|
ngx_http_monitoring_skip_fs(const char *type)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -411,10 +411,11 @@ ngx_http_monitoring_json_header(ngx_http_monitoring_json_writer_t *jw,
|
|||||||
tp = ngx_timeofday();
|
tp = ngx_timeofday();
|
||||||
|
|
||||||
ngx_http_monitoring_json_printf(jw,
|
ngx_http_monitoring_json_printf(jw,
|
||||||
"{\"version\":%d,\"module\":\"%s\",\"timestamp\":%T,"
|
"{\"version\":%d,\"module\":\"%s\",\"build\":\"%s\","
|
||||||
"\"msec\":%M,\"scope\":\"%s\",\"pid\":%P",
|
"\"timestamp\":%T,\"msec\":%M,\"scope\":\"%s\",\"pid\":%P",
|
||||||
NGX_HTTP_MONITORING_VERSION,
|
NGX_HTTP_MONITORING_VERSION,
|
||||||
NGX_HTTP_MONITORING_MODULE_VERSION,
|
NGX_HTTP_MONITORING_MODULE_VERSION,
|
||||||
|
NGX_HTTP_MONITORING_BUILD_METADATA,
|
||||||
tp->sec, tp->msec, scope, ngx_pid);
|
tp->sec, tp->msec, scope, ngx_pid);
|
||||||
|
|
||||||
(void) r;
|
(void) r;
|
||||||
|
|||||||
Reference in New Issue
Block a user