Skip to content

Commit ce8c912

Browse files
authored
Merge pull request #1 from steersbob/feature/cgi
Refactor to C++ application in Alpine container
2 parents cc283a0 + 690f823 commit ce8c912

25 files changed

+14841
-1497
lines changed

.devcontainer/Dockerfile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
FROM mcr.microsoft.com/devcontainers/base:alpine-3.20
2+
3+
RUN <<EOF
4+
set -ex
5+
apk update
6+
apk add --no-cache \
7+
asio-dev \
8+
autoconf \
9+
build-base \
10+
binutils \
11+
cmake \
12+
curl \
13+
file \
14+
gcc \
15+
g++ \
16+
git \
17+
libgcc \
18+
libtool \
19+
linux-headers \
20+
make \
21+
musl-dev \
22+
ninja \
23+
tar \
24+
unzip \
25+
wget
26+
EOF

.devcontainer/devcontainer.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/alpine
3+
{
4+
"name": "Alpine",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"build": {
7+
"dockerfile": "Dockerfile"
8+
},
9+
// Features to add to the dev container. More info: https://containers.dev/features.
10+
// "features": {},
11+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
12+
// "forwardPorts": [],
13+
// Use 'postCreateCommand' to run commands after the container is created.
14+
// "postCreateCommand": "uname -a",
15+
// Configure tool-specific properties.
16+
// "customizations": {},
17+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
18+
// "remoteUser": "root"
19+
"customizations": {
20+
"vscode": {
21+
"settings": {
22+
"shellformat.path": "/usr/local/bin/shfmt"
23+
},
24+
// Add the IDs of extensions you want installed when the container is created.
25+
"extensions": [
26+
"editorconfig.editorconfig",
27+
"ms-vscode.cpptools",
28+
"huizhou.githd",
29+
"letmaik.git-tree-compare",
30+
"GitHub.vscode-pull-request-github",
31+
"twxs.cmake",
32+
"ms-vscode.cmake-tools"
33+
]
34+
}
35+
},
36+
"mounts": [
37+
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind", // mount the Docker socket
38+
"source=/etc/localtime,target=/etc/localtime,type=bind,readonly", // mount localtime
39+
"source=/dev,target=/dev,type=bind", // Required for recognizing new USB devices
40+
// "source=${localWorkspaceFolder}/.devcontainer/.bash_aliases,target=/home/vscode/.bash_aliases,type=bind",
41+
// "source=brewblox-usb-proxy-extensions,target=/home/vscode/.vscode-server/extensions,type=volume"
42+
],
43+
"runArgs": [
44+
"--rm",
45+
"--cap-add=SYS_PTRACE", // required for attaching a C++ debugger
46+
"--security-opt",
47+
"seccomp=unconfined",
48+
"--privileged",
49+
"--network=host"
50+
],
51+
"remoteUser": "vscode",
52+
"features": {}
53+
}

.github/workflows/build.yml

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
workflow_dispatch: {}
99

1010
env:
11-
DOCKER_IMAGE: ghcr.io/BrewBlox/brewblox-usb-proxy
11+
DOCKER_IMAGE: ghcr.io/brewblox/brewblox-usb-proxy
1212

1313
jobs:
1414
build:
@@ -19,9 +19,6 @@ jobs:
1919
- uses: actions/checkout@v4
2020
- uses: docker/setup-qemu-action@v3
2121
- uses: docker/setup-buildx-action@v3
22-
- uses: actions/setup-python@v5
23-
with:
24-
python-version: "3.11"
2522

2623
- name: Get image metadata
2724
id: meta
@@ -37,21 +34,6 @@ jobs:
3734
username: ${{ github.actor }}
3835
password: ${{ secrets.GITHUB_TOKEN }}
3936

40-
- name: Install dependencies
41-
run: |
42-
python -m pip install --upgrade pip
43-
pip install poetry wheel
44-
poetry install
45-
46-
- name: Test
47-
run: |
48-
poetry run pytest
49-
poetry run flake8
50-
51-
- name: Build Python dist
52-
run: |
53-
poetry run invoke dist
54-
5537
- name: Build Docker image
5638
uses: docker/build-push-action@v5
5739
with:

