OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Clients

python-barbicanclient — Summary

Reverse-specification of python-barbicanclient — branch stable/2026.2

Branch stable/2026.2 @ 8162006e8677 (7.6.0-2-g8162006, 2 maintenance commits after tag 7.6.0) — repository https://opendev.org/openstack/python-barbicanclient — analysed on 2026-11-01

  • Scope: the whole repository: the openstack plugin, the Python API under it, and the legacy barbican shell. The change history (step 7) was not requested, so there is no 06 file.
  • Dependencies read at fixed versions: python-openstackclient 10.3.1, osc-lib 4.7.0, cliff 4.16.0, keystoneauth1 5.17.0. Cited with the prefixes openstackclient:, osc-lib:, cliff:, keystoneauth1:.
  • Other repositories read, not run: barbican: @ de32745a7a4f, keystonemiddleware: @ 3a8a835, castellan: @ bd6bd34, all on stable/2026.2.
  • Markers: references are file:line from the repository root. [observed] means seen by running the real CLI against the local stub on 127.0.0.1. [inferred] means not followed to the end. Unmarked statements were read in the code.
  • Observed behaviour against a stub says nothing about a real service. The stub answers what it was told to. It shows what the client sends, and how it reads a given answer. No real Barbican, Keystone, lab or cloud was contacted.

Files of this specification

FileContent
01-cli-commands_stable-2026.2.mdshared mechanics, functional description per family, the 23 commands with class, arguments and observed requests, the Python API behind them, the legacy shell
02-errors_stable-2026.2.mdexit codes, HTTP translation, exception classes, client-side messages with raise sites, and the defects with their reproduction
03-interactions_stable-2026.2.mdKeystone, Barbican discovery and API, headers, sample frames, local files
04-scenarios_stable-2026.2.md6 scenarios with sequence diagrams and diagnostics
05-async-processing_stable-2026.2.mdnone; what the client leaves to the user
07-rewrite-pack_stable-2026.2.mdclient contract: what the client requires from a key manager, vectors, proof and limits
_verification_stable-2026.2.mdoutput of the reference checker

Overview

python-barbicanclient provides three things:

  1. a Python library, barbicanclient.client.Client (barbicanclient/client.py:150), which castellan and others use;
  2. a plugin of the openstack command with 23 commands on secrets, consumers, orders, containers, CAs and ACLs (setup.cfg:35);
  3. a deprecated stand-alone shell, barbican, running the same 23 command classes (setup.cfg:30).

Every command is a thin layer on the library. A process authenticates, negotiates a microversion through two discovery requests, sends one to about a dozen REST calls, prints a table and exits. There is no database, bus, cache, server or background thread.

Architecture

flowchart LR
  user[User or script] -->|argv, env| osc[openstack process]
  user -->|argv, env| legacy[barbican process, deprecated]
  osc -->|entry points| cmds[barbicanclient.barbican_cli.v1]
  legacy -->|entry points| cmds
  cmds --> lib[barbicanclient.v1 managers]
  other[castellan and other Python callers] --> lib
  lib --> http[_HTTPClient, keystoneauth Adapter]
  http -->|HTTPS, X-Auth-Token, OpenStack-API-Version| kms[Barbican]
  http -->|POST /v3/auth/tokens| ks[Keystone]
  cmds <-->|--file| fs[local files]

Processes

ProcessEntry pointRoleListens to / consumes
openstackplugin key_manager, setup.cfg:33, barbicanclient/osc_plugin.py:26runs one of the 23 commandsargv, environment; listens on nothing
barbicansetup.cfg:30, barbicanclient/barbican.py:380deprecated shell, same commands, with its own authentication options and a no-auth modeargv, environment; listens on nothing

External dependencies

SystemProtocolLibraryConfigurationUsage
KeystoneHTTPS RESTkeystoneauth1--os-auth-url, clouds.yaml for OSC; --os-* for barbicantoken and catalog
BarbicanHTTPS RESTkeystoneauth1 Adapter (barbicanclient/client.py:34)catalog, service type key-manager, interface public (barbicanclient/client.py:28); --endpoint for barbican onlyeverything else

Key figures

ItemCount
Commands23 in one group openstack.key_manager.v1, 78 arguments, 0 import failures
Probe runs on the stub54 per mode (23 base runs, 31 option variants), 3 modes
Commands that sent a request23 of 23 in each mode
Operations used32: 25 observed on the wire, 7 only reachable from the Python API
Operations with no route in the service3: GET /cas, GET /cas/{id}, POST /secrets/{id}/metadata/{key} (read from barbican:, not run)
Service operations never called26, listed in the surface file
Exception classes9
Unit tests254 pass, 2 identical runs; 213 hold at least one request, 384 requests. 0 of the 23 command classes run in them.
Defect reproductions86 cases, 1 process each, in defect-repro
Background processingnone

