diff --git a/aiidalab_launch/__main__.py b/aiidalab_launch/__main__.py index 53feaf4..2b1def3 100644 --- a/aiidalab_launch/__main__.py +++ b/aiidalab_launch/__main__.py @@ -398,17 +398,11 @@ async def _async_start( "Try to use '--pull' to pull the image prior to start." ) - # Check if image has changed. - assert instance.image is not None - image_changed = ( - instance.container and instance.container.image.id != instance.image.id - ) - mounts_changed = ( - instance.container - and f"{instance.profile.container_name()}_conda" - not in (mount.get("Name") for mount in instance.container.attrs["Mounts"]) - ) - recreate = image_changed or mounts_changed + # Check if the container configuration has changed. + if instance.container: + configuration_changed = any(instance.configuration_changes()) + else: + configuration_changed = False try: @@ -420,22 +414,21 @@ async def _async_start( InstanceStatus.CREATED, InstanceStatus.EXITED, ): - if recreate: + if configuration_changed: with spinner("Recreating container..."): - instance.remove() - instance.create() + instance.recreate() with spinner("Starting container..."): instance.start() elif status is InstanceStatus.UP and restart: with spinner("Restarting container..."): - if any(instance.configuration_changes()): + if configuration_changed: instance.stop() - instance.remove() + instance.recreate() instance.start() else: instance.restart() elif status is InstanceStatus.UP and not restart: - if any(instance.configuration_changes()): + if configuration_changed: click.secho( "Container is already running, however the configuration " "has changed. A restart with --restart is recommended.", @@ -723,7 +716,7 @@ def reset(app_state, profile, yes): ) click.echo("Removing container and associated (data) volumes.") - instance.remove(data=True) + instance.remove(conda=True, data=True) if __name__ == "__main__": diff --git a/aiidalab_launch/core.py b/aiidalab_launch/core.py index 77b1212..53dc11e 100644 --- a/aiidalab_launch/core.py +++ b/aiidalab_launch/core.py @@ -275,6 +275,13 @@ def configuration_changes(self) -> Generator[str, None, None]: if self.container.image.id != self.image.id: yield "Image has changed." + try: + for mount in self._mounts(): + if docker_mount_for(self.container, mount["Target"]) != mount["Source"]: + raise ValueError + except ValueError: + yield "Mount configuration has changed." + if self.profile != Profile.from_container(self.container): yield "Profile configuration has changed." @@ -308,6 +315,12 @@ def create(self) -> Container: ) return self._container + def recreate(self) -> None: + self._requires_container() + assert self.container is not None + self.remove() + self.create() + def start(self) -> None: self._ensure_home_mount_exists() LOGGER.info(f"Starting container '{self.profile.container_name()}'...") @@ -344,21 +357,22 @@ def stop(self, timeout: float | None = None) -> None: except AttributeError: raise RuntimeError("no container") - def remove(self, data: bool = False) -> None: + def remove(self, conda: bool = False, data: bool = False) -> None: # Remove container if self.container: self.container.remove() self._container = None # Remove conda volume - try: - self.client.volumes.get(self.profile.conda_volume_name()).remove() - except docker.errors.NotFound: # already removed - logging.debug( - f"Failed to remove conda volume '{self.profile.conda_volume_name()}', likely already removed." - ) - except Exception as error: # unexpected error - raise RuntimeError(f"Failed to remove conda volume: {error}") + if conda: + try: + self.client.volumes.get(self.profile.conda_volume_name()).remove() + except docker.errors.NotFound: # already removed + logging.debug( + f"Failed to remove conda volume '{self.profile.conda_volume_name()}', likely already removed." + ) + except Exception as error: # unexpected error + raise RuntimeError(f"Failed to remove conda volume: {error}") if data and self.profile.home_mount: # Remove home volume diff --git a/tests/test_cli.py b/tests/test_cli.py index 5e99137..ac8d6f2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -216,7 +216,7 @@ def assert_status_down(): assert_status_down() assert get_volume(instance.profile.home_mount) - assert not get_volume(instance.profile.conda_volume_name()) + assert get_volume(instance.profile.conda_volume_name()) # Reset instance. result: Result = runner.invoke(