Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
730c64b
chore(release): prepare v1.6.0
rdwr-egore Oct 14, 2026
3b1de15
fix: align watchdog notifications and preserve syslog alerts
rdwr-egore Oct 14, 2026
7afbc3d
test: record real v1.6 Docker smoke acceptance evidence
rdwr-egore Oct 14, 2026
0666a12
fix: require stable recovery after manual Docker restarts
rdwr-egore Oct 14, 2026
220d573
fix: isolate destructive release tests and clarify customer alerts
rdwr-egore Oct 15, 2026
136ce0a
ci: validate isolated smoke on release pull requests
rdwr-egore Oct 15, 2026
7077dfe
docs: anonymize lab evidence and simplify v1.6 operator guidance
rdwr-egore Oct 15, 2026
d01d7e9
feat: guided online-offline release installation and packaging
rdwr-egore Oct 15, 2026
329fe22
docs: restore complete deployment and operations guide
rdwr-egore Oct 15, 2026
810b881
release: add versioned installer bundles and maintainer workflow
rdwr-egore Oct 16, 2026
3ae5a0e
release: use single installer bundle and evergreen developer guide
rdwr-egore Oct 16, 2026
43b0d44
docs: organize customer and engineering guides and audit references
rdwr-egore Oct 16, 2026
ff86a05
folders reorg
rdwr-egore Oct 16, 2026
6b6a3e6
fix: align docs and release packaging with renamed folders
rdwr-egore Oct 16, 2026
3ac48f7
docs: group all notification channels under one reference
rdwr-egore Oct 16, 2026
8732fda
docs: add direct CLI release download for connected hosts
rdwr-egore Oct 16, 2026
cb5610b
docs: choose published release version before downloading
rdwr-egore Oct 16, 2026
673040c
v1.6.0
rdwr-egore Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
# cp .env.example .env
# chmod 600 .env # secrets — restrict to owner only
#
# Only credentials/secrets belong here.
# All other settings (hosts, ports, recipients, thresholds) go in watchdog-config.yaml.
# Credentials/secrets and installer-managed deployment values live here.
# Monitoring thresholds, channel hosts, ports, and recipients belong in watchdog-config.yaml.

# ── Slack ─────────────────────────────────────────────────────────────────────
# Required if alert_channels includes "slack"
Expand Down Expand Up @@ -48,8 +48,5 @@ SMTP_PASSWORD=your-smtp-password/api key value

# ── Image version ─────────────────────────────────────────────────────────────
# Release tag the container runs (see the repository VERSION file). install.sh
# sets this to the version it loaded from watchdog.tar; unset means watchdog:latest.
# sets this to the verified release image version loaded from packaged watchdog.tar.
# WATCHDOG_VERSION=

# ── Tuning ────────────────────────────────────────────────────────────────────
LOG_LEVEL=INFO
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
branches:
- main
- "release/**"

jobs:
validate:
Expand All @@ -17,10 +18,18 @@ jobs:
run: |
bash -n install.sh
bash -n uninstall.sh
bash -n tests/release-smoke.sh
bash -n scripts/package_release.sh

- name: Validate Python syntax
run: |
python3 -m py_compile watchdog.py
python3 - <<'PYCODE'
from pathlib import Path
script = Path("tests/release-smoke.sh").read_text()
code = script.split("<<'PY'", 1)[1].rsplit("\nPY", 1)[0]
compile(code, "release-smoke", "exec")
PYCODE

- name: Install test dependencies
run: |
Expand Down
86 changes: 86 additions & 0 deletions .github/workflows/release-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Publish Container Alert release

# Publish only a reviewed, tagged commit already merged into main.
on:
push:
tags:
- "v*.*.*"

permissions:
contents: write

jobs:
package:
runs-on: ubuntu-24.04
timeout-minutes: 30

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Verify tag, version, and main ancestry
shell: bash
run: |
set -euo pipefail
version="$(tr -d '[:space:]' < VERSION)"
test "$GITHUB_REF_NAME" = "v$version"
git fetch origin main:refs/remotes/origin/main
git merge-base --is-ancestor "$GITHUB_SHA" origin/main
echo "WATCHDOG_VERSION=$version" >> "$GITHUB_ENV"

- name: Validate release scripts and Python tests
shell: bash
run: |
set -euo pipefail
bash -n install.sh uninstall.sh
python3 -m pip install docker==7.1.0 requests==2.32.3 PyYAML==6.0.2
python3 -m unittest discover -s tests -q

- name: Build version-labelled release image
shell: bash
run: |
set -euo pipefail
docker build --build-arg "WATCHDOG_VERSION=$WATCHDOG_VERSION" \
-t "watchdog:$WATCHDOG_VERSION" .
image_version="$(docker image inspect "watchdog:$WATCHDOG_VERSION" \
--format '{{index .Config.Labels "org.opencontainers.image.version"}}')"
test "$image_version" = "$WATCHDOG_VERSION"
docker run --rm --network none --entrypoint python3 \
"watchdog:$WATCHDOG_VERSION" -c \
"import docker, requests, yaml, pysnmp, cryptography, watchdog, lifecycle, ha_monitor"

