Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/dockerize #2

Merged
merged 3 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.circleci/
.git/
test/
80 changes: 80 additions & 0 deletions .github/workflows/container-build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Create and publish Container image

on:
push:
branches:
- master
tags:
- 'v*'
pull_request:
branches:
- master

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
TEST_TAG: ${{ github.repository }}:test_tag

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
lfs: true

- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Login to Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=edge
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
flavor: |
latest=auto
- name: Build and export
uses: docker/build-push-action@v4
with:
context: .
file: Containerfile
load: true
tags: ${{ env.TEST_TAG }}

- name: Test
run: |
docker container run --rm --volume "$(pwd)/test:/usr/local/src/solaredge/test" ${{ env.TEST_TAG }} '/bin/sh' -c 'apk add --no-cache bash && ./test/test.sh'
- name: Build and push
uses: docker/build-push-action@v4
with:
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6
context: .
file: Containerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
39 changes: 39 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Copyright (C) 2023 Olliver Schinagl <[email protected]>

ARG PYTHON_VERSION="3-alpine"

FROM index.docker.io/library/python:${PYTHON_VERSION}


COPY requirements.txt /tmp/

RUN apk add --no-cache --virtual .build-deps \
gcc \
musl-dev \
linux-headers \
&& \
pip --no-cache-dir install --requirement '/tmp/requirements.txt' && \
rm '/tmp/requirements.txt' && \
apk del .build-deps && \
install -d -m 775 '/usr/local/src' && \
addgroup -S 'semonitor' && \
adduser -D -G 'semonitor' -h '/usr/local/src/semonitor' -s '/bin/nologin' -S 'semonitor' && \
adduser 'semonitor' 'usb' && \
install -d -m 775 -g 'semonitor' -o 'semonitor' '/var/lib/semonitor'

VOLUME /var/lib/semonitor

WORKDIR /usr/local/src/semonitor/

COPY conversion /usr/local/src/semonitor/conversion
COPY scripts/semonitor.sh /usr/local/bin/semonitor.sh
COPY se /usr/local/src/semonitor/se
COPY semonitor.py /usr/local/src/semonitor/semonitor.py
COPY services/container-entrypoint.sh /init
COPY utilities /usr/local/src/semonitor/utilities

USER semonitor

ENTRYPOINT [ "/init" ]
97 changes: 97 additions & 0 deletions README.Docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Running semonitor in docker
TVHeadend can be run within a Docker container. This provides isolation from
other processes by running it in a containerized environment. As this is not
and in-depth tutorial on docker, those with Docker, containers or cgroups see
[docker.com][docker].

This guide is not a comprehensive guide, but just lists the most basic things!


## Building the container
Building the container is only needed if the official one is not sufficient,
or when developing on semonitor.

To build the image, the following can be used, where `ISSUE-123` is just used
as an example. It is important in that it will be re-used later.

```sh
docker image build \
--file 'Containerfile' \
--rm \
--tag 'semonitor:ISSUE-123' \
'./'
```


## Running tests
The tests are not included in the container image, so we volume mount them to
make them available to the installed semonitor application.

```sh
docker container run \
--interactive \
--rm \
--tty \
--user 'root:root' \
--volume "$(pwd)/test:/usr/local/src/semonitor/test" \
'semonitor:ISSUE-123' \
'/bin/sh' -c 'apk add --no-cache bash runuser && runuser --user semonitor -- ./test/test.sh'
```


## Running serial device
Running the semonitor on a serial device using the official latest image can
be done as follows.

```sh
docker container run \
--device '/dev/ttyUSB0:/dev/solaredge' \
--interactive \
--rm \
--tty \
--volume "$(pwd)/semonitor_logs/:/semonitor/" \
'ghcr.io/jbuel/solaredge:latest' \
semonitor.sh \
-a \
-b 115200 \
-m \
-o "/semonitor/json/$(date +%Y%m%d).json" \
-r "/semonitor/rec/$(date +%Y%m%d).rec" \
-s '1234567' \
-t 4 \
'/dev/solaredge'
```