Points of attention

Details and reproductions are in 02 § 4.

  1. **An error body without a top-level title hides the error [observed]**: the client prints 'title' and loses the status (barbicanclient/client.py:141). The body keystonemiddleware sends on a 401 has this form (keystonemiddleware:keystonemiddleware/auth_token/__init__.py:682), so a rejected token should read this way on a real deployment [inferred, the body was read, not captured].
  2. **Most discovery failures read Invalid client version v1; must be one of: v1 [observed]**, because the factory catches every KeyError and ValueError (barbicanclient/client.py:215). That includes a version document without max_version and a server range the client does not cover.
  3. A new field in a response breaks the client [observed]: secrets, consumers and orders are built with **response (barbicanclient/v1/secrets.py:679, barbicanclient/v1/orders.py:359). A key-manager rewrite cannot add fields.
  4. A secret without a name costs about 10 GET per display [observed]: the lazy test is "no name yet", so every property re-fetches (barbicanclient/v1/secrets.py:468). secret get sent 10 requests, and secret list with 2 unnamed secrets sent 21.
  5. **secret store --name shows Status None and Created None [observed]**, for the same reason in reverse: a named secret is never reloaded.
  6. **secret store --payload x stores application/octet-stream in base64, not text [observed]**, and the help says --payload-content-type is required with --payload (barbicanclient/barbican_cli/v1/secrets.py:165, barbicanclient/barbican_cli/v1/secrets.py:196).
  7. **--debug writes secret payloads to stderr [observed]**, on secret store and secret get --payload, despite the masking in the library's own debug line (barbicanclient/v1/secrets.py:389, keystoneauth1:keystoneauth1/session.py:602).
  8. **secret container create --secret foo silently drops the secret [observed]** and creates an empty container (barbicanclient/barbican_cli/v1/containers.py:138).
  9. **-f json fails on RSA and certificate containers [observed]**, after a create has already succeeded when it is secret container create (barbicanclient/v1/containers.py:294, barbicanclient/v1/containers.py:415).
  10. **ca get and ca list have no route in Barbican stable/2026.2** (barbican:barbican/api/controllers/versions.py:141). They should answer 404 on a real service [inferred].
  11. **add_secret_metadata posts to a route the service refuses** with 405 (barbicanclient/v1/secrets.py:808, barbican:barbican/api/controllers/secrets.py:104). Its 3 unit tests pass against a mock [inferred for the live result, both sides were read].
  12. The tests that call the shell pass without running the command [observed]: they ignore the exit code (barbicanclient/tests/keystone_client_fixtures.py:252). So nothing in the unit tests exercises barbican_cli, which is why items 5, 6, 8 and 9 survive.

No command returns a value from command.Command, so the defect of two OSC commands does not exist here: the 7 such classes (barbicanclient/barbican_cli/v1/secrets.py:38, barbicanclient/barbican_cli/v1/secrets.py:106, barbicanclient/barbican_cli/v1/secrets.py:225, barbicanclient/barbican_cli/v1/secrets.py:249, barbicanclient/barbican_cli/v1/orders.py:69, barbicanclient/barbican_cli/v1/containers.py:33, barbicanclient/barbican_cli/v1/acls.py:92) have no return, and all exit 0 in shape mode [observed].

Blind spots / [inferred]

  • No real service was contacted, by instruction. Everything about real answers comes from reading the barbican: and keystonemiddleware: source, and is marked. The shape stub bodies were written from that source.
  • **functionaltests/ was not run**: it needs a live Barbican and Keystone (etc/functional_tests.conf:5).
  • Unit tests prove little about the CLI: see point 12. The vectors record what the Python API sends against mocked answers.
  • Argument coverage: each command ran with its required arguments, and 31 variants added the options that change a request. Not every option combination ran. --os-* session options (--insecure, --os-cacert, --timeout) were not exercised, since the stub has no TLS.
  • Identity v2 in the legacy shell was read, not run: the stub has only v3.
  • Output formatting by cliff was not specified per command. Only the -f json failure was followed.
  • Python API callers other than castellan were not surveyed.
  • Skill scripts: inventory.py and check_refs.py were run from the skill and are not copied; the adapted tools are in _rewrite/.

Source: reverse-spec-python-barbicanclient/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.