.vscode/settings.json

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,79 @@
11
{
2-
"python.defaultInterpreterPath": ".venv/bin/python",
3-
"python.terminal.activateEnvInCurrentTerminal": true,
4-
"python.terminal.activateEnvironment": true,
5-
"python.testing.pytestArgs": [
6-
"."
7-
],
8-
"python.testing.unittestEnabled": false,
9-
"python.testing.pytestEnabled": true,
10-
"python.languageServer": "Pylance",
11-
"[python]": {
12-
"editor.defaultFormatter": "ms-python.autopep8"
2+
"files.associations": {
3+
"iostream": "cpp",
4+
"array": "cpp",
5+
"atomic": "cpp",
6+
"bit": "cpp",
7+
"*.tcc": "cpp",
8+
"cctype": "cpp",
9+
"charconv": "cpp",
10+
"chrono": "cpp",
11+
"clocale": "cpp",
12+
"cmath": "cpp",
13+
"codecvt": "cpp",
14+
"compare": "cpp",
15+
"concepts": "cpp",
16+
"condition_variable": "cpp",
17+
"cstdarg": "cpp",
18+
"cstddef": "cpp",
19+
"cstdint": "cpp",
20+
"cstdio": "cpp",
21+
"cstdlib": "cpp",
22+
"cstring": "cpp",
23+
"ctime": "cpp",
24+
"cwchar": "cpp",
25+
"cwctype": "cpp",
26+
"deque": "cpp",
27+
"map": "cpp",
28+
"set": "cpp",
29+
"string": "cpp",
30+
"unordered_map": "cpp",
31+
"unordered_set": "cpp",
32+
"vector": "cpp",
33+
"exception": "cpp",
34+
"algorithm": "cpp",
35+
"functional": "cpp",
36+
"iterator": "cpp",
37+
"memory": "cpp",
38+
"memory_resource": "cpp",
39+
"numeric": "cpp",
40+
"optional": "cpp",
41+
"random": "cpp",
42+
"ratio": "cpp",
43+
"string_view": "cpp",
44+
"system_error": "cpp",
45+
"tuple": "cpp",
46+
"type_traits": "cpp",
47+
"utility": "cpp",
48+
"format": "cpp",
49+
"fstream": "cpp",
50+
"future": "cpp",
51+
"initializer_list": "cpp",
52+
"iomanip": "cpp",
53+
"iosfwd": "cpp",
54+
"istream": "cpp",
55+
"limits": "cpp",
56+
"mutex": "cpp",
57+
"new": "cpp",
58+
"numbers": "cpp",
59+
"ostream": "cpp",
60+
"semaphore": "cpp",
61+
"span": "cpp",
62+
"sstream": "cpp",
63+
"stdexcept": "cpp",
64+
"stop_token": "cpp",
65+
"streambuf": "cpp",
66+
"thread": "cpp",
67+
"typeinfo": "cpp",
68+
"variant": "cpp",
69+
"csignal": "cpp",
70+
"any": "cpp",
71+
"complex": "cpp",
72+
"coroutine": "cpp",
73+
"list": "cpp",
74+
"source_location": "cpp",
75+
"shared_mutex": "cpp",
76+
"cinttypes": "cpp",
77+
"typeindex": "cpp"
1378
}
14-
}
79+
}

.vscode/tasks.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"type": "cmake",
6+
"label": "CMake: build",
7+
"command": "build",
8+
"targets": [
9+
"all"
10+
],
11+
"group": "build",
12+
"problemMatcher": [],
13+
"detail": "CMake template build task"
14+
}
15+
]
16+
}

CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
set(CMAKE_CXX_STANDARD 20)
3+
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
4+
set(CMAKE_RULE_MESSAGES OFF)
5+
6+
cmake_policy(SET CMP0076 NEW)
7+
8+
# Declare the project
9+
project(usb_proxy)
10+
add_executable(usb_proxy src/main.cpp)
11+
12+
# Get system variables
13+
get_filename_component(APP_ROOT
14+
${CMAKE_CURRENT_LIST_DIR}/
15+
ABSOLUTE
16+
)
17+
18+
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -ggdb")
19+
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -ggdb")

