Skip to content

Commit 6a6f66a

Browse files
agoscinskiGeigerJ2mbercx
authored
verdi presto: auto-start daemon (#7351)
When a broker is configured, `verdi presto` now automatically starts the daemon after profile setup, so users can immediately submit processes without a separate `verdi daemon start`. If the daemon fails to start, a warning is shown and the command exits successfully with a hint to run `verdi daemon start` manually. The autostart can be skipped by setting `AIIDA_NO_DAEMON_AUTOSTART=1`. This is an internal escape hatch for the Docker container, where s6-overlay manages the daemon lifecycle separately (the editable install in `run-before-daemon-start` must complete before the daemon starts). The env var is deliberately omitted from `--help` and the public CLI reference: it serves scripted container/CI setups, not interactive users. Only the literal string `1` is recognized; any other value (`0`, `true`, etc.) falls through to the safe default of "start the daemon" so a misspelled or unrecognized value cannot silently disable the daemon. --------- Co-authored-by: Julian Geiger <julian.geiger@gmx.net> Co-authored-by: Marnik Bercx <mbercx@gmail.com>
1 parent 6adc2b0 commit 6a6f66a

5 files changed

Lines changed: 106 additions & 7 deletions

File tree

.docker/aiida-core-base/s6-assets/init/aiida-prepare.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ verdi config set warnings.development_version False
1818
# If the environment variable `SETUP_DEFAULT_AIIDA_PROFILE` is not set, set it to `true`.
1919
if [[ ${SETUP_DEFAULT_AIIDA_PROFILE:-true} == true ]] && ! verdi profile show ${AIIDA_PROFILE_NAME} &> /dev/null; then
2020

21-
# Create AiiDA profile.
22-
verdi presto \
21+
# Create AiiDA profile. Set AIIDA_NO_DAEMON_AUTOSTART because the
22+
# Docker container manages the daemon lifecycle separately via
23+
# s6-overlay (aiida-daemon-start runs after this script and
24+
# run-before-daemon-start).
25+
AIIDA_NO_DAEMON_AUTOSTART=1 verdi presto \
2326
--verbosity info \
2427
--profile-name "${AIIDA_PROFILE_NAME:-default}" \
2528
--email "${AIIDA_USER_EMAIL:-aiida@localhost}" \

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Behavior changes
6+
7+
**`verdi presto`: auto-starts the daemon** ([#7351](https://github.com/aiidateam/aiida-core/pull/7351))
8+
9+
`verdi presto` now starts the daemon automatically after creating the profile, when a broker is configured.
10+
Scripts that previously ran `verdi daemon start` after `verdi presto` continue to work; the daemon-start invocation is idempotent.
11+
312
## v2.8.0 - 2026-03-16
413

514
This release brings important improvements to the `BaseRestartWorkChain`, the engine, stashing, typing coverage, and dependency updates.

docs/source/reference/command_line.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ Below is a list with all available subcommands.
319319
* Create a default user for the profile (email can be configured through the `--email` option)
320320
* Set up the localhost as a `Computer` and configure it
321321
* Set a number of configuration options with sensible defaults
322+
* Start the daemon (unless `--no-broker` is specified)
322323
323324
By default the command creates a profile that uses SQLite for the database. For the
324325
message broker, it automatically checks for RabbitMQ running on localhost. If found, it

src/aiida/cmdline/commands/cmd_presto.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from __future__ import annotations
1212

13+
import os
1314
import pathlib
1415
import re
1516
import typing as t
@@ -183,6 +184,7 @@ def verdi_presto(
183184
* Create a default user for the profile (email can be configured through the `--email` option)
184185
* Set up the localhost as a `Computer` and configure it
185186
* Set a number of configuration options with sensible defaults
187+
* Start the daemon (unless `--no-broker` is specified)
186188
187189
By default the command creates a profile that uses SQLite for the database. For the message broker, it automatically
188190
checks for RabbitMQ running on localhost. If found, it configures RabbitMQ as the broker. Otherwise, it falls back
@@ -289,3 +291,20 @@ def verdi_presto(
289291
computer.set_default_mpiprocs_per_machine(1)
290292

291293
echo.echo_success('Configured the localhost as a computer.')
294+
295+
# `AIIDA_NO_DAEMON_AUTOSTART` is an internal escape hatch for the Docker init script (see
296+
# `.docker/aiida-core-base/s6-assets/init/aiida-prepare.sh`); deliberately undocumented in `--help`.
297+
if broker_backend is not None and os.environ.get('AIIDA_NO_DAEMON_AUTOSTART') != '1':
298+
from aiida.common.exceptions import ConfigurationError
299+
from aiida.engine.daemon.client import DaemonException, get_daemon_client
300+
301+
echo.echo('Starting the daemon... ', nl=False)
302+
try:
303+
client = get_daemon_client(profile.name)
304+
client.start_daemon()
305+
except (DaemonException, ConfigurationError) as exception:
306+
echo.echo('FAILED', fg=echo.COLORS['error'], bold=True)
307+
echo.echo_warning(f'Failed to start the daemon: {exception}')
308+
echo.echo_report('You can start it manually with `verdi daemon start`.')
309+
else:
310+
echo.echo('OK', fg=echo.COLORS['success'], bold=True)

tests/cmdline/commands/test_presto.py

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests for ``verdi presto``."""
22

33
import textwrap
4+
from unittest.mock import MagicMock
45

56
import pytest
67

@@ -10,6 +11,16 @@
1011
from aiida.orm import Computer
1112

1213

14+
@pytest.fixture()
15+
def mock_daemon_client(monkeypatch):
16+
"""Mock the daemon client so the daemon is not actually started during tests."""
17+
from aiida.engine.daemon import client as daemon_client_mod
18+
19+
mock_client = MagicMock()
20+
monkeypatch.setattr(daemon_client_mod, 'get_daemon_client', lambda *args, **kwargs: mock_client)
21+
return mock_client
22+
23+
1324
@pytest.mark.parametrize(
1425
'profile_names, expected',
1526
(
@@ -30,8 +41,8 @@ def get_profile_names(self):
3041
assert get_default_presto_profile_name() == expected
3142

3243

33-
@pytest.mark.usefixtures('empty_config')
34-
def test_presto_without_rmq(pytestconfig, run_cli_command, monkeypatch):
44+
@pytest.mark.usefixtures('empty_config', 'mock_daemon_client')
45+
def test_presto_without_rmq(run_cli_command, monkeypatch):
3546
"""Test the ``verdi presto`` without RabbitMQ falls back to ZMQ."""
3647
from aiida.brokers.rabbitmq import defaults
3748

@@ -52,7 +63,7 @@ def detect_rabbitmq_config(**kwargs):
5263
assert profile.process_control_backend == 'core.zmq'
5364

5465

55-
@pytest.mark.usefixtures('empty_config')
66+
@pytest.mark.usefixtures('empty_config', 'mock_daemon_client')
5667
def test_presto(run_cli_command):
5768
"""Test that ``verdi presto`` configures a broker (RabbitMQ if available, otherwise ZMQ)."""
5869
result = run_cli_command(verdi_presto, ['--non-interactive'])
@@ -67,7 +78,7 @@ def test_presto(run_cli_command):
6778

6879

6980
@pytest.mark.requires_psql
70-
@pytest.mark.usefixtures('empty_config')
81+
@pytest.mark.usefixtures('empty_config', 'mock_daemon_client')
7182
def test_presto_use_postgres(run_cli_command, manager):
7283
"""Test the ``verdi presto`` with the ``--use-postgres`` flag."""
7384
result = run_cli_command(verdi_presto, ['--non-interactive', '--use-postgres'])
@@ -89,14 +100,70 @@ def test_presto_use_postgres_fail(run_cli_command):
89100
assert 'Failed to connect to the PostgreSQL server' in result.output
90101

91102

92-
@pytest.mark.usefixtures('empty_config')
103+
@pytest.mark.usefixtures('empty_config', 'mock_daemon_client')
93104
def test_presto_overdose(run_cli_command, config_with_profile_factory):
94105
"""Test that ``verdi presto`` still works for users that have over 10 presto profiles."""
95106
config_with_profile_factory(name='presto-10')
96107
result = run_cli_command(verdi_presto)
97108
assert 'Created new profile `presto-11`.' in result.output
98109

99110

111+
@pytest.mark.usefixtures('empty_config')
112+
def test_presto_starts_daemon(run_cli_command, mock_daemon_client):
113+
"""Test that ``verdi presto`` auto-starts the daemon."""
114+
result = run_cli_command(verdi_presto, ['--non-interactive'])
115+
assert 'Starting the daemon' in result.output
116+
mock_daemon_client.start_daemon.assert_called_once_with()
117+
118+
119+
@pytest.mark.usefixtures('empty_config')
120+
def test_presto_no_daemon_autostart_env(run_cli_command, mock_daemon_client, monkeypatch):
121+
"""Test that ``AIIDA_NO_DAEMON_AUTOSTART=1`` skips the daemon auto-start."""
122+
monkeypatch.setenv('AIIDA_NO_DAEMON_AUTOSTART', '1')
123+
result = run_cli_command(verdi_presto, ['--non-interactive'])
124+
assert 'Created new profile' in result.output
125+
assert 'Starting the daemon' not in result.output
126+
mock_daemon_client.start_daemon.assert_not_called()
127+
128+
129+
@pytest.mark.parametrize('value', ('0', 'true', 'false', '', 'banana'))
130+
@pytest.mark.usefixtures('empty_config')
131+
def test_presto_daemon_autostart_env_non_one(run_cli_command, mock_daemon_client, monkeypatch, value):
132+
"""Test that anything other than the literal string ``1`` does not skip the daemon.
133+
134+
Only ``AIIDA_NO_DAEMON_AUTOSTART=1`` is recognized; any other value (including ``0``, ``true``,
135+
typos, or empty string) falls through to "start the daemon" so a misspelled or unrecognized
136+
value doesn't silently disable the daemon.
137+
"""
138+
monkeypatch.setenv('AIIDA_NO_DAEMON_AUTOSTART', value)
139+
result = run_cli_command(verdi_presto, ['--non-interactive'])
140+
assert 'Starting the daemon' in result.output
141+
mock_daemon_client.start_daemon.assert_called_once_with()
142+
143+
144+
@pytest.mark.usefixtures('empty_config')
145+
def test_presto_no_broker_skips_daemon(run_cli_command, mock_daemon_client):
146+
"""Test that ``verdi presto --no-broker`` skips the daemon auto-start (no broker means no daemon)."""
147+
result = run_cli_command(verdi_presto, ['--non-interactive', '--no-broker'])
148+
assert 'Created new profile' in result.output
149+
assert 'Starting the daemon' not in result.output
150+
mock_daemon_client.start_daemon.assert_not_called()
151+
152+
153+
@pytest.mark.usefixtures('empty_config')
154+
def test_presto_daemon_start_failure(run_cli_command, mock_daemon_client):
155+
"""Test that ``verdi presto`` handles daemon start failure gracefully."""
156+
from aiida.engine.daemon.client import DaemonException
157+
158+
mock_daemon_client.start_daemon.side_effect = DaemonException('Could not start daemon')
159+
160+
result = run_cli_command(verdi_presto, ['--non-interactive'])
161+
assert 'Created new profile' in result.output
162+
assert 'FAILED' in result.output
163+
assert 'Could not start daemon' in result.output
164+
assert 'verdi daemon start' in result.output
165+
166+
100167
@pytest.mark.requires_psql
101168
@pytest.mark.usefixtures('empty_config')
102169
def test_presto_profile_name_exists(run_cli_command, config_with_profile_factory):

0 commit comments

Comments
 (0)