From 5029e46359f3e34a7220c3c94163d84d56988b4d Mon Sep 17 00:00:00 2001 From: DasMoorhuhn Date: Sat, 29 Jun 2024 03:32:54 +0200 Subject: [PATCH] multiplatform build --- .gitignore | 4 +- Dockerfile | 38 +++++++--- build_docker.sh | 35 +++++++++ build_docker_multi_platforn.sh | 10 +++ python/docker_entrypoint.sh | 8 +- python/requierements.txt | 1 - python/src/discovery.py | 2 +- python/src/load_env.py | 16 ++++ python/src/loop.py | 5 +- python/src/main.py | 22 +++++- run_docker.sh | 132 ++++++++++++++++++++++++++++----- 11 files changed, 235 insertions(+), 38 deletions(-) create mode 100644 build_docker.sh create mode 100644 build_docker_multi_platforn.sh create mode 100644 python/src/load_env.py diff --git a/.gitignore b/.gitignore index 48b6f47..73308d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ __pycache__ devices.yml history.* -data/ \ No newline at end of file +data/ +*.iso +*.cow \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 2803780..3fb8851 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,7 @@ -FROM python:3.12-alpine3.20 AS build_bluepy +# This is the build stage for getting all python libs. Mainly it's for reducing the final image size because for building and installing the pips, many tools are needed which are just bloat to the final image. +FROM python:3.12-alpine3.20 AS pip_build_stage + +COPY ./python/requierements.txt / RUN apk add \ make \ @@ -8,17 +11,32 @@ RUN apk add \ build-base \ freetype-dev \ libpng-dev \ - openblas-dev + openblas-dev \ + libxml2-dev \ + libxslt-dev +# BluePy needs to be build here... idk why but pip install fails on alpine somehow RUN git clone https://github.com/IanHarvey/bluepy.git && \ cd bluepy && \ python3.12 setup.py build && \ - python3.12 setup.py install + python3.12 setup.py install && \ + cd / +# Normal pip install for pyyaml failed on RPI4, so I build it here +RUN git clone https://github.com/yaml/pyyaml.git && \ + cd pyyaml && \ + python3.12 setup.py build && \ + python3.12 setup.py install && \ + cd / + +RUN pip3.12 install -r requierements.txt + + +# This is the stage for the final image FROM python:3.12-alpine3.20 + WORKDIR /src COPY ./python/src/ . -COPY ./python/requierements.txt . COPY ./python/docker_entrypoint.sh / RUN mkdir data RUN touch DOCKER @@ -26,12 +44,10 @@ VOLUME /src/data RUN apk add --no-cache sudo bluez tzdata ENV TZ=Europe/Berlin +ENV DOCKER=TRUE -# Copy bluepy from the bluepy build stage -COPY --from=build_bluepy /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages -COPY --from=build_bluepy /usr/local/bin /usr/local/bin +# Copy pips from the pip build stage +COPY --from=pip_build_stage /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY --from=pip_build_stage /usr/local/bin /usr/local/bin -RUN pip3.12 install -r requierements.txt && rm requierements.txt -# RUN echo '@reboot root python3.12 /src/serve_json.py' >> /etc/crontab - -ENTRYPOINT sh /docker_entrypoint.sh \ No newline at end of file +ENTRYPOINT sh /docker_entrypoint.sh diff --git a/build_docker.sh b/build_docker.sh new file mode 100644 index 0000000..f06bd74 --- /dev/null +++ b/build_docker.sh @@ -0,0 +1,35 @@ +ARCH=linux/arm/v6 +#ARCH=linux/amd64 +TAG=develop +HELP="USAGE: sh build_docker.sh \n +[ -a | --architecture ] Select a architecture. Default is auto\n +[ -t | --tag ] Set a docker tag. Default is develop \n +[ -h | --help ] Get this dialog" + +docker_build(){ + docker build --build-arg TARGETPLATFORM=$ARCH --platform $ARCH --tag dasmoorhuhn/atc-mithermometer-gateway:$TAG . +} + +while [ "$1" != "" ]; do + case $1 in + -a | --architecture ) + shift + ARCH=$1 + shift + ;; + -t | --tag ) + shift + TAG=$1 + shift + ;; + -h | --help ) + echo $HELP + exit + ;; + * ) + echo $HELP + exit 1 + esac +done + +docker_build \ No newline at end of file diff --git a/build_docker_multi_platforn.sh b/build_docker_multi_platforn.sh new file mode 100644 index 0000000..5a36005 --- /dev/null +++ b/build_docker_multi_platforn.sh @@ -0,0 +1,10 @@ +TAG=develop + +set e +docker buildx version +unset e + +docker buildx create --name builder +docker buildx use builder + +docker buildx build --tag dasmoorhuhn/atc-mithermometer-gateway:$TAG --platform=linux/amd64,linux/arm64,linux/arm --push . \ No newline at end of file diff --git a/python/docker_entrypoint.sh b/python/docker_entrypoint.sh index c9ddadd..9e4f582 100644 --- a/python/docker_entrypoint.sh +++ b/python/docker_entrypoint.sh @@ -1,4 +1,10 @@ #!/bin/sh -python3.12 api_endpoints.py & +env > .env + +if [ "$API" = true ]; then + python3.12 api_endpoints.py & + sleep 1 +fi + sudo python3.12 main.py \ No newline at end of file diff --git a/python/requierements.txt b/python/requierements.txt index 0965033..bfe7725 100644 --- a/python/requierements.txt +++ b/python/requierements.txt @@ -1,6 +1,5 @@ pyyaml bs4 -lxml requests flask flask_cors \ No newline at end of file diff --git a/python/src/discovery.py b/python/src/discovery.py index 0b7451b..7d8b17f 100644 --- a/python/src/discovery.py +++ b/python/src/discovery.py @@ -49,7 +49,7 @@ def cleanup(): devices = [] -def start_discovery(timeout=20.0): +def start_discovery(timeout=20): cleanup() global devices print(f'Start discovery with timout {timeout}s...') diff --git a/python/src/load_env.py b/python/src/load_env.py new file mode 100644 index 0000000..641c4fa --- /dev/null +++ b/python/src/load_env.py @@ -0,0 +1,16 @@ +# This is a quick and dirty hack since the ENVs from the docker run command won't show up in the main.py +# I echo the output from the env command of a .env file, read and load it here into the os.environ. +# https://stackoverflow.com/questions/78684481/python-wont-find-the-env-in-my-docker-container + + +import os + + +def load_env(): + if 'DOCKER' not in os.listdir('.'): return False + with open(file='.env', mode='r') as file: ENV = file.readlines() + for env in ENV: + env = env.strip() + key, value = env.split('=') + os.environ[key] = value + return True diff --git a/python/src/loop.py b/python/src/loop.py index df71b86..561a624 100644 --- a/python/src/loop.py +++ b/python/src/loop.py @@ -3,8 +3,9 @@ from log_data import log_to_json from discovery import start_discovery -def start_loop(interval=60): +def start_loop(interval=40, timeout=20): + print(f"Starting loop with interval {interval}s") while True: - devices = start_discovery() + devices = start_discovery(timeout=timeout) log_to_json(devices) sleep(interval) diff --git a/python/src/main.py b/python/src/main.py index 79b9328..934f877 100644 --- a/python/src/main.py +++ b/python/src/main.py @@ -1,9 +1,25 @@ +import os from discovery import start_discovery from log_data import log_to_json from loop import start_loop +from load_env import load_env +DOCKER = load_env() +INTERVAL = 40 +TIMEOUT = 20 -# devices = start_discovery() -# log_to_json(devices) +if DOCKER: + print("Running in docker") + interval = os.getenv('LOOP') + timeout = os.getenv('TIMEOUT') -start_loop() + try:INTERVAL = int(interval) + except:pass + + try:TIMEOUT = int(timeout) + except:pass + + start_loop(INTERVAL, TIMEOUT) + +else: + start_loop(interval=40) diff --git a/run_docker.sh b/run_docker.sh index 54837dd..c6e4b24 100644 --- a/run_docker.sh +++ b/run_docker.sh @@ -1,21 +1,117 @@ -TAG=develop-alpine +TAG=develop CONTAINER=dasmoorhuhn/atc-mithermometer-gateway:$TAG +CONTAINER_NAME=ATC_MiThermometer_Gateway +VOLUME=YOUR_VOLUME -sudo killall -9 bluetoothd > /dev/null 2>&1 -# docker stop $CONTAINER > /dev/null 2>&1 -sh stop_docker.sh -docker container rm ATC_MiThermometer_Gateway > /dev/null 2>&1 -echo Start container... -docker run \ - --cap-add=SYS_ADMIN \ - --cap-add=NET_ADMIN \ - --net=host \ - --name=ATC_MiThermometer_Gateway \ - --restart unless-stopped \ - --tty \ - -ti \ - -v /var/run/dbus/:/var/run/dbus/ \ - -v ./data:/src/data \ - $CONTAINER \ +D="" +TIME_ZONE="Europe/Berlin" +INTERACTIVE=false +BUILD=false +API=false +LOOP="0" +TIMEOUT="0" + +HELP="USAGE: sh run_docker.sh [OPTIONS] \n +[ -d ] Run in Backgrund \n +[ -t | --tag ] Set a docker tag \n +[ -b | --build ] Build the image before running the container \n +[ -l | --loop ] Start the gateway in looping mode. e.g.: --loop 40 will set the interval of the loop to 40s. Default is single run mode \n +[ -a | --api ] Start with the API \n +[ -tz | --timezone ] Set the timezone. Default is Europe/Berlin \n +[ -to | --timeout ] Set the timeout for the bluetooth scan. default is 20s \n +[ -h | --help ] Get this dialog" + +docker_run() { + sudo killall -9 bluetoothd > /dev/null 2>&1 + echo Killing old container... + docker stop $CONTAINER_NAME + docker container rm $CONTAINER_NAME > /dev/null 2>&1 + + COMMAND="docker run $D" + COMMAND="$COMMAND --cap-add=SYS_ADMIN" + COMMAND="$COMMAND --cap-add=NET_ADMIN" + COMMAND="$COMMAND --net=host" + COMMAND="$COMMAND --env TZ=$TIME_ZONE" + COMMAND="$COMMAND --env API=$API" + COMMAND="$COMMAND --name=$CONTAINER_NAME" + COMMAND="$COMMAND --restart=on-failure" + COMMAND="$COMMAND --volume=/var/run/dbus/:/var/run/dbus/" + COMMAND="$COMMAND --volume=$VOLUME:/src/data" + + if [ "$INTERACTIVE" = true ]; then + COMMAND="$COMMAND --interactive" + COMMAND="$COMMAND --tty" + COMMAND="$COMMAND --attach=stdout" + COMMAND="$COMMAND --attach=stdin" + fi + + if [ "$LOOP" != "0" ]; then + COMMAND="$COMMAND --env LOOP=$LOOP" + fi + + if [ "$TIMEOUT" != "0" ]; then + COMMAND="$COMMAND --env TIMEOUT=$TIMEOUT" + fi + + if [ "$BUILD" = true ]; then + sh build_docker.sh --tag $TAG + fi + + echo $COMMAND + echo Start container... + echo + $COMMAND $CONTAINER + + docker container rm $CONTAINER_NAME > /dev/null 2>&1 +} + +while [ "$1" != "" ]; do + case $1 in + -d ) + D="-d" + shift + ;; + -a | --api) + API=true + shift + ;; + -b | --build ) + BUILD=true + shift + ;; + -tz | --timezone ) + shift + TIME_ZONE=$1 + shift + ;; + -to | --timeout ) + shift + TIMEOUT=$1 + shift + ;; + -t | --tag ) + shift + TAG=$1 + shift + ;; + -l | --loop ) + shift + LOOP=$1 + shift + ;; + -i | --interactive ) + INTERACTIVE=true + shift + ;; + -h | --help ) + echo $HELP + exit + ;; + * ) + echo $HELP + exit 1 + esac +done + +docker_run -docker container rm ATC_MiThermometer_Gateway > /dev/null 2>&1 \ No newline at end of file