Skip to content

Commit

Permalink
feat(docker): add instructions and icon for docker compositions
Browse files Browse the repository at this point in the history
  • Loading branch information
sassanh committed Nov 28, 2024
1 parent 81a3e32 commit ac69465
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- chore: add `log_async_process` to log the output of an async processes in a notification if they don't run successfully
- refactor(core): housekeeping: rename `extra_information` to `qr_code_generation_instructions` in `ubo_input`, add `.tmpl` extension for extension files, use `textarea` for `LONG` input field type in web dashboard, rename `..._HOST` env variables to `..._ADDRESS`, use underscore thousand separators for big numbers in codebase
- feat(docker): support docker compositions and add a way to import `docker-compose.yml` files
- feat(docker): add instructions and icon for docker compositions

## Version 1.1.0

Expand Down
28 changes: 27 additions & 1 deletion ubo_app/services/080-docker/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@
SubMenuItem,
)

from ubo_app.store.dispatch_action import DispatchItem
from ubo_app.store.main import store
from ubo_app.store.services.docker import (
DockerItemStatus,
ImageState,
)
from ubo_app.store.services.notifications import (
Notification,
NotificationExtraInformation,
NotificationsAddAction,
)
from ubo_app.utils.async_ import create_task

if TYPE_CHECKING:
Expand Down Expand Up @@ -133,7 +139,27 @@ def action() -> PageWidget:
else functools.partial(stop_container, image=image),
),
)
if not image.id.startswith('composition_'):
if image.id.startswith('composition_'):
items.append(
DispatchItem(
label='Instructions',
key='instructions',
icon='󰋗',
store_action=NotificationsAddAction(
notification=Notification(
icon='󰋗',
title='Instructions',
content='',
extra_information=NotificationExtraInformation(
text=image.instructions,
)
if image.instructions
else None,
),
),
),
)
else:
items.append(
SubMenuItem(
label='Ports',
Expand Down
6 changes: 5 additions & 1 deletion ubo_app/services/080-docker/reducer.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ def image_reducer(
and 'label' in action.payload
):
return CompleteReducerResult(
state=ImageState(id=action.key, label=action.payload['label']),
state=ImageState(
id=action.key,
label=action.payload['label'],
instructions=action.payload.get('instructions', None),
),
events=[DockerImageRegisterAppEvent(image=action.key)],
)
raise InitializationActionError(action)
Expand Down
48 changes: 33 additions & 15 deletions ubo_app/services/080-docker/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import asyncio
import contextlib
import functools
import json
import uuid
from typing import TYPE_CHECKING, cast

Expand Down Expand Up @@ -328,6 +329,22 @@ async def act() -> None:
description='This will be saved as the docker-compose.yml file',
required=True,
),
InputFieldDescription(
name='icon',
label='Icon',
type=InputFieldType.TEXT,
description="""<a \
href="https://www.nerdfonts.com/cheat-sheet" target="_blank">Nerd Fonts</a> are \
supported""",
required=False,
),
InputFieldDescription(
name='instructions',
label='Instructions',
type=InputFieldType.LONG,
description='Instructions on how to use this composition',
required=False,
),
],
)

Expand All @@ -339,14 +356,15 @@ async def act() -> None:
composition_path.mkdir(exist_ok=True, parents=True)
with (composition_path / 'docker-compose.yml').open('w') as file:
file.write(data['yaml-config'])
with (composition_path / 'label').open('w') as file:
file.write(data['label'])
with (composition_path / 'metadata.json').open('w') as file:
data.pop('yaml-config')
file.write(json.dumps(data))
store.dispatch(
CombineReducerRegisterAction(
_id=reducer_id,
key=id,
reducer=image_reducer,
payload={'label': data['label']},
payload=data,
),
)

Expand Down Expand Up @@ -395,9 +413,9 @@ def registries_menu_items(usernames: dict[str, str]) -> Sequence[Item]:
]


def _register_image_app_entry(id: str) -> None:
if id in IMAGES:
image = IMAGES[id]
def _register_image_app_entry(event: DockerImageRegisterAppEvent) -> None:
if event.image in IMAGES:
image = IMAGES[event.image]
store.dispatch(
RegisterRegularAppAction(
menu_item=ActionItem(
Expand All @@ -409,19 +427,19 @@ def _register_image_app_entry(id: str) -> None:
),
)
else:
path = COMPOSITIONS_PATH / id
path = COMPOSITIONS_PATH / event.image
if not path.exists():
logger.error('Composition not found', extra={'image': id})
logger.error('Composition not found', extra={'image': event.image})
return
label = (path / 'label').read_text()
metadata = json.load((path / 'metadata.json').open())
store.dispatch(
RegisterRegularAppAction(
menu_item=ActionItem(
label=label,
icon='󰣆',
action=functools.partial(docker_item_menu, id),
label=metadata['label'],
icon=metadata['icon'] or '󰣆',
action=functools.partial(docker_item_menu, event.image),
),
key=id,
key=event.image,
),
)

Expand All @@ -442,7 +460,7 @@ def _load_images() -> None:
_id=reducer_id,
key=item.stem,
reducer=image_reducer,
payload={'label': (item / 'label').read_text()},
payload=json.load((item / 'metadata.json').open()),
)
for item in (
COMPOSITIONS_PATH.iterdir() if COMPOSITIONS_PATH.is_dir() else []
Expand Down Expand Up @@ -500,7 +518,7 @@ def init_service() -> None:
store.subscribe_event(DockerLoadImagesEvent, _load_images)
store.subscribe_event(
DockerImageRegisterAppEvent,
lambda event: _register_image_app_entry(event.image),
_register_image_app_entry,
)

create_task(
Expand Down
9 changes: 6 additions & 3 deletions ubo_app/services/090-web-ui/templates/index.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,17 @@
<input name="id" type="hidden" value="{{ input.id }}" />
<p>
<label>
<h3>
<h2>
{{ input.prompt }}
</h3>
</h2>
{% if input.fields %}
{% for field in input.fields %}
<p>
<label>
{{ field.label }}:
<span style='margin-bottom: .25em; display: inline-block;'>{{ field.label }}</span>
<span style='color: #888888'>
{% if field.required %} (required){%else%} (optional){% endif %}
</span>:
{% if field.type == 'select' %}
<select name="{{ field.name }}">
{% for option in field.options %}
Expand Down
1 change: 1 addition & 0 deletions ubo_app/store/services/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class ImageState(Immutable):

id: str
label: str
instructions: str | None
status: DockerItemStatus = DockerItemStatus.NOT_AVAILABLE
container_ip: str | None = None
docker_id: str | None = None
Expand Down

0 comments on commit ac69465

Please sign in to comment.