Processing outside the API — barbican — branch stable/2026.2
Branch
stable/2026.2@de32745a7a4f— repository https://opendev.org/openstack/barbican — analysed on 2026-10-29
| Process | Trigger | Type | Purpose | Reads | Writes / emits | Defined at |
|---|---|---|---|---|---|---|
| barbican-worker | process_type_order | RPC cast (topic barbican.workers) | generate the key(s) of an order | orders, projects, kek_data | secrets, encrypted_data, secret_store_metadata, containers, container_secret, orders status | barbican/queue/server.py:205 |
| barbican-retry | _check_retry_tasks | timer (dynamic, [retry_scheduler] periodic_interval_max_seconds = 10 s ± 20 %, first after initial_delay_seconds = 10 s) | re-enqueue scheduled order tasks | order_retry_tasks where retry_at <= now | RPC cast, delete the retry row | barbican/queue/retry_scheduler.py:80 |
| barbican-keystone-listener | identity.project.deleted | notification (exchange keystone, topic notifications) | remove a deleted project's data | projects | soft delete of the project's rows | barbican/queue/keystone_listener.py:52 |
| barbican-api | start-up | once per process | sync [secretstore:*] to secret_stores (multiple back ends) | secret_stores, project_secret_store | secret_stores | barbican/api/app.py:85, barbican/plugin/util/multiple_backends.py:106 |
| barbican-api | order created and [queue] enable = False | synchronous call | same as the worker, inside the request | as worker | as worker | barbican/queue/client.py:63 |
| barbican-manage | operator | CLI | database clean-up, store sync, KEK management | see § 4 | see § 4 | barbican/cmd/barbican_manage.py:50 |
1. Order processing (barbican-worker)
Trigger: RPC cast process_type_order(order_id, project_id, request_id) from POST /v1/orders (barbican/api/controllers/orders.py:199) or from the retry scheduler. Server: oslo.service Service running an oslo.messaging RPC server, [queue] asynchronous_workers processes (default 1, barbican/cmd/worker.py:52).
Functional purpose: turn a PENDING order into a generated secret (type key) or key pair and container (type asymmetric).
Flow (barbican/queue/server.py:202 decorators monitored → transactional → retryable_order):
BeginTypeOrder.process_and_suppress_exceptions(order_id, project_id)(barbican/tasks/resources.py:45).- Load the order by id and Keystone project id; a missing order is logged (
Could not retrieve information needed to process task) and the exception is suppressed (barbican/tasks/resources.py:87). - Copy the order's
creator_idinto the meta (barbican/tasks/resources.py:265), thenplugin.generate_secretorplugin.generate_asymmetric_secret(barbican/plugin/resources.py:163,barbican/plugin/resources.py:191), which choose a store withgenerate_supports, generate through it and save the rows. - Success: order
ACTIVE,secret_idorcontainer_idset (barbican/tasks/resources.py:197). Failure: orderERROR,error_status_code,error_reason(barbican/tasks/resources.py:189). retryable_orderlooks at the returnedFollowOnProcessingStatusDTO; key and asymmetric orders always returnNO_ACTION_REQUIRED, so no retry is ever scheduled on this branch (barbican/tasks/resources.py:260,barbican/queue/server.py:156).transactionalcommits, or rolls back on an unexpected exception (barbican/queue/server.py:86).
Concurrency and idempotency: no lock. A redelivered cast would generate a second secret and overwrite order.secret_id [inferred: no status check before generation, barbican/tasks/resources.py:245]. RPC casts are not redelivered by oslo.messaging after acknowledgement, so this only happens with manual replays.
Errors: see 02 § 4. Cost: a handful of queries per order plus the crypto work (RSA 4096 generation can take seconds).
2. Retry scheduler (barbican-retry)
Trigger: self.tg.add_dynamic_timer(self._check_retry_tasks, initial_delay=…, periodic_interval_max=…) (barbican/queue/retry_scheduler.py:65); the callback returns the next delay, random in [0.8, 1.2] × periodic_interval_max_seconds (barbican/queue/retry_scheduler.py:35).
Data read: SELECT * FROM order_retry_tasks WHERE retry_at <= :now AND deleted = 0 ORDER BY created_at (all rows, paging defaults apply) (barbican/queue/retry_scheduler.py:116, barbican/model/repositories.py:1228).
Flow: for each task, call TaskClient.<retry_task>(*retry_args, **retry_kwargs) (an RPC cast), then delete the row and commit; on error roll back and leave the row (barbican/queue/retry_scheduler.py:128).
Concurrency: none — several barbican-retry processes would enqueue the same task twice. Upstream deploys one. Effective use on this branch: none (see § 1 step 5); the process can be omitted unless a third-party plugin returns retries.
3. Keystone listener (barbican-keystone-listener)
Trigger: oslo.messaging notification listener on Target(exchange='keystone', topic='notifications', fanout=True) (barbican/queue/__init__.py:88), pool [keystone_notifications] pool_name, thread_pool_size threads (default 10). Starts only if [keystone_notifications] enable = True (barbican/cmd/keystone_listener.py:52).
Emitter: Keystone with [oslo_messaging_notifications] driver = messagingv2 and its notification_format = basic (the payload must carry resource_info) [inferred from barbican/queue/keystone_listener.py:129].
Flow (barbican/queue/keystone_listener.py:52):
- Parse
event_type(identity.project.deleted→ resourceproject, operationdeleted,barbican/queue/keystone_listener.py:106) andpayload['resource_info']. - Other events → return
None(acknowledged). KeystoneEventConsumer.processin its own transaction: find the Barbican project; if absent, log and return (barbican/tasks/keystone_consumer.py:115); elsedelete_all_project_resources(project.id): soft-delete containers, secrets (with their children), KEKs, then the project row (barbican/model/repositories.py:274).- Return
HANDLED; on failureREQUEUE(ifallow_requeue) orHANDLED.
Idempotency: a redelivered event finds the project already soft-deleted and does nothing. Gaps: external-store objects (Vault, KMIP, HSM) are left behind; consumers, orders, ACLs and quotas rows are not deleted [inferred from the four repositories called in barbican/model/repositories.py:283]; the error log path is broken (02 § 4).
4. Maintenance commands (barbican-manage)
| Command | Effect | Defined at |
|---|---|---|
db upgrade [-v head], db revision, db history, db current | Alembic | barbican/cmd/barbican_manage.py:99–barbican/cmd/barbican_manage.py:141 |
db clean [-m min_days] [-p] [-e] [-V] [-L log] | hard-delete soft-deleted rows older than min_days (children first, batched), -p remove projects without secrets/containers/orders/KEKs, -e soft-delete expired secrets first | barbican/cmd/barbican_manage.py:74, barbican/model/clean.py:458 |
db sync_secret_stores | write [secretstore:*] to secret_stores (also done by the API at start) | barbican/cmd/barbican_manage.py:157, barbican/model/sync.py:26 |
hsm gen_mkek, check_mkek, gen_hmac, check_hmac | create / verify the HSM master keys | barbican/cmd/barbican_manage.py:191–barbican/cmd/barbican_manage.py:287 |
hsm rewrap_pkek [--dry-run] | re-wrap every project KEK with the current MKEK/HMAC key | barbican/cmd/barbican_manage.py:314 |
simple_crypto rewrap_pkek [--dry-run] | re-encrypt every kek_data.plugin_meta with the first master KEK (MultiFernet.rotate) | barbican/cmd/barbican_manage.py:361, barbican/cmd/simple_crypto.py:90 |
simple_crypto new_pkek --project | deactivate the project's KEK and create a new unbound one | barbican/cmd/simple_crypto.py:40 |
simple_crypto rewrap_secrets --project | re-encrypt all the project's non-deleted secrets with its active KEK | barbican/cmd/simple_crypto.py:51 |
barbican-status upgrade check | oslo.upgradecheck (policy file checks) | barbican/cmd/status.py:22 |
clean is the only path that removes ciphertexts of deleted secrets; until it runs they remain decryptable with the master key (07 § 3.6).
Source: reverse-spec-barbican/05-async-processing_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.