Dockerfile

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,62 @@
1-
FROM python:3.11-bookworm as base
1+
FROM alpine:3.20 as base
2+
WORKDIR /app
23

3-
ENV PIP_EXTRA_INDEX_URL=https://www.piwheels.org/simple
4-
ENV PIP_FIND_LINKS=/wheeley
5-
ENV VENV=/app/.venv
6-
ENV PATH="$VENV/bin:$PATH"
4+
RUN <<EOF
5+
set -ex
6+
apk update
7+
apk add --no-cache \
8+
asio-dev \
9+
autoconf \
10+
build-base \
11+
binutils \
12+
cmake \
13+
curl \
14+
file \
15+
gcc \
16+
g++ \
17+
git \
18+
libgcc \
19+
libtool \
20+
linux-headers \
21+
make \
22+
musl-dev \
23+
ninja \
24+
tar \
25+
unzip \
26+
wget
27+
EOF
728

8-
COPY ./dist /app/dist
29+
COPY CMakeLists.txt /app/
30+
COPY src/* /app/src/
931

1032
RUN <<EOF
1133
set -ex
12-
13-
mkdir /wheeley
14-
python3 -m venv $VENV
15-
pip3 install --upgrade pip wheel setuptools
16-
pip3 wheel --wheel-dir=/wheeley -r /app/dist/requirements.txt
17-
pip3 wheel --wheel-dir=/wheeley /app/dist/*.tar.gz
34+
mkdir build/
35+
cd build
36+
cmake -G Ninja -S ..
37+
ninja
1838
EOF
1939

20-
FROM python:3.11-slim-bookworm
40+
FROM alpine:3.20
2141
EXPOSE 5000
22-
WORKDIR /app
23-
24-
ENV PIP_FIND_LINKS=/wheeley
25-
ENV VENV=/app/.venv
26-
ENV PATH="$VENV/bin:$PATH"
2742

28-
COPY --from=base /wheeley /wheeley
29-
COPY ./entrypoint.sh ./entrypoint.sh
43+
COPY --from=base /app/build/usb_proxy /app/usb_proxy
3044

3145
RUN <<EOF
3246
set -ex
33-
34-
apt-get update
35-
apt-get install -y --no-install-recommends \
47+
apk update
48+
apk add --no-cache \
3649
socat \
37-
usbutils
38-
rm -rf /var/cache/apt/archives /var/lib/apt/lists
50+
libstdc++
51+
52+
rm -rf /var/cache/apk/*
3953

40-
python3 -m venv $VENV
41-
pip3 install --no-index brewblox_usb_proxy
42-
pip3 freeze
43-
rm -rf /wheeley
54+
chmod +x /app/usb_proxy
4455
EOF
4556

57+
# Discovery terminates expired proxies as a side effect.
58+
# By periodically discovering the non-existent "health" device,
59+
# we both confirm that the service is healthy, and trigger this side effect.
60+
HEALTHCHECK CMD wget --quiet --spider --tries=1 http://127.0.0.1:5000/usb-proxy/discover/health || exit 1
4661

47-
ENTRYPOINT ["bash", "./entrypoint.sh"]
62+
ENTRYPOINT ["/app/usb_proxy"]

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
11
# USB Proxy for Spark devices
22

33
This service scans for connected Spark USB devices, and then forwards the connection over TCP.
4+
5+
## Endpoints
6+
7+
### `GET /usb-proxy/discover/{desired_id: string}`
8+
9+
This will scan all connected USB devices.
10+
All previously connected devices that are no longer present will have their proxies stopped.\
11+
If no proxy is active for any device that matches `desired_id`, one will be started.
12+
13+
`desired_id` is either a lower case device ID for a Spark 2/3, or the wildcard value "all".
14+
15+
The return value is a JSON object with all detected devices.
16+
The key is the device ID, and the value is the associated TCP port, or `null` if no proxy is active.
17+
18+
Example output:
19+
```json
20+
{
21+
"30003d001947383434353030":null,
22+
"280038000847343337373738":9001
23+
}
24+
```

brewblox_usb_proxy/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)