diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4190681..9ab4120 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,12 +26,15 @@ jobs: needs: [pre-commit] runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 30 strategy: fail-fast: false matrix: python-version: ['3.8', '3.9', '3.10'] + default-image: + - '' # use the application default + - aiidalab/aiidalab-docker-stack:latest steps: @@ -52,7 +55,7 @@ jobs: - name: Run tests run: | - pytest -v --slow + pytest -v --slow --default-image=${{ matrix.default-image }} coverage xml - name: Upload coverage to Codecov diff --git a/aiidalab_launch/instance.py b/aiidalab_launch/instance.py index fd225b7..16d8aa6 100644 --- a/aiidalab_launch/instance.py +++ b/aiidalab_launch/instance.py @@ -214,7 +214,8 @@ def _run_post_start(self) -> None: LOGGER.warning( "Failed to ensure ~/.conda directory is owned by the system user." ) - LOGGER.debug("The ~/.conda directory is owned by the system user.") + else: + LOGGER.debug("The ~/.conda directory is owned by the system user.") def stop(self, timeout: float | None = None) -> None: self._requires_container() @@ -270,7 +271,7 @@ def exec_create(self, cmd: str, privileged: bool = False) -> str: return self.client.api.exec_create( self.container.id, cmd, - user=None if privileged else self.profile.system_user, + user="root" if privileged else self.profile.system_user, workdir=None if privileged else f"/home/{self.profile.system_user}", )["Id"] except docker.errors.APIError: @@ -291,7 +292,9 @@ async def _init_scripts_finished(container: Container) -> None: result = await loop.run_in_executor( None, container.exec_run, "wait-for-services" ) - if result.exit_code != 0: + if result.exit_code in (126, 127): + LOGGER.debug("Container does not support wait-for-services script.") + elif result.exit_code != 0: raise FailedToWaitForServices( "Failed to check for init processes to complete." ) diff --git a/aiidalab_launch/profile.py b/aiidalab_launch/profile.py index 7a4aa05..985f81e 100644 --- a/aiidalab_launch/profile.py +++ b/aiidalab_launch/profile.py @@ -10,6 +10,7 @@ import toml from docker.models.containers import Container +from .core import LOGGER from .util import docker_mount_for, get_docker_env, is_volume_readonly MAIN_PROFILE_NAME = "default" @@ -30,7 +31,7 @@ def _default_port() -> int: # explicit function required to enable test patchin return DEFAULT_PORT -_DEFAULT_IMAGE = "aiidalab/aiidalab-docker-stack:latest" +_DEFAULT_IMAGE = "aiidalab/full-stack:latest" def _valid_volume_name(source: str) -> None: @@ -65,7 +66,7 @@ class Profile: name: str = MAIN_PROFILE_NAME port: int | None = field(default_factory=_default_port) default_apps: list[str] = field(default_factory=lambda: ["aiidalab-widgets-base"]) - system_user: str = "aiida" + system_user: str = "jovyan" image: str = _DEFAULT_IMAGE home_mount: str | None = None extra_mounts: set[str] = field(default_factory=set) @@ -93,6 +94,14 @@ def __post_init__(self): self.extra_mounts.remove(extra_mount) self.extra_mounts.add(f"{extra_mount}:rw") + if ( + self.image.split(":")[0] == "aiidalab/full-stack" + and self.system_user != "jovyan" + ): + LOGGER.warning( + "Resetting the system user may create issues for this image!" + ) + def container_name(self) -> str: return f"{CONTAINER_PREFIX}{self.name}" @@ -127,6 +136,7 @@ def environment(self, jupyter_token: str) -> dict: "AIIDALAB_DEFAULT_APPS": " ".join(self.default_apps), "JUPYTER_TOKEN": str(jupyter_token), "SYSTEM_USER": self.system_user, + "NB_USER": self.system_user, } def dumps(self) -> str: diff --git a/tests/conftest.py b/tests/conftest.py index 5327532..7eca9e1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -57,6 +57,16 @@ def docker_client(): pytest.skip("docker not available") +@pytest.fixture(autouse=True) +def _select_default_image(monkeypatch_session, pytestconfig): + _default_image = pytestconfig.getoption("default_image") + if _default_image is not None: + monkeypatch_session.setattr( + aiidalab_launch.profile, "_DEFAULT_IMAGE", _default_image + ) + yield None + + @pytest.fixture(scope="session", autouse=True) def _pull_docker_image(docker_client): try: @@ -250,6 +260,11 @@ def pytest_addoption(parser): default=False, help="Enable long running tests.", ) + parser.addoption( + "--default-image", + action="store", + help="Select the default image to test against.", + ) def pytest_configure(config): diff --git a/tests/test_cli.py b/tests/test_cli.py index 36c155d..0e4c00f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -161,11 +161,11 @@ def test_status(self, started_instance): assert started_instance.profile.home_mount in result.output assert started_instance.url() in result.output - def test_exec(self): + def test_exec(self, started_instance): runner: CliRunner = CliRunner() result: Result = runner.invoke(cli.cli, ["exec", "--", "whoami"]) assert result.exit_code == 0 - assert "aiida" in result.output + assert started_instance.profile.system_user in result.output def test_logs(self): runner: CliRunner = CliRunner() diff --git a/tests/test_config.py b/tests/test_config.py index 8205261..6bb0076 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -13,7 +13,7 @@ port = 8888 default_apps = [ "aiidalab-widgets-base",] system_user = "aiida" - image = "aiidalab/aiidalab-docker-stack:latest" + image = "aiidalab/full-stack:edge" home_mount = "aiidalab_default_home" """ } diff --git a/tests/test_instance.py b/tests/test_instance.py index 16ae263..4bda23e 100644 --- a/tests/test_instance.py +++ b/tests/test_instance.py @@ -122,7 +122,7 @@ async def test_profile_configuration_changes(instance): assert not any(instance.configuration_changes()) # Change image - instance.profile.image = "aiidalab/aiidalab-docker-stack:1234" + instance.profile.image = "aiidalab/full-stack:1234" assert "Profile configuration has changed." in instance.configuration_changes() instance.profile = deepcopy(original_profile) assert not any(instance.configuration_changes()) @@ -159,7 +159,10 @@ def test_instance_host_ports(self, started_instance): def test_instance_exec_create(self, docker_client, started_instance): exec_id = started_instance.exec_create(cmd="whoami") - assert docker_client.api.exec_start(exec_id).decode().strip() == "aiida" + assert ( + docker_client.api.exec_start(exec_id).decode().strip() + == started_instance.profile.system_user + ) exec_id_privileged = started_instance.exec_create(cmd="whoami", privileged=True) assert (