diff --git a/.dockerignore b/.dockerignore index 4d35d8dfbe..1598c6af21 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,6 +7,7 @@ !README.md !setup.py !requirements.txt +!requirements-full.txt # ignore on every level **/__pycache__/ diff --git a/docker/Dockerfile.offline b/docker/Dockerfile.offline new file mode 100644 index 0000000000..b9f0334f31 --- /dev/null +++ b/docker/Dockerfile.offline @@ -0,0 +1,63 @@ +FROM python:3.11-slim AS build + +ARG TARGETPLATFORM +ARG BUILDPLATFORM + +COPY ./thingsboard_gateway /thingsboard_gateway +COPY . . + +ENV PATH="/root/.cargo/bin:/root/.local/bin:$PATH" \ + PYTHONPATH="." \ + configs="/thingsboard_gateway/config" \ + extensions="/thingsboard_gateway/extensions" \ + logs="/thingsboard_gateway/logs" + +# Installs ALL connector dependencies (requirements-full.txt) at build time so +# that the image is fully self-contained and requires no internet access at runtime. +# TBUtility.install_package() calls inside connectors will be no-ops because the +# packages are already present when the connector module is imported. +RUN mkdir -p /default-config/config /default-config/extensions/ && \ + cp -r /thingsboard_gateway/config/* /default-config/config/ && \ + cp -r /thingsboard_gateway/extensions/* /default-config/extensions && \ + echo "Running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + gcc python3-dev build-essential libssl-dev libffi-dev zlib1g-dev \ + python3-grpcio curl pkg-config libssl-dev \ + unixodbc-dev cmake && \ + case "$TARGETPLATFORM" in \ + "linux/amd64") DEFAULT_HOST="x86_64-unknown-linux-gnu";; \ + "linux/386") DEFAULT_HOST="i686-unknown-linux-gnu";; \ + "linux/arm64") DEFAULT_HOST="aarch64-unknown-linux-gnu";; \ + "linux/arm/v7") DEFAULT_HOST="armv7-unknown-linux-gnueabihf";; \ + *) \ + echo "Unsupported platform detected. Falling back to x86_64."; \ + DEFAULT_HOST="x86_64-unknown-linux-gnu";; \ + esac && \ + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host=$DEFAULT_HOST --profile minimal && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* && \ + echo '#!/bin/sh\n\ +# Main start script\n\ +CONF_FOLDER="/thingsboard_gateway/config"\n\ +FIRSTLAUNCH="${CONF_FOLDER}/.firstlaunch"\n\ +if [ ! -f "$FIRSTLAUNCH" ]; then\n\ + cp -r /default-config/config/* /thingsboard_gateway/config/\n\ + cp -r /default-config/extensions/* /thingsboard_gateway/extensions/\n\ + touch $FIRSTLAUNCH\n\ + echo "#Remove this file only if you want to recreate default config files! This will overwrite existing files" > $FIRSTLAUNCH\n\ +fi\n\ +python /thingsboard_gateway/tb_gateway.py' > /start-gateway.sh && chmod +x /start-gateway.sh && \ + python3 -m pip install --no-cache-dir --upgrade pip setuptools wheel && \ + python3 -m pip install --no-cache-dir cryptography && \ + python3 -m pip install --no-cache-dir -r requirements-full.txt && \ + (rustup self uninstall -y || { \ + echo "rustup uninstall failed, removing manually..."; \ + rm -rf /root/.rustup /root/.cargo; \ + }) && \ + apt-get remove --purge -y gcc python3-dev build-essential libssl-dev libffi-dev zlib1g-dev pkg-config unixodbc-dev && \ + apt-get autoremove -y + +VOLUME ["${configs}", "${extensions}", "${logs}"] + +CMD [ "/bin/sh", "/start-gateway.sh" ] diff --git a/docker_build_multiarch.sh b/docker_build_multiarch.sh index 42c62b0b9b..b1e93e5c77 100755 --- a/docker_build_multiarch.sh +++ b/docker_build_multiarch.sh @@ -23,9 +23,14 @@ set -e # Supported operations: # 1. Default: Build for current platform and load into local Docker # 2. --push: Build for one or more platforms and push to remote registry -# 3. --platform : Manually specify platform(s) (e.g. linux/amd64,linux/arm64) +# 3. --offline: Build with all connector dependencies pre-installed (no internet +# required at runtime). Uses docker/Dockerfile.offline which installs +# requirements-full.txt instead of requirements.txt. +# 4. --save: Build for current platform and export as a tar.gz file +# (for deployment in air-gapped / offline environments) +# 5. --platform : Manually specify platform(s) (e.g. linux/amd64,linux/arm64) # If omitted, supported platforms are auto-detected. -# 4. --help: Show this help +# 6. --help: Show this help # # Usage Examples: # ./docker_build_multiarch.sh @@ -37,9 +42,22 @@ set -e # ./docker_build_multiarch.sh --push -r myregistry/tb-gateway --platform linux/amd64,linux/arm64 # → Push multi-arch image to registry with manually specified platforms # +# ./docker_build_multiarch.sh --offline +# → Build self-contained offline image (all connector deps included) for current platform +# +# ./docker_build_multiarch.sh --offline --save +# → Build offline image and export as tar.gz for air-gapped deployment +# +# ./docker_build_multiarch.sh --offline --push -r myregistry/tb-gateway +# → Build and push offline image to registry +# +# ./docker_build_multiarch.sh --save +# → Build online image and export as tar.gz +# # Notes: # -r or --repository is required when using --push # --platform is optional and overrides auto-detection when provided +# --save and --push are mutually exclusive ############################################################################### # ─── Configurable Defaults ──────────────────────────────────────────────── @@ -51,6 +69,8 @@ BUILDER_NAME="multiarch-builder" # ─── Argument Parsing ───────────────────────────────────────────────────── PUSH=false +SAVE=false +OFFLINE=false REPOSITORY="" while [[ $# -gt 0 ]]; do @@ -71,6 +91,14 @@ while [[ $# -gt 0 ]]; do REPOSITORY="$2" shift 2 ;; + --save) + SAVE=true + shift + ;; + --offline) + OFFLINE=true + shift + ;; -h|--help) echo "Usage: ./docker_build_multiarch.sh [OPTIONS]" echo "" @@ -80,12 +108,18 @@ while [[ $# -gt 0 ]]; do echo " --push Push the image to a registry (requires -r)" echo " -r, --repository REPO Target repository (e.g. myrepo/tb-gateway)" echo " --platform PLATFORMS Manually set platforms (comma-separated)" + echo " --offline Build self-contained image with all connector dependencies pre-installed" + echo " (uses Dockerfile.offline + requirements-full.txt, no internet needed at runtime)" + echo " --save Build and export image as a tar.gz file for air-gapped deployment" echo " -h, --help Show this help message" echo "" echo "Examples:" echo " ./docker_build_multiarch.sh" echo " ./docker_build_multiarch.sh --push -r myrepo/tb-gateway" echo " ./docker_build_multiarch.sh --push -r myrepo/tb-gateway --platform linux/amd64,linux/arm64" + echo " ./docker_build_multiarch.sh --offline" + echo " ./docker_build_multiarch.sh --offline --save" + echo " ./docker_build_multiarch.sh --offline --push -r myrepo/tb-gateway" exit 0 ;; *) @@ -101,6 +135,19 @@ if [[ "$PUSH" == true && -z "$REPOSITORY" ]]; then exit 1 fi +if [[ "$PUSH" == true && "$SAVE" == true ]]; then + echo "[X] Error: --push and --save are mutually exclusive." + exit 1 +fi + +if [[ "$OFFLINE" == true ]]; then + DOCKERFILE_PATH="docker/Dockerfile.offline" + echo "[*] Offline mode: using $DOCKERFILE_PATH (requirements-full.txt, all connector deps pre-installed)" +fi + +# ─── Version Detection ──────────────────────────────────────────────────── +VERSION=$(grep -Po 'VERSION[ ,]=[ ,]"\K(([0-9])+(\.){0,1})+' thingsboard_gateway/version.py 2>/dev/null || echo "latest") + IMAGE_TAG="${REPOSITORY:-$DEFAULT_IMAGE}:${TAG}" # ─── Setup Buildx ───────────────────────────────────────────────────────── @@ -117,7 +164,7 @@ echo "[*] Registering QEMU emulation..." docker run --rm --privileged tonistiigi/binfmt --install all # ─── Detect Supported Platforms If Not Set ───────────────────────────────── -if [[ -z "$PLATFORMS" ]]; then +if [[ -z "${PLATFORMS:-}" ]]; then echo "[*] Parsing base image from Dockerfile..." BASE_IMAGE=$(awk '/^FROM/ { for (i=1; i<=NF; i++) if ($i !~ /FROM|--platform=|\$TARGETPLATFORM|AS/) print $i }' "$DOCKERFILE_PATH" | head -n1) @@ -159,6 +206,42 @@ if [[ "$PUSH" == true ]]; then --tag "$IMAGE_TAG" \ --build-arg BUILDKIT_INLINE_CACHE=1 \ --push "$CONTEXT" + +elif [[ "$SAVE" == true ]]; then + # --save: build for a single platform and export as tar.gz for air-gapped deployment + SAVE_PLATFORM="${PLATFORMS%%,*}" # use only the first platform if multiple specified + if [[ "$PLATFORMS" == *","* ]]; then + echo "[!] --save supports a single platform. Using: $SAVE_PLATFORM" + echo "[!] To export multiple platforms separately, run --save once per platform." + fi + + ARCH="${SAVE_PLATFORM//\//_}" # e.g. linux/amd64 → linux_amd64 + OFFLINE_SUFFIX="" + [[ "$OFFLINE" == true ]] && OFFLINE_SUFFIX="-offline" + OUTPUT_FILE="tb-gateway-${VERSION}${OFFLINE_SUFFIX}-${ARCH}.tar.gz" + + echo "[*] Building image for: $SAVE_PLATFORM" + docker buildx build \ + --platform "$SAVE_PLATFORM" \ + --file "$DOCKERFILE_PATH" \ + --tag "$IMAGE_TAG" \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + --load "$CONTEXT" + + echo "[*] Exporting image to: $OUTPUT_FILE" + docker save "$IMAGE_TAG" | gzip > "$OUTPUT_FILE" + + echo "[✓] Build completed!" + echo " Image Tag : $IMAGE_TAG" + echo " Platform : $SAVE_PLATFORM" + echo " Output file : $OUTPUT_FILE" + echo "" + echo "To deploy in an air-gapped environment:" + echo " 1. Transfer $OUTPUT_FILE to the target host" + echo " 2. docker load < $OUTPUT_FILE" + echo " 3. docker compose up -d (update docker-compose.yml image: field to $IMAGE_TAG)" + exit 0 + else CURRENT_PLATFORM="$(docker version -f '{{.Server.Os}}')/$(docker version -f '{{.Server.Arch}}')" echo "[*] Building and loading image for current platform: $CURRENT_PLATFORM"