## Using compose
It is also possible to run the container using `docker compose`. Here an
example. The device used is a udev symlinked serial to USB adapter, whith
the appropriate permissions.

```yaml
networks:
semonitor: {}

volumes:
semonitor:

services:
sslh:
image: ghcr.io/jbhuel/solaredge:master
cap_drop:
- all
ulimits:
nproc: 64
nofile:
soft: 4194304
hard: 16777216
devices:
- /dev/solaredge
env_file:
- common.env
volumes:
- semonitor:/var/lib/semonitor:rw
networks:
- semonitor
command: -a -b 115200 -m -o "/var/lib/semonitor/json/$(date +%Y%m%d).json" -r "/var/lib/semonitor/rec/$(date +%Y%m%d).rec" -s '1234567' -t 4 '/dev/solaredge'
restart: unless-stopped
```
13 changes: 13 additions & 0 deletions scripts/semonitor.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh
# SPDX-License-Identifier: AGPL-3.0-or-later

set -eu
if [ -n "${DEBUG_TRACE_SH:-}" ] && \
[ "${DEBUG_TRACE_SH:-}" != "${DEBUG_TRACE_SH#*"$(basename "${0}")"*}" ] || \
[ "${DEBUG_TRACE_SH:-}" = 'all' ]; then
set -x
fi

exec '/usr/local/src/solaredge/semonitor.py' "${@}"

exit 0
2 changes: 1 addition & 1 deletion se/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def validated_ports(ports_str):
elif args.datasource != "stdin":
# figure out the list of valid serial ports on this server
# this is either a list of tuples or ListPortInfo objects
serial_ports = serial.tools.list_ports.comports()
serial_ports = serial.tools.list_ports.comports(include_links=True)
serial_port_names = map(lambda p: p.device if isinstance(p,
serial.tools.list_ports_common.ListPortInfo) else p[0], serial_ports)
serialDevice = args.datasource in serial_port_names
Expand Down
4 changes: 3 additions & 1 deletion se/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import logging
import se.logutils

from pathlib import Path

logger = logging.getLogger(__name__)
socketTimeout = 120.0

Expand Down Expand Up @@ -52,7 +54,7 @@ def openDataSocket(ports):

# open serial device
def openSerial(inFileName, baudRate):
return serial.Serial(inFileName, baudrate=baudRate)
return serial.Serial(Path(inFileName).resolve(), baudrate=baudRate)

def openInFile(inFileName):
if inFileName == "stdin":
Expand Down
2 changes: 1 addition & 1 deletion se/msg.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
sleepInterval = .1

# Hard coded last0503.msg file. os module used to find full path to calling msg.py file, then removes the se part so it's essentially the root of solaredge (where semonitor.py lives)
LAST0503FILE = os.path.dirname(os.path.realpath(__file__)).replace('/'+ __name__.split(".")[0], '') + "/last0503.msg"
LAST0503FILE = os.path.dirname(os.path.realpath(__file__)).removesuffix(__name__.split(".")[0]) + "last0503.msg"

class SECrypto:
def __init__(self, key, msg0503):
Expand Down
24 changes: 24 additions & 0 deletions services/container-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2023 Olliver Schinagl <[email protected]>
#
# A beginning user should be able to docker run image bash (or sh) without
# needing to learn about --entrypoint
# https://github.com/docker-library/official-images#consistency

set -eu

bin='semonitor.sh'

# run command if it is not starting with a "-" and is an executable in PATH
if [ "${#}" -le 0 ] || \
[ "${1#-}" != "${1}" ] || \
[ -d "${1}" ] || \
! command -v "${1}" > '/dev/null' 2>&1; then
entrypoint='true'
fi

exec ${entrypoint:+${bin:?}} "${@}"

exit 0