diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 1d0cefd..ba87556 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -12,6 +12,20 @@ on: - master - main workflow_dispatch: + inputs: + component: + description: Component to publish + required: true + type: choice + options: + - server-e2e + - agent + - server + - web + tag: + description: Version-prefixed image tag with a suffix (e.g. 7.0.0-e2e.1) + required: true + type: string env: REGISTRY: ghcr.io @@ -26,7 +40,7 @@ jobs: strategy: matrix: - component: [agent, server, server-e2e, web] + component: ${{ github.event_name == 'workflow_dispatch' && fromJSON(format('["{0}"]', inputs.component)) || fromJSON('["agent","server","server-e2e","web"]') }} steps: - name: Checkout repository @@ -51,16 +65,39 @@ jobs: echo "netxms_version=${NETXMS_VERSION}" >> $GITHUB_OUTPUT echo "package_version=${NETXMS_PACKAGE_VERSION}" >> $GITHUB_OUTPUT + - name: Validate manual publish + if: github.event_name == 'workflow_dispatch' + env: + PUBLISH_TAG: ${{ inputs.tag }} + NETXMS_VERSION: ${{ steps.version.outputs.netxms_version }} + SELECTED_COMPONENT: ${{ inputs.component }} + BUILD_COMPONENT: ${{ matrix.component }} + run: | + case "$SELECTED_COMPONENT" in + agent|server|server-e2e|web) ;; + *) echo "::error::Unknown component"; exit 1 ;; + esac + if [[ "$SELECTED_COMPONENT" != "$BUILD_COMPONENT" ]]; then + echo "::error::Manual publish must build only the selected component" + exit 1 + fi + if [[ "$PUBLISH_TAG" != "$NETXMS_VERSION"-?* || ! "$PUBLISH_TAG" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]]; then + echo "::error::Tag must be a valid Docker tag starting with $NETXMS_VERSION- and a nonempty suffix" + exit 1 + fi + - name: Generate metadata id: meta uses: docker/metadata-action@v6 with: images: ${{ env.IMAGE_PREFIX }}/${{ matrix.component }} + flavor: latest=${{ github.event_name == 'workflow_dispatch' && 'false' || 'auto' }} tags: | - type=match,pattern=release-(\d+\.\d+\.\d+)$,group=1 - type=match,pattern=release-(\d+\.\d+)\.\d+$,group=1 - type=raw,value=${{ steps.version.outputs.netxms_version }},enable=${{ startsWith(github.ref, 'refs/tags/release-') }} - type=raw,value=latest,enable={{is_default_branch}} + type=match,pattern=release-(\d+\.\d+\.\d+)$,group=1,enable=${{ github.event_name != 'workflow_dispatch' }} + type=match,pattern=release-(\d+\.\d+)\.\d+$,group=1,enable=${{ github.event_name != 'workflow_dispatch' }} + type=raw,value=${{ steps.version.outputs.netxms_version }},enable=${{ github.event_name != 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/release-') }} + type=raw,value=latest,enable=${{ github.event_name != 'workflow_dispatch' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + type=raw,value=${{ inputs.tag || 'manual' }},enable=${{ github.event_name == 'workflow_dispatch' }} - name: Download NetXMS web archive if: matrix.component == 'web' @@ -76,6 +113,7 @@ jobs: ./pin-package-version ${{ matrix.component }}/files/netxms-pin ${{ steps.version.outputs.package_version }} - name: Build and push Docker image + if: matrix.component != 'server-e2e' uses: docker/build-push-action@v7 with: context: ./${{ matrix.component }} @@ -89,6 +127,76 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + - name: Build and load e2e image + if: matrix.component == 'server-e2e' + uses: docker/build-push-action@v7 + with: + context: ./server-e2e + platforms: linux/amd64 + load: true + push: false + provenance: false + tags: netxms-server-e2e:smoke + labels: ${{ steps.meta.outputs.labels }} + build-args: | + NETXMS_VERSION=${{ steps.version.outputs.netxms_version }} + NETXMS_PACKAGE_VERSION=${{ steps.version.outputs.package_version }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Smoke test e2e image + if: matrix.component == 'server-e2e' + timeout-minutes: 3 + run: | + NX_API_KEY=$(sed -n 's/^NX_API_KEY=//p' README.md) + if [[ ! "$NX_API_KEY" =~ ^[a-z2-7]{32}$ ]]; then + echo "::error::README must contain one public fixture API key" + exit 1 + fi + docker run --detach --platform linux/amd64 --network none --name netxms-e2e-smoke netxms-server-e2e:smoke + deadline=$((SECONDS + 120)) + while (( SECONDS < deadline )); do + if [[ "$(docker inspect --format '{{.State.Running}}' netxms-e2e-smoke)" != true ]]; then + echo "::error::NetXMS exited before the WebAPI became ready" + exit 1 + fi + if docker exec netxms-e2e-smoke curl --fail --silent --show-error --connect-timeout 1 --max-time 2 --output /dev/null --header "Authorization: Bearer $NX_API_KEY" http://127.0.0.1:8000/v1/server-info; then + exit 0 + fi + sleep 1 + done + echo "::error::NetXMS WebAPI did not become ready within 120 seconds" + exit 1 + + - name: Push tested e2e image + if: matrix.component == 'server-e2e' && github.event_name != 'pull_request' + env: + IMAGE_TAGS: ${{ steps.meta.outputs.tags }} + run: | + if [[ -z "$IMAGE_TAGS" ]]; then + echo "::error::No e2e image tags were generated" + exit 1 + fi + while IFS= read -r tag; do + [[ -n "$tag" ]] || continue + docker tag netxms-server-e2e:smoke "$tag" + docker push "$tag" + done <<< "$IMAGE_TAGS" + + - name: E2e container logs + if: failure() && matrix.component == 'server-e2e' + run: | + if docker container inspect netxms-e2e-smoke > /dev/null 2>&1; then + docker logs netxms-e2e-smoke + fi + + - name: Clean up e2e container + if: always() && matrix.component == 'server-e2e' + run: | + if docker container inspect netxms-e2e-smoke > /dev/null 2>&1; then + docker rm --force --volumes netxms-e2e-smoke + fi + - name: Clean up pin file if: matrix.component != 'web' && always() run: | @@ -118,7 +226,7 @@ jobs: NETXMS_VERSION=${{ steps.version.outputs.netxms_version }} POSTGRES_DB=netxms POSTGRES_USER=netxms - POSTGRES_PASSWORD=test_password + POSTGRES_PASSWORD=netxms EOF # Use locally built images for testing @@ -151,7 +259,7 @@ jobs: create-release: runs-on: ubuntu-latest needs: build - if: startsWith(github.ref, 'refs/tags/release-') + if: github.event_name != 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/release-') steps: - name: Checkout repository diff --git a/CLAUDE.md b/CLAUDE.md index b50a6da..faa1ae3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,11 +6,12 @@ Important: do not write comments. Do not explain what are you doing. Do NOT comm ## Project Overview -This repository contains Docker containerization for NetXMS (Network Management System), providing three main components: +This repository contains Docker containerization for NetXMS (Network Management System), providing four main components: - **Server**: NetXMS monitoring server with PostgreSQL database support - **Agent**: NetXMS monitoring agent for collecting system metrics - **Web**: Jetty-based web interface for NetXMS management console +- **Server E2E**: Disposable SQLite test server with seeded data, WebAPI, and a public fixture API key ## Build Commands @@ -38,7 +39,7 @@ make clean-pin ### Component Structure -- Each component (agent, server, web) has its own Dockerfile and supporting files +- Each component (agent, server, server-e2e, web) has its own Dockerfile and supporting files - All containers use Debian 12 base (except web which uses Jetty Alpine) - NetXMS packages are pinned to specific versions via `pin-package-version` script - Version configuration is centralized in `build.properties` diff --git a/README.md b/README.md index eef6f6c..4ec8346 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,16 @@ Images are also tagged with specific NetXMS versions (e.g., `5.2.3`). Note: **NEVER** use `:latest` in the production, always pin specific release. +## Server E2E Fixture + +The `server-e2e` image bundles a disposable SQLite database with seeded test data and the WebAPI enabled on port 8000. It runs on `linux/amd64`. + +The following API key is public and intended only for tests against this fixture. It authenticates as the fixture's `admin` user and expires on 2038-01-01 14:35:32 UTC. + +```sh +NX_API_KEY=dsly7ba3yqntvcsj6tkgi53ie4zaez6n +``` + ## Deployment ### Environment Setup diff --git a/server-e2e/Dockerfile b/server-e2e/Dockerfile index 7cafb9e..39f9ca6 100644 --- a/server-e2e/Dockerfile +++ b/server-e2e/Dockerfile @@ -1,6 +1,6 @@ FROM debian:13-slim -RUN apt-get update && apt-get install -y ca-certificates +RUN apt-get update && apt-get install -y ca-certificates curl COPY --chown=root:root files/netxms.list /etc/apt/sources.list.d/ COPY --chown=root:root files/netxms-keyring.gpg /etc/apt/trusted.gpg.d/ @@ -19,4 +19,15 @@ EXPOSE 8000/tcp COPY files/db.sqlite3 /tmp/db.sqlite3 COPY files/netxmsd.conf /etc/netxmsd.conf +RUN set -eu; \ + nxdbmgr -c /etc/netxmsd.conf upgrade; \ + upgrade_output="$(nxdbmgr -c /etc/netxmsd.conf upgrade 2>&1)"; \ + printf '%s\n' "$upgrade_output"; \ + printf '%s\n' "$upgrade_output" | grep -Fx 'Core database schema is up to date'; \ + printf '%s\n' "$upgrade_output" | grep -Fx 'Database upgrade succeeded'; \ + nxdbmgr -c /etc/netxmsd.conf background-upgrade; \ + background_output="$(nxdbmgr -c /etc/netxmsd.conf background-upgrade 2>&1)"; \ + printf '%s\n' "$background_output"; \ + printf '%s\n' "$background_output" | grep -Fx 'No pending background upgrades' + ENTRYPOINT [ "/usr/bin/netxmsd", "-c", "/etc/netxmsd.conf", "-q"] diff --git a/server-e2e/files/db.sqlite3 b/server-e2e/files/db.sqlite3 index 4b6072e..a1ecf9e 100644 Binary files a/server-e2e/files/db.sqlite3 and b/server-e2e/files/db.sqlite3 differ