Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
DEFAULT_OUTPUTS_PUSH_ATTEMPTS: Final[int] = 3
DEFAULT_TASK_UPDATE_INTERVAL_S: Final[int] = 1

main = typer.Typer(name=PROJECT_NAME)
main = typer.Typer(
name=PROJECT_NAME,
pretty_exceptions_enable=False,
pretty_exceptions_show_locals=False,
)

_logger = logging.getLogger(__name__)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ async def runs_docker_compose_down_task(
settings: Annotated[ApplicationSettings, Depends(get_settings)],
shared_store: Annotated[SharedStore, Depends(get_shared_store)],
app: Annotated[FastAPI, Depends(get_application)],
mounted_volumes: Annotated[MountedVolumes, Depends(get_mounted_volumes)],
) -> TaskId:
assert request # nosec

Expand All @@ -135,6 +136,7 @@ async def runs_docker_compose_down_task(
app=app,
shared_store=shared_store,
settings=settings,
mounted_volumes=mounted_volumes,
)
except TaskAlreadyRunningError as e:
return cast(str, e.managed_task.task_id) # type: ignore[attr-defined] # pylint:disable=no-member
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import asyncio
import json
import logging
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import AsyncIterator

import typer
from fastapi import FastAPI
Expand All @@ -17,7 +17,11 @@
from .modules.outputs import OutputsManager, setup_outputs

log = logging.getLogger(__name__)
main = typer.Typer(name=PROJECT_NAME)
main = typer.Typer(
name=PROJECT_NAME,
pretty_exceptions_enable=False,
pretty_exceptions_show_locals=False,
)


main.command()(create_settings_command(settings_cls=ApplicationSettings, logger=log))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@
from ..modules.mounted_fs import MountedVolumes
from ..modules.notifications._notifications_ports import PortNotifier
from ..modules.outputs import OutputsManager, event_propagation_disabled
from .long_running_tasksutils import run_before_shutdown_actions
from .long_running_tasks_utils import (
ensure_read_permissions_on_user_service_data,
run_before_shutdown_actions,
)
from .resource_tracking import send_service_started, send_service_stopped

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -237,6 +240,7 @@ async def task_runs_docker_compose_down(
app: FastAPI,
shared_store: SharedStore,
settings: ApplicationSettings,
mounted_volumes: MountedVolumes,
) -> None:
if shared_store.compose_spec is None:
_logger.warning("No compose-spec was found")
Expand Down Expand Up @@ -312,6 +316,8 @@ async def _send_resource_tracking_stop(platform_status: SimcorePlatformStatus):
await _send_resource_tracking_stop(SimcorePlatformStatus.OK)
raise

await ensure_read_permissions_on_user_service_data(mounted_volumes)

await _send_resource_tracking_stop(SimcorePlatformStatus.OK)

# removing compose-file spec
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import logging
import os
from datetime import timedelta
from typing import Final

from models_library.callbacks_mapping import UserServiceCommand
from servicelib.logging_utils import log_context
Expand All @@ -9,10 +12,13 @@
ContainerExecTimeoutError,
)
from ..models.shared_store import SharedStore
from ..modules.container_utils import run_command_in_container
from ..modules.mounted_fs import MountedVolumes
from .container_utils import run_command_in_container

_logger = logging.getLogger(__name__)

_TIMEOUT_PERMISSION_CHANGES: Final[timedelta] = timedelta(minutes=5)


