vcswatch reports that
this package seems to have a new changelog entry (version
0.0.3, distribution
UNRELEASED) and new commits
in its VCS. You should consider whether it's time to make
an upload.
Here are the relevant commit messages:
commit 2639aa2feb2dee4e21a7fc84109ea9f349f75255
Author: Thomas Goirand <zigo@debian.org>
Date: Mon Oct 12 10:34:28 2026 +0200
Select the eventlet backend on the Epoxy oslo.service
The Epoxy-era oslo.service (4.1.x) ships the backend system with
only the eventlet backend: init_backend(THREADING) raised
ValueError("Unknown backend: 'threading'"), which broke the test
discovery (Ran 0 tests) and the worker daemon startup on Epoxy,
on both Bookworm and Trixie.
Fall back to the eventlet backend of the backend system when the
threading one raises ValueError: the worker monkey-patches the
standard library early, like on the pre-backend releases, while the
test process selects it without patching, like on Zed.
commit b0cd775d80b8ca7483bf4902d9fbb72b6d09bcd0
Author: Thomas Goirand <zigo@debian.org>
Date: Mon Oct 12 09:34:03 2026 +0200
Relax the requirements floors for Bullseye/Xena
The floors were written for the Zed-era stacks: on plain Bullseye
(Xena's distro) eight of them exceed the shipped versions, making
the Depends of the built package uninstallable. Relax them to the
Bullseye levels: Flask 1.1, oslo.config 8.3, oslo.context 3.1,
oslo.db 8.4, oslo.middleware 4.1, oslo.service 2.4, oslo.utils 4.6,
openstacksdk 0.50.0, alembic 1.4, sqlalchemy 1.3.
The missing-body test of the service update endpoint now tolerates
the flask 1.1 message: 1.1 hands the handler a None body instead of
raising while parsing it, and the 400 answer carries the other
error string of the same client error.
commit c76d9cc1f6303c173a2ba2d1fd86cade01ad425a
Author: Thomas Goirand <zigo@debian.org>
Date: Mon Oct 12 09:33:57 2026 +0200
Poll for the decision locks on tooz < 3
tooz 2.7.1 (Bullseye) acquire() takes no timeout argument: the
TypeError was swallowed by the graceful degradation and every
terminal state write happened without the decision lock. On those
releases, poll with acquire(blocking=False) until the decision
timeout instead; the blocking=True form would wait forever.
commit d2363dfd080930cd0b4cc606f27aeac28d947ac0
Author: Thomas Goirand <zigo@debian.org>
Date: Mon Oct 12 09:33:57 2026 +0200
Import declarative_base from its 1.3 location
SQLAlchemy only moved declarative_base to sqlalchemy.orm with 1.4:
on 1.3 the import broke every module reaching the models, test
discovery included. Fall back to the declarative extension.
commit 58d27f8fa30b87e72b20e5e9fa6b0cec089d235e
Author: Thomas Goirand <zigo@debian.org>
Date: Mon Oct 12 09:33:53 2026 +0200
Make the enginefacade sessions transactional
oslo.db before 13 defaults its enginefacade sessions to autocommit:
on SQLAlchemy < 1.4 (e.g. Bullseye's 1.3.22) commit() then raises
"No transaction is begun" after a bare add() or a session.execute(),
in the test helpers and in the production paths alike. Configure the
maker's '__autocommit' option to False: honored by the old releases
(the option key is private there, a plain 'autocommit' would be
silently ignored), and warned-and-ignored by oslo.db >= 13, whose
sessions begin transactions on demand anyway, so SQLAlchemy 2.0
never sees the option.
commit 117cd003495bfde0961ae653c9f908881d459268
Author: Thomas Goirand <zigo@debian.org>
Date: Mon Oct 12 08:54:28 2026 +0200
Relax PBR version to 2.0.0, otherwise can't build in Xena.
commit d009af726d8ee0dac9bcc184cf8ee608eb86999b
Author: Thomas Goirand <zigo@debian.org>
Date: Fri Oct 9 00:25:09 2026 +0200
salsa-ci.yml
commit aeceb2d0eea7fb57ba4906aae97a45f5caa7b0e3
Author: Thomas Goirand <zigo@debian.org>
Date: Thu Oct 8 01:02:02 2026 +0200
Record the coverage campaign lessons in the skill
Add the stestr subprocess coverage recipe (coverage.pth hook,
parallel data files, and the rm -f .coverage* trap), the FakeClock
100 s step gotcha which expires short wait deadlines before their
first iteration, the fetch_json seeding pattern for the discovery
engine tests, the source-aware download field checks of the schema,
and the cp -a destination nesting trap which collects a suite
twice.
commit d79881c9e591f3be6924562e71e12fa42824e5ec
Author: Thomas Goirand <zigo@debian.org>
Date: Thu Oct 8 00:19:12 2026 +0200
Cover the worker edge paths
The worker daemon ran its failure and cleanup branches untested: the
exact paths which execute when an infrastructure problem is already
happening, and which the abort and force-abort machinery relies on.
15 new tests document the behavior:
- force_abort_task swallows a database error with a warning and a
rollback, leaving the row for the next report;
- the decision lock releases survive a failing lock release and a
failing coordination session close, on the manager's task purge
too; the periodic purge and the stale task sweep survive database
errors the same way;
- report_task_operation only warns when the operation write fails;
- _write_terminal_state swallows a rollback which itself fails and
reports the write as not done;
- wait_for_free_slot abandons a task whose row vanished inside the
decision section, and survives a failing coordination close;
- the mutual exclusion decorator refuses an unknown worker group at
decorate time, and lets the dispatch of a deleted task return
without touching anything; init_host reaches the parent hook.
worker/manager.py and worker/tasks/base.py are now 100% covered.
commit 3d1e96fc54edbe22aaa43ac3778d44b1814d5427
Author: Thomas Goirand <zigo@debian.org>
Date: Thu Oct 8 00:10:56 2026 +0200
Cover the validate-compute and validate-cleanup error paths
The two validation endpoints had their happy paths and batch
rejections tested, but not their upstream failures. 11 new tests
document the behavior:
- validate-compute refuses a non-boolean keep_enabled (400), fails
with a generic 500 on a Keystone error or when the admin project
disappeared, and maps IntegrityError and DisconnectionError of
the task insert to their specific JSON errors;
- validate-cleanup fails with a generic 500 on a Keystone error or
an absent admin project, answers 503 when no worker is free of
validations, maps the insert IntegrityError, and on a messaging
timeout or error answers 503 and cleans the dangling task row
up.
api.py goes from 89% to 93% covered.
commit 8624a6b96da26502f83930d216263dccd95a7c87
Author: Thomas Goirand <zigo@debian.org>
Date: Thu Oct 8 00:08:25 2026 +0200
Guard the rollbacks of the validate and purge handlers
Let the validate-compute, validate-cleanup and purge-project
handlers serve their generic JSON error even when the database
session cannot be opened.
Their except branches called session.rollback() unguarded, but a
get_db_session() failure leaves session at None: the rollback then
raised an AttributeError inside the handler, and flask answered
with its generic HTML 500 instead of the documented JSON error
body. The rollbacks are now guarded like the ones of the task and
service endpoints.
commit 1e7633063d1c08845f08a01abcbf7a08aeaf203c
Author: Thomas Goirand <zigo@debian.org>
Date: Thu Oct 8 00:03:59 2026 +0200
Cover the task endpoint validation and error paths
The task endpoints answered 400s, 500s and mid-flight races that no
test ever exercised. 19 new tests document the behavior:
- tasks-delete refuses a missing or empty task_ids field, a
non-string or non-UUID id and an unparseable body (all 400),
fails closed with a generic 500 on a database error, and honors
the shell-convenience string 'true'/'false' force values;
- tasks-abort refuses a non-string id and an unparseable body,
reports a RUNNING task whose row carries no worker host, reports
a task deleted while waiting for the decision lock (simulated on
a mocked session, like the finished-while-waiting case), and
fails closed on a database error;
- tasks-list keeps only the tasks matching a valid state filter and
refuses an unknown state;
- task show refuses a non-UUID id, answers 404 for an unknown task
and fails closed on a database error.
api.py goes from 85% to 89% covered.
commit f881034765c573970f40f39b52a859438de2061e
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 23:58:45 2026 +0200
Make the service update answer 400 on a bad body
Let the service update endpoint answer 400 instead of 500 on an
unparseable request body, like every other endpoint.
update_service() lumped the body parsing into the same try/except as
the database work, so a body flask could not parse fell into the
generic 500 branch. The parse now has its own try/except returning
400, matching the purge_project() and services_delete() patterns.
Still fail-closed: no state change, no error detail in the response.
commit bc08573d72c4c9190b713d83c09f1578708ec6aa
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 23:18:16 2026 +0200
Cover the services list, delete and update endpoints
The services endpoints only had the no-leak and auth-flow tests:
the validation branches, the state transitions and the 404 paths
were untested. 21 new tests document the behavior:
- services-list shows the host, disabled flag and reason of every
registered service, and an empty registry lists nothing;
- services-delete removes services, and refuses a missing or empty
service_ids field, non-string or non-UUID ids (400), unknown ids
(404, deleting nothing when mixed with known ones), and fails
closed with a generic 500 on a database error;
- service update flips the enabled state (disabled = not enabled),
records the reason, accepts an omitted enabled field, and refuses
a null body, a non-UUID id, an unknown service, a non-boolean
enabled, a non-string reason and an empty reason when disabling.
A body flask cannot parse fails closed with the generic 500 instead
of a 400; documented as-is.
commit 753daf373ebb3d801d7ee1b655a0029444629866
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 23:03:20 2026 +0200
Extend the vigie skill with the abort and tempest knowledge
Capture what the task abort/force work and the new tempest plugin
taught: the stop registry + decision_lock/finish_task machinery and
the non-reentrant tooz lock trap, the tasks-abort/tasks-delete API
shapes and the request-order lesson, the full Vigie API contract
(auth, policy, endpoints), the four-repo layout, and the tempest
plugin mechanics (scenario manager usage from plugins, per-class
dynamic credentials, the RestClient catalog bypass, openstacksdk
stack facts, stestr skipped-setUpClass reporting, schroot
verification flow).
commit 542acfa493535ee419b883ed2c59ce574df1c94e
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 21:38:26 2026 +0200
Let the API force-delete running tasks
POST /v2/tasks-delete accepts a 'force' option: when it is given,
the RUNNING 409 refusal is skipped and the rows are deleted even
when their task is still running. This is the escape hatch for a
task whose worker died before the abort machinery existed: the
row is gone, and the task list stops showing a run nobody can
abort.
No worker change is needed: a live worker notices the deletion
by itself at its next operation report - the reporter raises a
TaskAborted when the row is gone - and unwinds cleanly, its
finish_task() finding nothing left to write. Force-deleting
therefore stops a live task at its next step boundary, but not
in the middle of a wait: force-aborting remains the way to stop
a task promptly.
commit d8e5ca39cd91d43cf20fa6f3ad33fb156c7dd3ad
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 21:35:41 2026 +0200
Let the API force-abort running tasks
POST /v2/tasks-abort accepts a 'force' option. A force abort
still calls the worker - the API never touches the rows itself -
but casts the new force_abort_task RPC instead of stop_task: the
worker marks the row aborted even when it holds no thread for
the task anymore, which is the case of a worker which restarted
while its task was running.
The choreography is unchanged: the whole request is still refused
unless every task is RUNNING, the decision lock of each worker
group is still held while the RUNNING state is re-verified and
the casts are sent, and a failed cast is reported per task with
the row left untouched. The response message says 'Force-aborted
N task(s).' when the force option was given.
commit a76bd59a38a71aa63f177b6d43fc02f52a930009
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 21:33:49 2026 +0200
Make the tasks-abort rows follow the request order
The SQL rows of the tasks-abort endpoint came back in no
particular order, so the aborted and error lists - which the CLI
shows one row per task - could come out shuffled compared to the
order the operator asked for. Each worker group now handles its
rows sorted by their position in the request. Spotted as a
flaky test ordering on Bullseye+Zed, where sqlite happens to
return different row orders than on unstable.
commit 31b45c05de049d0fbc2c852c58d5141602a0024f
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 21:33:46 2026 +0200
Let the workers force-abort a task
When a worker restarted while a task was running, its task row
stays RUNNING forever: the new worker process holds no thread for
it, the in-memory stop registry died with the old process, and
the stale-task watchdog only covers the compute validations.
The new WorkerManager.force_abort_task(ctxt, task_id) RPC is the
worker side of the API force abort: it records the stop request
(harmless when the task is not live in this process, and the
right thing when it actually is: the task aborts at its next
boundary, converging with the direct write on the same terminal
values), then marks the row ERROR with 'The task was aborted.'
and the operation 'Aborted task.' - through finish_task() and its
group decision lock for the task types which have a worker group,
and through the extracted _write_terminal_state() without a lock
for the ones which do not (tooz locks are not reentrant, so the
write must never be nested under an already-held lock).
A task deleted while being force-aborted is ignored, and its stop
request is not left behind in the registry.
commit d5b781a7a184053502e5590a425c5fec97be4be9
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 18:20:21 2026 +0200
Add POST /v2/tasks-abort, refuse to delete running tasks
Deleting a task which is still running would leave its worker
executing it with the row gone under its feet. The tasks-delete
endpoint therefore refuses the whole request with a 409, listing
the running IDs, when any requested task is RUNNING: nothing is
deleted. The operator aborts the running tasks first.
The new POST /v2/tasks-abort endpoint (policy vigie:tasks_abort)
asks the workers to stop the running tasks:
- the request is validated like tasks-delete (task_ids list of
UUIDs), then refused with a 404 when a task is unknown, or a
409 when any of them is not RUNNING: a task which does not run
cannot be aborted, it can be deleted;
- the tasks are grouped per worker group, and for each group the
cluster-wide decision lock is held while the RUNNING state is
re-verified and the stop_task casts are sent: the choreography
cannot interleave with a decision of the workers, nor with
their terminal state writes, which hold the same lock;
- the casts go through _cast_stop_task(), which targets the
deterministic topic of the worker holding the task and carries
nothing but the task ID;
- a cast failure, a task finished while waiting for the lock and
a task deleted mid-flight are reported per task in the 202
response, next to the accepted aborts;
- when the coordination backend is unavailable, the stop requests
are still sent (fail-open), so that an infrastructure problem
never leaves a task unabortable. Task types without a worker
group are aborted without a lock, for the same reason.
The API never touches the rows: the workers mark the aborted
tasks themselves, which the task list shows as ERROR with the
operation 'Aborted task.'.
commit 4af316efce6f6fa405744a544977cc89c6b844a0
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 18:16:34 2026 +0200
Let the workers stop a running task
WorkerManager.stop_task(ctxt, task_id) is the new RPC method the
API will cast to: it records the request in an in-memory stop
registry of the worker process.
The operation reporter (report_task_operation, the callback given
to vigietools) becomes the first abort point of a running task:
it raises TaskAborted when a stop was requested for the task, or
when its row is gone from the database. Database problems still
never abort the task in progress. The task methods hand an
abort_check (the registry lookup) to vigietools as well, so that
every step, resource and wait boundary of the purge, of the
validations and of the clean-ups stops too.
The abort unwinds into the task methods, which mark the task
ERROR with 'The task was aborted.' and the operation 'Aborted
task.' through the new finish_task() helper, and forget the stop
request in all cases.
All the terminal state writes (DONE and ERROR alike) now go
through finish_task(): it re-reads the row (the session may hold
a stale copy), writes under the cluster-wide decision lock of
the worker group, so that a decision of another worker cannot
count a task as RUNNING while it flips to DONE, and fails open
when the coordination backend is unavailable, exactly like the
decisions themselves. A failing write never raises: the RPC
method returns with its book-keeping done as well as the
database allowed.
commit c97563bccb8d537fbd9f87d620bb8e30cf5d6c13
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 16:06:33 2026 +0200
Fix d/changelog.
commit 514601c92a43ba891184e56f8fb0d711441b9c2b
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 16:03:48 2026 +0200
Keep eventlet out of the unit test process
On oslo.service releases which ship the backend system (>= 4.1.0),
importing oslo_service without an initialized backend falls back to
its eventlet implementation: the test process then imports the
eventlet loopingcall and friends, deprecation-warns, and loads the
green modules. Select the threading backend at the very top of
vigie.tests.base instead, like the production daemons do in
vigie.cmd.worker, so that the tests run on the same backend as the
daemons and eventlet stays unused.
Note that a bare eventlet import cannot be avoided, since futurist
(a dependency of oslo.service) imports it unconditionally: the
regression tests therefore assert the use, not the import — the
threading backend is selected, and the standard library of the test
process is never monkey-patched.
On Zed there is no backend module and eventlet remains the only
backend: the selection is a no-op there (ImportError path), the
tests skip, and the Zed monkey-patch taint stays handled by the
existing shield in vigie.tests.cmd.test_worker.
commit 5fa34838d2e51d8540c16d7872bc7fb9055e3c6b
Author: Thomas Goirand <zigo@debian.org>
Date: Wed Oct 7 15:32:12 2026 +0200
Add project purging as a queued Vigie task
POST /v2/purge-project now accepts a batch of projects (names or
IDs) plus the vigietools purge options (delete_project,
purge_gnocchi_resources, force, tmp_user_role). Every project is
resolved before anything is scheduled: an unknown project or the
admin project rejects the whole request. Otherwise one PROJECT_PURGE
task per project is created on a single selected alive worker, and
each task is cast to it with the purge options; a messaging failure
cleans up the remaining tasks like for the validations.
The worker runs the purges through vigietools, reporting every step
in the operation column of the task, and marks the task DONE or
ERROR with the error message of the purge. Unlike the compute
validations, purges are not mutually exclusive: up to
[tasks]/project_purge_max_executors of them may run at the same
time, cluster-wide, the additional ones being queued in the tasks
table by the mutual exclusion decorator, whose decision now counts
the running tasks of the group against the configured number of
slots (evaluated at each decision, so raising the number of
executors lets the queued purges start without restarting the
workers).
The stub-endpoints and auth-flow test expectations are updated for
the now implemented endpoint.
commit 60203163cfa265b5b000c0b74dc80b36e2dcf5d2
Author: Thomas Goirand <zigo@debian.org>
Date: Tue Oct 6 08:39:49 2026 +0200
Add a Vigie skill for opencode.
Capture the project knowledge and the lessons learned so far in an
opencode project skill (.opencode/skills/vigie/SKILL.md): the
architecture map, the get_config() argv trap, the cfg.ConfigOpts
annotation rule, the file-driver tooz helper, the Zed eventlet
monkey-patch taint, the build-time test wiring, the schroot
verification recipes (egg_info, git safe.directory, detached jobs
with short polls), the Debian packaging notes and the PoC debugging
pointer. It follows the conventions of the sibling vgt and oci
skills.