Skip to content

Commit

Permalink
feat(docker): add ngrok service (currently serves port 22 with no aut…
Browse files Browse the repository at this point in the history
…h token)
  • Loading branch information
sassanh committed Mar 23, 2024
1 parent e01c923 commit dc75880
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 37 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/integration_delivery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -384,14 +384,14 @@ jobs:
bigger than 2GB
run: |
for file in artifacts/*; do
if [ $(stat -c%s "$file") -gt 2000000000 ]; then
split -b 2000000000 "$file" "$file"_
if [ $(stat -c%s "$file") -gt 2147000000 ]; then
split -b 2147000000 "$file" "$file"_
rm "$file"
fi
done
- name: Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
files: artifacts/*
tag_name: ${{ needs.build.outputs.version }}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Version 0.11.4

- feat(docker): add ngrok service (currently serves port 22 with no auth token)

## Version 0.11.3

- test: add wireless flow test, work in progress
Expand Down
15 changes: 7 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ubo-gui = [
'dev',
] },
]
python-redux = "^0.13.1"
python-redux = "^0.13.2"
pyzbar = "^0.1.9"
sdbus-networkmanager = { version = "^2.0.0", markers = "platform_machine=='aarch64'" }
rpi_ws281x = { version = "^5.0.0", markers = "platform_machine=='aarch64'" }
Expand Down
2 changes: 1 addition & 1 deletion scripts/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM ubuntu:mantic

ARG DEBIAN_FRONTEND=noninteractive
RUN apt -y update
RUN apt -y install curl git libcap-dev libegl1 libgl1 libmtdev1 libzbar0 python3 python3-dev
RUN apt -y install gcc curl git libcap-dev libegl1 libgl1 libmtdev1 libzbar0 python3 python3-dev
RUN curl -sSL https://install.python-poetry.org | python3 -
ENV PATH="${PATH}:/root/.local/bin"
WORKDIR /ubo-app
Expand Down
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Ubo app tests."""
16 changes: 15 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
wait_for,
)

from tests.fixtures import (
pytest.register_assert_rewrite('tests.fixtures')

from tests.fixtures import ( # noqa: E402
AppContext,
LoadServices,
Stability,
Expand Down Expand Up @@ -130,3 +132,15 @@ def get(self: FakeAiohttp, url: str, **kwargs: dict[str, object]) -> Fake:
return parent.get(url, **kwargs)

sys.modules['aiohttp'] = FakeAiohttp()

class FakeSensor(Fake):
lux = 0.0
temperature = 0.0

class FakeSensorModule(Fake):
PCT2075 = FakeSensor
VEML7700 = FakeSensor

sys.modules['adafruit_pct2075'] = FakeSensorModule()
sys.modules['adafruit_veml7700'] = FakeSensorModule()
sys.modules['i2c'] = Fake()
30 changes: 24 additions & 6 deletions tests/fixtures/load_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,30 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Callable, Sequence, TypeAlias
from typing import TYPE_CHECKING, Coroutine, Literal, Protocol, Sequence, cast, overload

import pytest

if TYPE_CHECKING:
from tests.conftest import WaitFor

LoadServices: TypeAlias = Callable[[Sequence[str]], None]

class LoadServices(Protocol):
"""Load services and wait for them to be ready."""

@overload
def __call__(
self: LoadServices,
service_ids: Sequence[str],
) -> None: ...

@overload
def __call__(
self: LoadServices,
service_ids: Sequence[str],
*,
run_async: Literal[True],
) -> Coroutine[None, None, None]: ...


@pytest.fixture()
Expand All @@ -18,12 +34,14 @@ def load_services(wait_for: WaitFor) -> LoadServices:

def load_services_and_wait(
service_ids: Sequence[str],
) -> None:
*,
run_async: bool = False,
) -> Coroutine[None, None, None] | None:
from ubo_app.load_services import load_services

load_services(service_ids)

@wait_for
@wait_for(run_async=cast(Literal[True], run_async))
def check() -> None:
from ubo_app.load_services import REGISTERED_PATHS

Expand All @@ -33,6 +51,6 @@ def check() -> None:
for service in REGISTERED_PATHS.values()
), f'{service_id} not loaded'

check()
return check()

return load_services_and_wait
return cast(LoadServices, load_services_and_wait)
14 changes: 11 additions & 3 deletions tests/fixtures/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def __init__(
)
if self.results_dir.exists():
for file in self.results_dir.glob(
'window:*' if override else 'window:*.mismatch.*',
'window-*' if override else 'window-*.mismatch.*',
):
file.unlink()
self.results_dir.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -113,8 +113,16 @@ def take(self: WindowSnapshot, title: str | None = None) -> None:
hash_mismatch_path.write_text( # pragma: no cover
f'// MISMATCH: {filename}\n{new_snapshot}\n',
)
write_image(image_mismatch_path, array)
assert new_snapshot == old_snapshot, f'Window snapshot mismatch: {title}'
if self.make_screenshots:
write_image(image_mismatch_path, array)
elif self.make_screenshots:
write_image(image_path, array)
if title:
assert (
new_snapshot == old_snapshot
), f'Window snapshot mismatch for {title}'
else:
assert new_snapshot == old_snapshot, 'Window snapshot mismatch'

self.test_counter[title] += 1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@
"ports": [],
"status": "not_available"
},
"ngrok": {
"container_ip": null,
"docker_id": null,
"icon": "smart_toy",
"id": "ngrok",
"ip_addresses": [
"192.168.1.1"
],
"label": "Ngrok",
"path": "ngrok/ngrok:latest",
"ports": [],
"status": "not_available"
},
"ollama": {
"container_ip": null,
"docker_id": null,
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ async def test_all_services_register(
app_context.set_app(app)
load_services(ALL_SERVICES_LABELS)
await stability()
window_snapshot.take()
store_snapshot.take()
window_snapshot.take()
12 changes: 7 additions & 5 deletions ubo_app/services/040-sensors/setup.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
"""Setup the service."""

from __future__ import annotations

from datetime import datetime, timezone

import adafruit_pct2075
import adafruit_veml7700
import board
from kivy.clock import Clock

from ubo_app.store import dispatch
from ubo_app.store.services.sensors import Sensor, SensorsReportReadingAction


def read_sensors(_: float | None = None) -> None:
"""Read the sensor."""
import adafruit_pct2075
import adafruit_veml7700
import board

i2c = board.I2C()
temperature_sensor = adafruit_pct2075.PCT2075(i2c, address=0x48)
temperature = temperature_sensor.temperature
Expand All @@ -35,5 +35,7 @@ def read_sensors(_: float | None = None) -> None:

def init_service() -> None:
"""Initialize the service."""
from kivy.clock import Clock

Clock.schedule_interval(read_sensors, 1)
read_sensors()
44 changes: 36 additions & 8 deletions ubo_app/services/080-docker/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
ImageState,
ImageStatus,
)
from ubo_app.store.services.notifications import (
Importance,
Notification,
NotificationsAddAction,
)
from ubo_app.utils.async_ import create_task, run_in_executor


Expand Down Expand Up @@ -277,21 +282,44 @@ def act() -> None:
if container.status != 'running':
container.start()
else:
hosts = {
key: getattr(docker_state, value).container_ip
if hasattr(docker_state, value)
else value
for key, value in IMAGES[image.id].hosts.items()
if not hasattr(docker_state, value)
or getattr(docker_state, value).container_ip
}
hosts = {}
for key, value in IMAGES[image.id].hosts.items():
if not hasattr(docker_state, value):
dispatch(
NotificationsAddAction(
notification=Notification(
title='Dependency error',
content=f'Container "{value}" is not loaded',
importance=Importance.MEDIUM,
),
),
)
return
if not getattr(docker_state, value).container_ip:
dispatch(
NotificationsAddAction(
notification=Notification(
title='Dependency error',
content=f'Container "{value}" does not have an IP'
' address',
importance=Importance.MEDIUM,
),
),
)
return
if hasattr(docker_state, value):
hosts[key] = getattr(docker_state, value).container_ip
else:
hosts[key] = value
docker_client.containers.run(
image.path,
hostname=image.id,
publish_all_ports=True,
detach=True,
volumes=IMAGES[image.id].volumes,
ports=IMAGES[image.id].ports,
network_mode=IMAGES[image.id].network_mode,
environment=IMAGES[image.id].environment,
extra_hosts=hosts,
restart_policy='always',
)
Expand Down
17 changes: 17 additions & 0 deletions ubo_app/services/080-docker/reducer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Docker reducer."""

from __future__ import annotations

from dataclasses import field, replace
Expand Down Expand Up @@ -54,8 +55,12 @@ class ImageEntry(Immutable):
label: str
icon: str
path: str
dependencies: list[str] | None = None
ports: dict[str, str] = field(default_factory=dict)
hosts: dict[str, str] = field(default_factory=dict)
note: str | None = None
environment: dict[str, str] | None = None
network_mode: str = 'bridge'
volumes: list[str] | None = None


Expand Down Expand Up @@ -86,6 +91,8 @@ class ImageEntry(Immutable):
id='pi_hole',
label='Pi-hole',
icon='dns',
environment={'WEBPASSWORD': 'admin'},
note='Password: admin',
path=DOCKER_PREFIX + 'pihole/pihole:latest',
),
ImageEntry(
Expand All @@ -100,9 +107,19 @@ class ImageEntry(Immutable):
label='Open WebUI',
icon='code',
path=DOCKER_PREFIX + 'ghcr.io/open-webui/open-webui:main',
dependencies=['ollama'],
network_mode='container:ollama',
ports={'8080/tcp': '8080'},
hosts={'host.docker.internal': 'ollama'},
),
ImageEntry(
id='ngrok',
label='Ngrok',
icon='smart_toy',
network_mode='host',
path=DOCKER_PREFIX + 'ngrok/ngrok:latest',
ports={'22/tcp': '22'},
),
*(
[
ImageEntry(
Expand Down

0 comments on commit dc75880

Please sign in to comment.