- name: Assemble and validate single customer package
shell: bash
run: |
set -euo pipefail
docker save "watchdog:$WATCHDOG_VERSION" -o watchdog.tar
bash scripts/package_release.sh watchdog.tar dist
(cd dist && sha256sum --check SHA256SUMS)
package="dist/container-alert-v$WATCHDOG_VERSION.tar.gz"
tar -tzf "$package" | grep -qx 'CyberController-Container-Alert/watchdog.tar'
tar -tzf "$package" | grep -qx 'CyberController-Container-Alert/SHA256SUMS'
tar -tzf "$package" | grep -qx 'CyberController-Container-Alert/install.sh'

- name: Publish GitHub Release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
notes=(--generate-notes)
if [ -f "docs/operations/RELEASE_NOTES_V$WATCHDOG_VERSION.md" ]; then
notes=(--notes-file "docs/operations/RELEASE_NOTES_V$WATCHDOG_VERSION.md")
fi

# Check both assets before making this public release visible.
gh release create "$GITHUB_REF_NAME" \
dist/SHA256SUMS "dist/container-alert-v$WATCHDOG_VERSION.tar.gz" \
--verify-tag --draft --title "Container Alert $GITHUB_REF_NAME" \
"${notes[@]}"

expected=$(printf '%s\n' SHA256SUMS \
"container-alert-v$WATCHDOG_VERSION.tar.gz" | sort)
actual=$(gh release view "$GITHUB_REF_NAME" --json assets --jq '.assets[].name' | sort)
test "$actual" = "$expected" || { echo "Release assets incomplete" >&2; exit 1; }
gh release edit "$GITHUB_REF_NAME" --draft=false
105 changes: 105 additions & 0 deletions .github/workflows/release-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Release smoke (isolated)

# Runs only on disposable GitHub-hosted workers; never on operational engines.
# PR trigger permits validation before release; manual reruns after first merge.
on:
pull_request:
branches:
- main
paths:
- "watchdog.py"
- "lifecycle.py"
- "ha_monitor.py"
- "install.sh"
- "VERSION"
- "docker-compose.yaml"
- "docker-compose.build.yaml"
- "tests/release-smoke.sh"
- ".github/workflows/release-smoke.yml"
- ".github/workflows/release-package.yml"
- "scripts/package_release.sh"
- "Dockerfile"
workflow_dispatch:

permissions:
contents: read

jobs:
real-docker:
name: Real Docker (disposable GitHub runner)
runs-on: ubuntu-24.04
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
timeout-minutes: 20

steps:
- uses: actions/checkout@v4

- name: Build release candidate on disposable runner
run: docker build --pull=false --build-arg "WATCHDOG_VERSION=$(cat VERSION)" --tag "watchdog:$(cat VERSION)" .

- name: Create empty, labelled isolated Docker Engine
shell: bash
run: |
set -euo pipefail
marker="ci-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
echo "SMOKE_ENGINE_MARKER=${marker}" >> "${GITHUB_ENV}"
docker run -d --name ca-smoke-engine --restart no \
--privileged --cpus 2 --memory 2g --pids-limit 768 \
--network bridge -p 127.0.0.1::2375 \
-e DOCKER_TLS_CERTDIR="" \
docker:27.5-dind \
--storage-driver=vfs --iptables=false --ip6tables=false \
--bridge=none \
--label "com.radware.container-alert.smoke-engine=${marker}"
port="$(docker port ca-smoke-engine 2375/tcp | tail -1 | awk -F: '{print $NF}')"
test -n "${port}"
echo "DOCKER_HOST=tcp://127.0.0.1:${port}" >> "${GITHUB_ENV}"
for i in $(seq 1 40); do
if DOCKER_HOST="tcp://127.0.0.1:${port}" docker info >/dev/null 2>&1; then
echo "SMOKE_DOCKER_SOCKET_GID=$(docker exec ca-smoke-engine stat -c '%g' /var/run/docker.sock)" >> "${GITHUB_ENV}"
echo "Disposable Engine ready."
exit 0
fi
sleep 2
done
echo "Disposable Engine failed to start" >&2
exit 1

- name: Load exact built image into isolated Engine
shell: bash
run: |
set -euo pipefail
DOCKER_HOST=unix:///var/run/docker.sock docker save "watchdog:$(cat VERSION)" | DOCKER_HOST="${DOCKER_HOST}" docker load
DOCKER_HOST="${DOCKER_HOST}" docker image inspect "watchdog:$(cat VERSION)" >/dev/null

- name: Real Docker acceptance (15 scenarios, no operational access)
shell: bash
env:
SMOKE_RUNNER_KIND: ${{ runner.environment }}
run: |
set -euo pipefail
SMOKE_IMAGE="watchdog:$(cat VERSION)" bash tests/release-smoke.sh

- name: Real OOM then external restart (full 120-second recovery)
shell: bash
env:
SMOKE_RUNNER_KIND: ${{ runner.environment }}
SMOKE_ONLY: T15_EXTERNAL_OOM
run: |
set -euo pipefail
SMOKE_IMAGE="watchdog:$(cat VERSION)" bash tests/release-smoke.sh

- name: Upload test reports (test-only synthetic data)
if: always()
uses: actions/upload-artifact@v4
with:
name: container-alert-release-smoke-${{ github.run_id }}-${{ github.run_attempt }}
path: /tmp/container-alert-smoke-*/
if-no-files-found: warn
retention-days: 30

- name: Remove disposable test Engine
if: always()
shell: bash
run: |
DOCKER_HOST=unix:///var/run/docker.sock docker rm -f ca-smoke-engine 2>/dev/null || true
Loading
Loading