From d544873055382c8509af49fc6270c68319a69f5d Mon Sep 17 00:00:00 2001 From: Thomas Moore Date: Fri, 8 Jan 2021 09:24:13 -0500 Subject: [PATCH] Add Ingress Support (#1) * Add ingress support * Development framework * Add ARCH arg for correct s6 version * Support new ARCH arg for correct s6 overlay version * Add PR trigger to workflow * Add image again Co-authored-by: Matt White --- .devcontainer/Dockerfile | 45 ++++++++++++++ .devcontainer/devcontainer.json | 29 +++++++++ .devcontainer/start_ha.sh | 103 ++++++++++++++++++++++++++++++++ .github/workflows/teslamate.yml | 26 ++++++-- .vscode/tasks.json | 41 +++++++++++++ Dockerfile | 15 ++++- README.md | 8 --- build.json | 5 ++ config.json | 10 +--- scripts/ha-bootstrap.sh | 2 +- services/nginx/finish | 2 + services/nginx/run | 3 + services/nginx/teslamate.conf | 42 +++++++++++++ services/teslamate/finish | 2 + services/teslamate/run | 11 ++++ 15 files changed, 319 insertions(+), 25 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/start_ha.sh create mode 100644 .vscode/tasks.json create mode 100644 build.json create mode 100644 services/nginx/finish create mode 100644 services/nginx/run create mode 100644 services/nginx/teslamate.conf create mode 100644 services/teslamate/finish create mode 100644 services/teslamate/run diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..9713462 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,45 @@ +FROM ubuntu:20.04 + +WORKDIR /workspaces + +# Default ENV +ENV LANG C.UTF-8 +ENV DEBIAN_FRONTEND noninteractive + +# Set shell +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Use the mirror protocol for a fast mirror +RUN sed -i -e 's/http:\/\/archive\.ubuntu\.com\/ubuntu\//mirror:\/\/mirrors\.ubuntu\.com\/mirrors\.txt/' /etc/apt/sources.list + +# Install docker, jq, socat +# https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/ +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + curl \ + dbus \ + software-properties-common \ + gpg-agent \ + git \ + jq \ + socat \ + sudo \ + && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - \ + && add-apt-repository "deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ + && apt-get update && apt-get install -y --no-install-recommends \ + docker-ce \ + docker-ce-cli \ + containerd.io +# This is a development container. Don't bother to clean up apt cache, this way we have it handy later + +COPY .devcontainer/start_ha.sh /usr/local/bin/start_ha.sh + +# Install dependencies for the add-on development below. For example, if you're running Node.js, +# you may want something like the following... +# RUN apt-get install -y --no-install-recommends nodejs npm + +# Generate a machine-id for this container +RUN rm /etc/machine-id && dbus-uuidgen --ensure=/etc/machine-id + +ENV DEBIAN_FRONTEND=dialog diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..913bf0b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +// For format details, see https://aka.ms/vscode-remote/devcontainer.json +// TODO: When https://github.com/microsoft/vscode-remote-release/issues/2129 is fixed, move to ${localWorkspaceFolderBasename}\ +{ + "name": "Home Assistant Add-On", + "context": "..", + "dockerFile": "Dockerfile", + "appPort": 8123, + "runArgs": [ + "-e", + "GIT_EDITOR=code --wait", + "--privileged" + ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/test_hassio/addons/local/myaddon,type=bind,consistency=delegated", + "workspaceFolder": "/workspaces/test_hassio/addons/local/myaddon", + "mounts": [ + // Cache docker images between devcontainer rebuilds (and share between devcontainers) + "source=vsc-hassio-docker,target=/var/lib/docker,type=volume" + ] + + // Post-create command to initialize the workspace. For example, for a node.js add-on you may want: + // "postCreateCommand": "cd /workspaces/test_hassio/addons/local/myaddon && npm install", + // "extensions": [ + // "dbaeumer.vscode-eslint", + // "maty.vscode-mocha-sidebar" + // ] +} diff --git a/.devcontainer/start_ha.sh b/.devcontainer/start_ha.sh new file mode 100644 index 0000000..7262a35 --- /dev/null +++ b/.devcontainer/start_ha.sh @@ -0,0 +1,103 @@ +#!/bin/bash +set -eE + +DOCKER_TIMEOUT=30 +DOCKER_PID=0 + + +function start_docker() { + local starttime + local endtime + + echo "Starting docker..." + dockerd 2> /dev/null & + DOCKER_PID=$! + + echo "Waiting for docker to initialize..." + starttime="$(date +%s)" + endtime="$(date +%s)" + until docker info >/dev/null 2>&1; do + if [ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]; then + sleep 1 + endtime=$(date +%s) + else + echo "Timeout while waiting for docker to come up" + exit 1 + fi + done + echo "Docker was initialized" +} + + +function stop_docker() { + local starttime + local endtime + + echo "Stopping in container docker..." + if [ "$DOCKER_PID" -gt 0 ] && kill -0 "$DOCKER_PID" 2> /dev/null; then + starttime="$(date +%s)" + endtime="$(date +%s)" + + # Now wait for it to die + kill "$DOCKER_PID" + while kill -0 "$DOCKER_PID" 2> /dev/null; do + if [ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]; then + sleep 1 + endtime=$(date +%s) + else + echo "Timeout while waiting for container docker to die" + exit 1 + fi + done + else + echo "Your host might have been left with unreleased resources" + fi +} + + +function install() { + docker pull homeassistant/amd64-hassio-supervisor:dev + docker pull homeassistant/amd64-hassio-cli:dev +} + +function cleanup_hass_data() { + rm -rf /workspaces/test_hassio/{apparmor,backup,config.json,dns,dns.json,homeassistant,homeassistant.json,ingress.json,share,ssl,tmp,updater.json} + rm -rf /workspaces/test_hassio/addons/{core,data,git} +} + +function cleanup_docker() { + echo "Cleaning up stopped containers..." + docker rm $(docker ps -a -q) +} + +function run_supervisor() { + docker run --rm --privileged \ + --name hassio_supervisor \ + --security-opt seccomp=unconfined \ + --security-opt apparmor:unconfined \ + -v /run/docker.sock:/run/docker.sock \ + -v /run/dbus:/run/dbus \ + -v "/workspaces/test_hassio":/data \ + -v /etc/machine-id:/etc/machine-id:ro \ + -e SUPERVISOR_SHARE="/workspaces/test_hassio" \ + -e SUPERVISOR_NAME=hassio_supervisor \ + -e SUPERVISOR_DEV=1 \ + -e HOMEASSISTANT_REPOSITORY="homeassistant/qemux86-64-homeassistant" \ + homeassistant/amd64-hassio-supervisor:dev +} + +case "$1" in + "--cleanup") + echo "Cleaning up old environment" + cleanup_docker || true + cleanup_hass_data || true + exit 0;; + *) + echo "Creating development Home Assistant environment" + start_docker + trap "stop_docker" ERR + install + cleanup_docker || true + run_supervisor + stop_docker;; +esac \ No newline at end of file diff --git a/.github/workflows/teslamate.yml b/.github/workflows/teslamate.yml index 5bdc1eb..fe29eca 100644 --- a/.github/workflows/teslamate.yml +++ b/.github/workflows/teslamate.yml @@ -7,6 +7,12 @@ on: paths: - '**' - '!**.md' + pull_request: + branches: + - main + paths: + - '**' + - '!**.md' env: DOCKER_USERNAME: mattffffff @@ -45,15 +51,21 @@ jobs: run: | case "${{ matrix.arch }}" in "amd64" ) - echo "PLATFORM=linux/amd64" >> $GITHUB_ENV ;; - "i386" ) - echo "PLATFORM=linux/386" >> $GITHUB_ENV ;; + echo "PLATFORM=linux/amd64" >> $GITHUB_ENV + echo "ARCH=amd64" >> $GITHUB_ENV + ;; "aarch64" ) - echo "PLATFORM=linux/arm64" >> $GITHUB_ENV ;; + echo "PLATFORM=linux/arm64" >> $GITHUB_ENV + echo "ARCH=aarch64" >> $GITHUB_ENV + ;; "armhf" ) - echo "PLATFORM=linux/arm/v7" >> $GITHUB_ENV ;; + echo "PLATFORM=linux/arm/v7" >> $GITHUB_ENV + echo "ARCH=armhf" >> $GITHUB_ENV + ;; "armv7" ) - echo "PLATFORM=linux/arm/v7" >> $GITHUB_ENV ;; + echo "PLATFORM=linux/arm/v7" >> $GITHUB_ENV + echo "ARCH=arm" >> $GITHUB_ENV + ;; esac - name: Set environment variables from config.json run: | @@ -68,6 +80,7 @@ jobs: --pull \ --platform=$PLATFORM \ --build-arg TESLAMATE_TAG=$TESLAMATE_TAG \ + --build-arg ARCH=$ARCH \ --label io.hass.type="addon" \ --label io.hass.name="$ADDON_NAME" \ --label io.hass.description="$ADDON_DESCRIPTION" \ @@ -84,6 +97,7 @@ jobs: --push \ --platform=$PLATFORM \ --build-arg TESLAMATE_TAG=$TESLAMATE_TAG \ + --build-arg ARCH=$ARCH \ --label io.hass.type="addon" \ --label io.hass.name="$ADDON_NAME" \ --label io.hass.description="$ADDON_DESCRIPTION" \ diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..1cfad2d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Start Home Assistant", + "type": "shell", + "command": "/usr/local/bin/start_ha.sh", + "group": { + "kind": "test", + "isDefault": true, + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + },{ + "label": "Cleanup stale Home Assistant environment", + "type": "shell", + "command": "/usr/local/bin/start_ha.sh --cleanup", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + },{ + "label": "Run Home Assistant CLI", + "type": "shell", + "command": "docker run --rm -ti -v /etc/machine-id:/etc/machine-id --network=hassio --add-host hassio:172.30.32.2 homeassistant/amd64-hassio-cli:dev", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a1cf6bd..c6d65fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ FROM teslamate/grafana:${TESLAMATE_TAG} as grafana FROM teslamate/teslamate:${TESLAMATE_TAG} +ARG ARCH ARG BASHIO_VERSION=0.9.0 ARG S6_OVERLAY_VERSION=1.22.1.0 @@ -19,8 +20,9 @@ RUN apk add --no-cache \ ca-certificates \ curl \ bind-tools \ + nginx \ \ - && curl -L -s "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-amd64.tar.gz" \ + && curl -L -s "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${ARCH}.tar.gz" \ | tar zxvf - -C / \ && mkdir -p /etc/fix-attrs.d \ && mkdir -p /etc/services.d \ @@ -34,10 +36,17 @@ RUN apk add --no-cache \ COPY --chown=root scripts/*.sh / RUN chmod a+x /*.sh +COPY --chown=root services/teslamate/run services/teslamate/finish /etc/services.d/teslamate/ +RUN chmod a+x /etc/services.d/teslamate/* + +COPY --chown=root services/nginx/run services/nginx/finish /etc/services.d/nginx/ +RUN chmod a+x /etc/services.d/nginx/* + +COPY --chown=root services/nginx/teslamate.conf /etc/nginx/conf.d/ + COPY --from=grafana --chown=root /dashboards /dashboards COPY --from=grafana --chown=root /dashboards_internal /dashboards # USER nobody -ENTRYPOINT ["/ha-bootstrap.sh"] -CMD ["bin/teslamate", "start"] \ No newline at end of file +ENTRYPOINT ["/init"] \ No newline at end of file diff --git a/README.md b/README.md index 199ce58..9b32071 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,6 @@ This addon builds on the excellent work of [Adrian Kumpf](https://github.com/adr If using the Postgres addon from this repo, the database host is ```29b65938-postgres``` -## No Ingress Support - -TeslaMate does not currently support a base path, so it expects all requests to be generated from the URL root ```/```. -See https://github.com/adriankumpf/teslamate/issues/494. - -This means we cannot implement Ingress and must expose TeslaMate to the network. -I recommend you only do this to configure TeslaMate and you then remove the external port mapping. - ## Grafana Configuration > I recommend you use the existing Grafana addon from the community addons diff --git a/build.json b/build.json new file mode 100644 index 0000000..905b9e3 --- /dev/null +++ b/build.json @@ -0,0 +1,5 @@ +{ + "args": { + "TESLAMATE_TAG": "1.20.1" + } +} \ No newline at end of file diff --git a/config.json b/config.json index f58f6da..3aced30 100644 --- a/config.json +++ b/config.json @@ -6,12 +6,10 @@ "arch": ["armhf", "armv7", "aarch64", "amd64"], "startup": "application", "boot": "auto", - "ingress": false, + "ingress": true, "ports": { - "4000/tcp": null }, "ports_description": { - "4000/tcp": "Insecure web interface - disable when not in use" }, "options": { "database_user": "user", @@ -33,8 +31,7 @@ "mqtt_tls": true, "mqtt_tls_accept_invalid_certs": false, "mqtt_namespace": "account_0", - "timezone": "Europe/London", - "port": 4000 + "timezone": "Europe/London" }, "schema": { "database_user": "str", @@ -56,8 +53,7 @@ "mqtt_tls": "bool", "mqtt_tls_accept_invalid_certs": "bool", "mqtt_namespace": "str", - "timezone": "str", - "port": "int" + "timezone": "str" }, "url": "https://github.com/matt-FFFFFF/hassio-addon-repository/blob/master/teslamate/README.md", "image": "mattffffff/teslamate-{arch}" diff --git a/scripts/ha-bootstrap.sh b/scripts/ha-bootstrap.sh index 9b0491d..9c8fde7 100755 --- a/scripts/ha-bootstrap.sh +++ b/scripts/ha-bootstrap.sh @@ -18,7 +18,7 @@ export MQTT_NAMESPACE=export MQTT_TLS=$(bashio::config 'mqtt_namespace') # Other things export TZ=$(bashio::config 'timezone') -export PORT=$(bashio::config 'port') +export PORT=4000 # Import dashboards if [ $(bashio::config 'grafana_import_dashboards') == 'true' ]; then diff --git a/services/nginx/finish b/services/nginx/finish new file mode 100644 index 0000000..cb029b9 --- /dev/null +++ b/services/nginx/finish @@ -0,0 +1,2 @@ +#!/bin/sh +s6-svscanctl -t /var/run/s6/services \ No newline at end of file diff --git a/services/nginx/run b/services/nginx/run new file mode 100644 index 0000000..d468e7f --- /dev/null +++ b/services/nginx/run @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv sh +mkdir /run/nginx +exec nginx -g 'daemon off;' \ No newline at end of file diff --git a/services/nginx/teslamate.conf b/services/nginx/teslamate.conf new file mode 100644 index 0000000..f8b8b52 --- /dev/null +++ b/services/nginx/teslamate.conf @@ -0,0 +1,42 @@ +# Enable gzipping of responses. +gzip on; + +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + server_name _; + listen 8099 default_server; + + location / { + proxy_pass http://localhost:4000/; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Referer $http_referer; + + # Disable encoding to allow the sub_filter to work + proxy_set_header Accept-Encoding ""; + + # Enable sub-filtering + sub_filter_types text/css text/xml application/javascript; + sub_filter_once off; + + # Home URLs in the HTML + sub_filter 'href="/"' 'href="$http_x_ingress_path/"'; + + # Fonts in the CSS + sub_filter 'url(/' 'url($http_x_ingress_path/'; + + # Update fixed paths in the .js files + sub_filter '"/live"' '"$http_x_ingress_path/live"'; + sub_filter '"/js/"' '"$http_x_ingress_path/js/"'; + } +} \ No newline at end of file diff --git a/services/teslamate/finish b/services/teslamate/finish new file mode 100644 index 0000000..cb029b9 --- /dev/null +++ b/services/teslamate/finish @@ -0,0 +1,2 @@ +#!/bin/sh +s6-svscanctl -t /var/run/s6/services \ No newline at end of file diff --git a/services/teslamate/run b/services/teslamate/run new file mode 100644 index 0000000..0609cbb --- /dev/null +++ b/services/teslamate/run @@ -0,0 +1,11 @@ +#!/usr/bin/with-contenv sh + +ingress_entry=$(curl -X GET \ + -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \ + -H "Content-Type: application/json" \ + -s http://supervisor/addons/self/info | \ + jq -r '.data.ingress_entry') +sed -i "s^url: \[^url: \[path: \"${ingress_entry}\", ^" /opt/app/releases/*/releases.exs + +cd /opt/app +exec /ha-bootstrap.sh bin/teslamate start \ No newline at end of file