diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..bde490f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "docker" # See documentation for possible values + directory: "/" # Location of package manifests + target-branch: "main" + schedule: + interval: "daily" + assignees: + - "fredclausen" + + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + assignees: + - "fredclausen" diff --git a/.github/workflows/cancel_dupes.yml b/.github/workflows/cancel_dupes.yml new file mode 100644 index 0000000..daa8c3e --- /dev/null +++ b/.github/workflows/cancel_dupes.yml @@ -0,0 +1,20 @@ +name: Cancelling Duplicates +on: + workflow_run: + workflows: + - "Deploy to Docker Hub" + - "Check Linting" + - "Tests" + types: ["requested"] + +jobs: + cancel-duplicate-workflow-runs: + name: "Cancel duplicate workflow runs" + runs-on: ubuntu-18.04 + steps: + - uses: potiuk/cancel-workflow-runs@master + name: "Cancel duplicate workflow runs" + with: + cancelMode: allDuplicates + token: ${{ secrets.GITHUB_TOKEN }} + sourceRunId: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..f0321fc --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,113 @@ +--- +name: Deploy + +on: + workflow_dispatch: + inputs: + reason: + required: false + description: "Reason for running this workflow" + use_test_image: + required: false + type: boolean + description: "Use base image testpr" + default: false + + push: + branches: + - main + # Don't trigger if it's just a documentation update + paths-ignore: + - "**.md" + - "**.MD" + - "**.yml" + - "LICENSE" + - ".gitattributes" + - ".gitignore" + - ".dockerignore" + +# Set workflow-wide environment variables +# - REPO: repo name on dockerhub +# - IMAGE: image name on dockerhub +env: + # DOCKERHUB_REPO: sdr-enthusiasts + # DOCKERHUB_IMAGE: vrs + GHCR_IMAGE: sdr-enthusiasts/docker-radar1090 + GHCR_REGISTRY: ghcr.io + GH_LABEL: main + GHCR_TAG: latest + +jobs: + workflow-dispatch: + name: Triggered via Workflow Dispatch? + # only run this step if workflow dispatch triggered + # log the reason the workflow dispatch was triggered + if: | + github.event_name == 'workflow_dispatch' && + github.event.inputs.reason != '' + runs-on: ubuntu-latest + steps: + - name: Log dispatch reason + env: + INPUTS_REASON: ${{ github.event.inputs.reason }} + INPUTS_USE_TEST_IMAGE: ${{ github.event.inputs.use_test_image }} + run: | + echo "Workflow dispatch reason: $INPUTS_REASON" + echo "Use test image: $INPUTS_USE_TEST_IMAGE" + + hadolint: + name: Run hadolint against docker files + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Pull hadolint/hadolint:latest Image + run: docker pull hadolint/hadolint:latest + - name: Run hadolint against Dockerfiles + run: docker run --rm -i -v "$PWD":/workdir --workdir /workdir --entrypoint hadolint hadolint/hadolint --ignore DL3015 --ignore DL3003 --ignore DL3006 --ignore DL3010 --ignore DL4001 --ignore DL3007 --ignore DL3008 --ignore SC2068 --ignore DL3007 --ignore SC1091 --ignore DL3013 --ignore DL3010 $(find . -type f -iname "Dockerfile*") + + deploy: + name: Deploy without telegraf + uses: sdr-enthusiasts/common-github-workflows/.github/workflows/build_and_push_image.yml@main + with: + push_enabled: true + push_destinations: ghcr.io + ghcr_repo_owner: ${{ github.repository_owner }} + ghcr_repo: ${{ github.repository }} + get_version_method: file_in_container:file=/CONTAINER_VERSION + # set build_latest to true if github.event.inputs.use_test_image is false + build_latest: ${{ github.event.inputs.use_test_image == 'false' || github.event.inputs.use_test_image == '' }} + build_baseimage_test: ${{ github.event.inputs.use_test_image == 'true' }} + # only build the entire stack if we are not using the test image + build_version_specific: false + build_platform_specific: false + build_nohealthcheck: false + build_baseimage_url: docker-tar1090:latest/docker-tar1090:baseimage-test + secrets: + ghcr_token: ${{ secrets.GITHUB_TOKEN }} + + # unfortunately we can't use build_and_push_image.yml to build the telegraf label because + # that GH Action doesn't have the capability to build specific custom-named labels + + deploy_with_telegraf: + name: Deploy with telegraf and healthcheck + uses: sdr-enthusiasts/common-github-workflows/.github/workflows/build_and_push_image.yml@main + with: + push_enabled: true + push_destinations: ghcr.io + ghcr_repo_owner: ${{ github.repository_owner }} + ghcr_repo: ${{ github.repository }} + get_version_method: file_in_container:file=/CONTAINER_VERSION + # set build_latest to true if github.event.inputs.use_test_image is false + build_latest: ${{ github.event.inputs.use_test_image == 'false' || github.event.inputs.use_test_image == '' }} + build_baseimage_test: ${{ github.event.inputs.use_test_image == 'true' }} + build_baseimage_url: docker-tar1090:telegraf/docker-tar1090:telegraf-baseimage-test + # only build the entire stack if we are not using the test image + build_version_specific: false + build_platform_specific: false + build_nohealthcheck: false + docker_latest_tag: telegraf + dockerfile_changes: | + docker-tar1090:latest/docker-tar1090:telegraf + + secrets: + ghcr_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pre-commit-updates.yaml b/.github/workflows/pre-commit-updates.yaml new file mode 100644 index 0000000..f075972 --- /dev/null +++ b/.github/workflows/pre-commit-updates.yaml @@ -0,0 +1,23 @@ +name: Update pre-commit hooks + +on: + workflow_dispatch: + schedule: + - cron: 0 0 * * * + +jobs: + update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.0 + with: + fetch-depth: 0 + - uses: vrslev/pre-commit-autoupdate@v1.0.0 + - uses: peter-evans/create-pull-request@v5 + with: + branch: pre-commit-autoupdate + title: "chore(deps): Update pre-commit hooks" + commit-message: "chore(deps): Update pre-commit hooks" + body: Update pre-commit hooks + labels: dependencies + delete-branch: True diff --git a/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/watchdog b/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/watchdog new file mode 100644 index 0000000..e69de29 diff --git a/rootfs/etc/s6-overlay/s6-rc.d/watchdog/dependencies.d/radar1090 b/rootfs/etc/s6-overlay/s6-rc.d/watchdog/dependencies.d/radar1090 new file mode 100644 index 0000000..e69de29 diff --git a/rootfs/etc/s6-overlay/s6-rc.d/watchdog/run b/rootfs/etc/s6-overlay/s6-rc.d/watchdog/run new file mode 100755 index 0000000..64a10d7 --- /dev/null +++ b/rootfs/etc/s6-overlay/s6-rc.d/watchdog/run @@ -0,0 +1,2 @@ +#!/bin/sh +exec /etc/s6-overlay/scripts/watchdog diff --git a/rootfs/etc/s6-overlay/s6-rc.d/watchdog/type b/rootfs/etc/s6-overlay/s6-rc.d/watchdog/type new file mode 100644 index 0000000..5883cff --- /dev/null +++ b/rootfs/etc/s6-overlay/s6-rc.d/watchdog/type @@ -0,0 +1 @@ +longrun diff --git a/rootfs/etc/s6-overlay/scripts/watchdog b/rootfs/etc/s6-overlay/scripts/watchdog new file mode 100755 index 0000000..f23308c --- /dev/null +++ b/rootfs/etc/s6-overlay/scripts/watchdog @@ -0,0 +1,105 @@ +#!/command/with-contenv bash +# shellcheck shell=bash disable=SC1091,SC2015,SC2164,SC2068,SC2145,SC2120 + +source /scripts/common +s6wrap=(s6wrap --quiet --timestamps --prepend="$(basename "$0")") + +#--------------------------------------------------------------------------------------------- +# This repository, docker container, and accompanying scripts and documentation is +# Copyright (C) 2022-2023, Ramon F. Kolb (kx1t) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Radar1090 is an ADS-B RADAR Feed Service +# Copyright (C) 2023 by Michael J. Tubby B.Sc. MIET G8TIC mik@tubby.org All Rights Reserved. +# No license to the "radar" binary and its source code is implied; contact the author for information. +#--------------------------------------------------------------------------------------------- + +# Watchdog to ensure there's traffic flowing for radar1090 +# Uses TCPDUMP to ensure there are packets sent to the destination port + +"${s6wrap[@]}" echo "Started as an s6 service" + +MEASURE_TIME="${MEASURE_TIME:-15}" # how long we take samples to check data is flowing +MEASURE_INTERVAL="${MEASURE_INTERVAL:-300}" # wait time between check runs +TRANSPORT_PROTOCOL="${TRANSPORT_PROTOCOL:-udp}" # [udp|tcp] the protocol used to transport data to the remote aggregator +TRANSPORT_PROTOCOL="${TRANSPORT_PROTOCOL,,}" + +HEALTHFILE=/run/watchdog-log/health_failures_since_last_success + +# make sure the log files exists: +mkdir -p "$(dirname "$HEALTHFILE")" +echo "0" > "$HEALTHFILE" + +monitor_feeder () { + # see if data is flowing from this container to the remote aggregator + # usage: monitor_feeder [measure_time] + # where measure_time is optional; if omitted, $MEASURE_TIME or 10 secs will be used + # $RADARSERVER and $RADARPORT are used to indicate the aggregator's name and port, defaulting to adsb-in.1090mhz.uk and 2227 + # Function returns 0 (true) if successful and 1 (false) if the host cannot be reached + local measure_time + local result + local resultcode + + chk_enabled "$VERBOSE" && "${s6wrap[@]}" --args echo monitoring "${RADARSERVER:-adsb-in.1090mhz.uk}:${RADARPORT:-2227}/${TRANSPORT_PROTOCOL:-udp} for ${measure_time:-10} secs" || true + + measure_time="${1:-${MEASURE_TIME:-10}}" + result="$(grep captured <<< "$(timeout --preserve-status "${measure_time}" tcpdump -p dst "${RADARSERVER:-adsb-in.1090mhz.uk}" and "${TRANSPORT_PROTOCOL:-udp}" port "${RADARPORT:-2227}" 2>/dev/stdout 1>/dev/null)" | awk '{print $1}')" + resultcode="$?" + + if (( resultcode != 0 )); then + return $resultcode + elif [[ "$result" == "0" ]]; then + return 1 + else + return 0 + fi +} + +fix_feeder () { # bring down the feeder Python app: + pkill -SIGTERM -f "/usr/sbin/radar" + sleep 10 + + # check if data is flowing again: + monitor_feeder + return $? +} + +while : +do + # first sleep a bit + sleep "$MEASURE_INTERVAL" & wait ! + + # then check if data is flowing + if ! monitor_feeder; then + "${s6wrap[@]}" --args echo "WARNING: DataFlow Failure: No data is flowing to ${RADARSERVER:-adsb-in.1090mhz.uk}:${RADARPORT:-2227}/${TRANSPORT_PROTOCOL:-udp} after checking for ${measure_time:-10} secs. Feeder will be restarted" + if ! fix_feeder + then + "${s6wrap[@]}" --args echo "FATAL: DataFlow Failure: Restarting feeder module didn't restart the data flow. Please check your system! We will try again in $MEASURE_INTERVAL secs" + read -r healthfailures < "$HEALTHFILE" + (( healthfailures++ )) || true + echo "$healthfailures" > "$HEALTHFILE" + else + "${s6wrap[@]}" --args echo "SUCCESS: Feeder restart made data flow again!" + echo "0" > "$HEALTHFILE" + fi + + else + if chk_enabled "$VERBOSE"; then + "${s6wrap[@]}" --args echo "SUCCESS: Data is flowing to ${RADARSERVER:-adsb-in.1090mhz.uk}:${RADARPORT:-2227}/${TRANSPORT_PROTOCOL:-udp} after checking for ${measure_time:-10} secs" + fi + echo "0" > "$HEALTHFILE" + + fi + +done diff --git a/rootfs/scripts/healthcheck.sh b/rootfs/scripts/healthcheck.sh new file mode 100644 index 0000000..59a2430 --- /dev/null +++ b/rootfs/scripts/healthcheck.sh @@ -0,0 +1,51 @@ +#!/command/with-contenv bash +# shellcheck shell=bash disable=SC1091 +# SC2015,SC2164,SC2068,SC2145,SC2120 + +source /scripts/common +s6wrap=(s6wrap --quiet --timestamps --prepend="$(basename "$0")") + +#--------------------------------------------------------------------------------------------- +# This repository, docker container, and accompanying scripts and documentation is +# Copyright (C) 2022-2023, Ramon F. Kolb (kx1t) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Radar1090 is an ADS-B RADAR Feed Service +# Copyright (C) 2023 by Michael J. Tubby B.Sc. MIET G8TIC mik@tubby.org All Rights Reserved. +# No license to the "radar" binary and its source code is implied; contact the author for information. +#--------------------------------------------------------------------------------------------- + +# Healthcheck for autoheal. Returns 1 if $HEALTHFILE contains a number greater than 0 +# This number is written by the watchdog service to ensure there's traffic flowing for radar1090 + +HEALTHFILE=/run/watchdog-log/health_failures_since_last_success +MEASURE_TIME="${MEASURE_TIME:-15}" # how long we take samples to check data is flowing +MEASURE_INTERVAL="${MEASURE_INTERVAL:-300}" # wait time between check runs +TRANSPORT_PROTOCOL="${TRANSPORT_PROTOCOL:-udp}" # [udp|tcp] the protocol used to transport data to the remote aggregator +TRANSPORT_PROTOCOL="${TRANSPORT_PROTOCOL,,}" + +FAILURES_TO_GO_UNHEALTHY=3 # after this number of failures, the container will go unhealthy + +# make sure the log files exists: +mkdir -p "$(dirname "$HEALTHFILE")" +touch "$HEALTHFILE" + +read -r healthfailures < "$HEALTHFILE" + +if [[ -n "$healthfailures" ]] && (( healthfailures > FAILURES_TO_GO_UNHEALTHY )); then + "${s6wrap[@]}" --args echo "UNHEALTHY: No data is flowing to ${RADARSERVER:-adsb-in.1090mhz.uk}:${RADARPORT:-2227}/${TRANSPORT_PROTOCOL:-udp} - failure count since last successful measurement is $healthfailures" + exit 1 +else + exit 0 +fi \ No newline at end of file