async def run_before_shutdown_actions(
shared_store: SharedStore, before_shutdown: list[UserServiceCommand]
Expand Down Expand Up @@ -40,3 +46,22 @@ async def run_before_shutdown_actions(
container_name,
exc_info=True,
)


async def ensure_read_permissions_on_user_service_data(
mounted_volumes: MountedVolumes,
) -> None:
# Makes sure sidecar has access to all files in the user services.
# The user could have removed read permissions form a file, which will cause an error.

# NOTE: command runs inside self container since the user service container might not always be running
self_container = os.environ["HOSTNAME"]
for path_to_store in ( # apply changes to otuputs and all state folders
*mounted_volumes.disk_state_paths_iter(),
mounted_volumes.disk_outputs_path,
):
await run_command_in_container(
self_container,
command=f"chmod -R o+rX '{path_to_store}'",
timeout=_TIMEOUT_PERMISSION_CHANGES.total_seconds(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ async def _update_metrics(self):
)
self._metrics_response = MetricsResponse.from_reply(metrics_fetch_result)
except ContainerExecContainerNotFoundError as e:
_logger.info(
"Container %s was not found could nto recover metrics",
_logger.debug(
"Container %s was not found could not recover metrics",
container_name,
)
self._metrics_response = MetricsResponse.from_error(e)
except Exception as e: # pylint: disable=broad-exception-caught
_logger.info("Unexpected exception", exc_info=True)
_logger.debug("Could not recover metrics", exc_info=True)
self._metrics_response = MetricsResponse.from_error(e)

async def _task_metrics_recovery(self) -> None:
Expand Down
8 changes: 8 additions & 0 deletions services/dynamic-sidecar/tests/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from aiodocker.volumes import DockerVolume
from async_asgi_testclient import TestClient
from fastapi import FastAPI
from pytest_mock.plugin import MockerFixture
from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict
from simcore_service_dynamic_sidecar.core.application import AppState, create_app
from simcore_service_dynamic_sidecar.core.docker_compose_utils import (
Expand Down Expand Up @@ -157,3 +158,10 @@ def port_notifier(app: FastAPI) -> PortNotifier:
settings.DY_SIDECAR_PROJECT_ID,
settings.DY_SIDECAR_NODE_ID,
)


@pytest.fixture
def mock_ensure_read_permissions_on_user_service_data(mocker: MockerFixture) -> None:
mocker.patch(
"simcore_service_dynamic_sidecar.modules.long_running_tasks.ensure_read_permissions_on_user_service_data",
)
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ def _get_awaitable() -> Awaitable:


async def test_containers_down_after_starting(
mock_ensure_read_permissions_on_user_service_data: None,
httpx_async_client: AsyncClient,
client: Client,
compose_spec: str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ async def _get_task_id_docker_compose_down(httpx_async_client: AsyncClient) -> T


def _get_resource_tracking_messages(
mock_core_rabbitmq: dict[str, AsyncMock]
mock_core_rabbitmq: dict[str, AsyncMock],
) -> list[RabbitResourceTrackingMessages]:
return [
x[0][1]
Expand Down Expand Up @@ -200,6 +200,7 @@ async def _wait_for_containers_to_be_running(app: FastAPI) -> None:


async def test_service_starts_and_closes_as_expected(
mock_ensure_read_permissions_on_user_service_data: None,
mock_core_rabbitmq: dict[str, AsyncMock],
app: FastAPI,
httpx_async_client: AsyncClient,
Expand Down Expand Up @@ -383,6 +384,7 @@ async def _mocked_get_container_states(

@pytest.mark.parametrize("expected_platform_state", SimcorePlatformStatus)
async def test_user_services_crash_when_running(
mock_ensure_read_permissions_on_user_service_data: None,
mock_core_rabbitmq: dict[str, AsyncMock],
app: FastAPI,
httpx_async_client: AsyncClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ async def logging_event_handler_observer(
],
)
async def test_chown_triggers_event(
mock_ensure_read_permissions_on_user_service_data: None,
logging_event_handler_observer: None,
fake_dy_volumes_mount_dir: Path,
command_template: str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import logging
from collections.abc import Awaitable, Callable
from datetime import timedelta
from typing import TypedDict

from fastapi import FastAPI
from servicelib.async_utils import cancel_wait_task
Expand All @@ -12,44 +11,31 @@
from .background_tasks import removal_policy_task
from .modules.redis import get_redis_lock_client


@exclusive_periodic(
get_redis_lock_client,
task_interval=timedelta(hours=1),
retry_after=timedelta(minutes=5),
)
async def periodic_removal_policy_task(app: FastAPI) -> None:
await removal_policy_task(app)


_logger = logging.getLogger(__name__)


class EfsGuardianBackgroundTask(TypedDict):
name: str
task_func: Callable


_EFS_GUARDIAN_BACKGROUND_TASKS = [
EfsGuardianBackgroundTask(
name="efs_removal_policy_task", task_func=periodic_removal_policy_task
)
]


def _on_app_startup(app: FastAPI) -> Callable[[], Awaitable[None]]:
async def _startup() -> None:
with (
log_context(_logger, logging.INFO, msg="Efs Guardian startup.."),
log_context(_logger, logging.INFO, msg="Efs Guardian background task "),
log_catch(_logger, reraise=False),
):
app.state.efs_guardian_background_tasks = []
app.state.efs_guardian_removal_policy_background_task = None

# Setup periodic tasks
for task in _EFS_GUARDIAN_BACKGROUND_TASKS:
app.state.efs_guardian_background_tasks.append(
asyncio.create_task(task["task_func"](), name=task["name"])
)
_logger.info("starting efs guardian removal policy task")

@exclusive_periodic(
get_redis_lock_client(app),
task_interval=timedelta(hours=1),
retry_after=timedelta(minutes=5),
)
async def _periodic_removal_policy_task() -> None:
await removal_policy_task(app)

app.state.efs_guardian_removal_policy_background_task = asyncio.create_task(
_periodic_removal_policy_task(),
name=_periodic_removal_policy_task.__name__,
)

return _startup

Expand All @@ -63,12 +49,9 @@ async def _stop() -> None:
log_catch(_logger, reraise=False),
):
assert _app # nosec
if _app.state.efs_guardian_background_tasks:
await asyncio.gather(
*[
cancel_wait_task(task)
for task in _app.state.efs_guardian_background_tasks
]
if _app.state.efs_guardian_removal_policy_background_task:
await cancel_wait_task(
_app.state.efs_guardian_removal_policy_background_task
)

return _stop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ qx.Class.define("osparc.dashboard.NewPlusMenu", {
spacingX: 20,
});

osparc.utils.Utils.setIdToWidget(this, "newPlusMenu");

this.getContentElement().setStyles({
"border-color": qx.theme.manager.Color.getInstance().resolve("strong-main"),
});
Expand Down Expand Up @@ -207,17 +209,9 @@ qx.Class.define("osparc.dashboard.NewPlusMenu", {
__addIcon: function(menuButton, resourceInfo, resourceMetadata) {
let source = null;
if (resourceInfo && resourceInfo["icon"]) {
// first the one set in the ui_config
source = resourceInfo["icon"];
} else if (resourceMetadata && resourceMetadata["icon"]) {
// second the icon from the resource
source = resourceMetadata["icon"];
} else if (resourceMetadata && resourceMetadata["thumbnail"]) {
// third the thumbnail from the resource
source = resourceMetadata["thumbnail"];
} else {
// finally product icon
source = osparc.dashboard.CardBase.PRODUCT_ICON;
source = osparc.utils.Utils.getIconFromResource(resourceMetadata);
}

if (source) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,13 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
});
win.center();
win.open();
win.addListenerOnce("close", () => {
win.addListener("changeConfirmed", e => {
if (win.getConfirmed()) {
this.openUpdateServices();
} else {
this.__openResource();
}
win.close();
});
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ qx.Class.define("osparc.dashboard.ResourceUpgradeHelper", {
this.bind("secondaryText", secondaryButton, "label");
secondaryButton.addListener("execute", () => {
this.setConfirmed(false);
this.close(1);
}, this);
this.addButton(secondaryButton);

Expand All @@ -50,7 +49,6 @@ qx.Class.define("osparc.dashboard.ResourceUpgradeHelper", {
this.bind("primaryText", primaryButton, "label");
primaryButton.addListener("execute", () => {
this.setConfirmed(true);
this.close(1);
}, this);
const command = new qx.ui.command.Command("Enter");
primaryButton.setCommand(command);
Expand Down Expand Up @@ -86,7 +84,8 @@ qx.Class.define("osparc.dashboard.ResourceUpgradeHelper", {

confirmed: {
check: "Boolean",
init: false
init: null,
event: "changeConfirmed"
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ qx.Class.define("osparc.desktop.credits.CreditsServiceListItem", {
const name = this.getChildControl("title");
const serviceMetadata = osparc.service.Utils.getLatest(serviceKey);
if (serviceMetadata) {
icon.setSource(serviceMetadata["thumbnail"] ? serviceMetadata["thumbnail"] : osparc.dashboard.CardBase.PRODUCT_THUMBNAIL);
const source = osparc.utils.Utils.getIconFromResource(serviceMetadata);
icon.setSource(source);
name.setValue(serviceMetadata["name"]);
} else {
icon.setSource(osparc.dashboard.CardBase.PRODUCT_THUMBNAIL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,18 @@ qx.Class.define("osparc.product.tours.Tours", {

statics: {
TOURS: {
"s4llite": {
fetchTours: () => osparc.product.tours.Tours.fetchTours("/resource/osparc/tours/s4llite_tours.json")
"osparc": {
fetchTours: () => osparc.product.tours.Tours.fetchTours("/resource/osparc/tours/osparc_tours.json")
},
"s4l": {
fetchTours: () => osparc.product.tours.Tours.fetchTours("/resource/osparc/tours/s4l_tours.json")
},
"s4lacad": {
fetchTours: () => osparc.product.tours.Tours.fetchTours("/resource/osparc/tours/s4l_tours.json")
},
"s4llite": {
fetchTours: () => osparc.product.tours.Tours.fetchTours("/resource/osparc/tours/s4llite_tours.json")
},
"tis": {
fetchTours: () => osparc.product.tours.Tours.fetchTours("/resource/osparc/tours/tis_tours.json")
},
Expand Down
Loading
Loading