Skip to content

Add kolla drift-detection tool#604

Closed
ideaship wants to merge 12 commits into
mainfrom
drift-detection
Closed

Add kolla drift-detection tool#604
ideaship wants to merge 12 commits into
mainfrom
drift-detection

Conversation

@ideaship

@ideaship ideaship commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What

Adds check-kolla-drift.py, a plugin-based drift detector for the OSISM kolla
container-image toolchain, and wires it into CI. It catches divergence between
the OSISM consumer repos (defaults, release, generics, the container-image
repos) and upstream openstack/kolla / openstack/kolla-ansible when it is
introduced
— before it breaks a deploy or a gate.

The framework lands first (empty registry, flags nothing on its own), then seven
plugins in lifecycle order, each reviewable in isolation:

  • kolla_enablement_orphan — OSISM enables a service upstream no longer defines
  • kolla_orphan_config — dead companion vars / image blocks for an orphaned service
  • kolla_secrets_orphan — OSISM kolla secret with no upstream counterpart
  • kolla_enablement_build — enabled + upstream-buildable service missing from the build set
  • kolla_version_chain_upstream — upstream docker/ service with no version-pin line
  • kolla_version_chain_inner — a referenced version pin that resolves to nothing (inert pin)
  • kolla_inventory — upstream inventory group missing from the OSISM 50/51-kolla files
    (subsumes the retired check-kolla-inventory.py, which is removed)

Sources resolve remotely (GitHub) by default, so the detector runs anywhere
including CI; --base-dir reads local checkouts. An allowlist marks intentional
exceptions (each requiring a reason); allowlist entries that match nothing are
reported stale. Exit codes: 0 clean / 1 drift or stale / 2 input error.

Full reference: docs/check-kolla-drift.md.

What this finds in practice

Run against the live OSISM repos, the detector surfaced a real backlog of drift;
the fixes it pointed to are now merged across the consumer repos:

Across a full offline sweep it took the actionable-drift count from 81 → 3,
and the remaining 3 (the valkey items above) have since merged too. It also
self-audits its allowlist: an entry that no longer suppresses real drift is
reported as a hard error, so suppressions can't silently rot.

CI wiring

  • A kolla-drift tox env runs python3 src/check-kolla-drift.py; a
    generics-tox-kolla-drift Zuul job wraps it and runs in periodic-daily,
    catching drift introduced upstream after a change has merged.

🤖 Generated with Claude Code

ideaship added 9 commits June 29, 2026 15:38
OSISM assembles each container image's version from a chain of separate
repos: openstack/kolla defines the images, container-images-kolla pins
concrete versions, and container-image-kolla-ansible consumes them through
its versions.yml.j2 template. Nothing checks that these stay in agreement,
so a service can fall out of the chain silently and only surface at deploy
or run time.

Add a local-first, plugin-based drift detector to catch that automatically:

  - source: read a file or list a directory from any OSISM repo,
    transparently from a local checkout (--base-dir) or, with no checkout,
    from GitHub raw + API, so checks run offline or online unchanged. Reads
    pinned repos and release-range refs from a local git clone via git
    object reads; resolves a release to its upstream ref (treating 422 as
    an absent ref).
  - model: DriftEntry records one disagreement between an authoritative
    source and a consumer and carries allowlist suppression plus optional
    per-entry summary/remediation overrides.
  - plugin protocol: a check declares the files it reads and returns the
    drift it finds; the driver discovers checks from a registry.
  - driver (check-kolla-drift.py): selects enabled checks (or --plugin),
    runs them, applies the allowlist, renders a grouped, lifecycle-ordered
    text report (or JSON), flags stale allowlist entries as a hard error,
    and exits 0 (clean) / 1 (drift) / 2 (input error) for CI use.
  - config: remote defaults with per-repo source overrides, base_dirs for
    local reads, and releases/release_refs keys for range-aware checks.
  - allowlist: known-intentional exceptions, each requiring a reason.

This lands the infrastructure only; the plugin registry is empty, so it
flags nothing on its own. The kolla checks that use it follow in the next
commits, each reviewable in isolation. Adds a kolla-drift-test tox env and
the pytest/responses test deps.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
Flag OSISM enable_* flags whose service is absent from upstream
kolla-ansible's top-level enable-defaults across all supported
releases (the service was removed or renamed upstream). Orphan =
absent from the union over the supported range; per-release keys
come from enablement.upstream_enable_keys (monolithic
group_vars/all.yml at 2024.1/2024.2/2025.1, split
group_vars/all/*.yml at 2025.2), resolved via release_to_ref.
Guard an empty release range, which would otherwise report every
flag as orphan.

The SCOPE selects which OSISM enable flags are eligible. Ship the
"explicit" scope: it flags the dead enable_X: "no" cleanup flags
upstream kolla-ansible no longer defines at any supported release
(freezer, murano, sahara, senlin, solum, vitrage and their horizon
plugins, ironic_pxe_uefi, outward_rabbitmq, skydive), which is
what drives the osism/defaults cleanup. The OSISM-invented common
and kolla_operations flags have no upstream counterpart by design
and are kept via the allowlist.

Add the I/O-free enablement parse helpers (parse_enable_flags,
truthy_enables, canon, release_range) and the upstream
enable-defaults resolver. Expose orphan_ids() for reuse by
kolla_orphan_config. Register and enable the plugin.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
kolla_enablement_orphan flags the enable_<service> flag of a service
upstream kolla-ansible no longer defines, but osism/defaults also
carries that service's companion vars (e.g. senlin_api_port,
outward_rabbitmq_port) and image-definition blocks (<service>_tag,
<service>_*_image/_tag). Those are equally dead, yet nothing enumerated
them, so a cleanup that deleted only the enable flag left them dangling.

For each genuinely dead service (an orphan per kolla_enablement_orphan,
excluding the OSISM-invented ones kept via that check's allowlist)
report every top-level var across osism/defaults all/*.yml whose name
is the service id or begins with "<service id>_", grouped by the file
it lives in. The enable flag itself is left to kolla_enablement_orphan
(it never matches the "<service>_" prefix).

Reuse kolla_enablement_orphan.orphan_ids() for the dead-service set and
secrets_map.parse_secret_keys for the top-level key parse; match on the
canon (hyphen/underscore) form. Register it second in the lifecycle
PLUGINS order, right after kolla_enablement_orphan, and enable it in
the default config. A registry test now guards that every registered
plugin is enabled in the default config, so a plugin in PLUGINS but
missing from the config can no longer silently never run.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
Detect OSISM kolla secrets with no upstream counterpart. The enablement
and version-chain checks already reconcile OSISM enable flags and version
pins against upstream, but stale secret vars left behind by removed
services (e.g. outward_rabbitmq_password) went undetected.

For each supported release the plugin compares the top-level keys of the
cfg-cookiecutter kolla secrets template
(environments/kolla/secrets.yml.<release>) against kolla-ansible's
authoritative etc/kolla/passwords.yml at that release's resolved ref. A
key present in the OSISM template but absent upstream is an orphaned
secret. Both sides are release-specific, so the comparison is per
release, not unioned.

Keys are extracted with secrets_map.parse_secret_keys (a line regex, not
a YAML load, because the template values are jinja/cookiecutter
placeholders that do not parse as YAML); canon normalises the
hyphen/underscore split. OSISM-invented secrets with no upstream
counterpart are kept via the allowlist; a release with no OSISM template
is skipped, not an error. Register it next to the sibling
kolla_enablement_orphan and enable it in the default config. The current
real-world baseline is clean across 2024.1-2025.2, so it serves as a
regression gate.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
Flag OSISM-enabled kolla services (defaults all/099-kolla.yml enable_X
truthy) that are buildable upstream at a supported release (a docker/X
dir exists in openstack/kolla at the release's resolved ref) but absent
from that release's OSISM build set (release latest/openstack-R.yml
infrastructure_projects / openstack_projects). The docker/ universe is
the scope filter, so feature flags are out of scope with no alias table.
Range-aware via release_range + release_to_ref.

Wire the defaults and release paths and enable the plugin. The live
baseline is clean today (enable_valkey is not yet in defaults); the
post-valkey gap is exercised by the synthetic fixtures. Add a per-plugin
SUMMARY/REMEDIATION so the report names the file an operator must edit.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
Flag openstack/kolla docker/ services (pinned stable/2025.2) that have
no versions['K'] line in the kolla-ansible template -- unwired pins
that silently default. One-way: producer keys are not folded in, so
blazar and masakari (present in the producer, absent from the template)
still flag.

Pin kolla to openstack/stable/2025.2 via sources, add the docker/
service parser and the versions.yml.j2 template parser, enable the
plugin, and seed the allowlist with the ignore-tier services from the
empirical stable/2025.2-vs-template delta: not-deployed services
(cyborg, tacker, telegraf, zun -- zun has a kolla docker image but
OSISM does not build or deploy it), build base layers (base,
openstack_base), and naming/variant or supporting images with no
distinct versions['K'] key (hacluster, letsencrypt,
networking_baremetal, ovsdpdk, httpd, mariadb_server). Every seed
entry maps to a real delta item so none is stale; valkey/blazar/
masakari are intentionally left to flag.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
A versions['K'] key the kolla-ansible template references but
container-images-kolla's SBOM_IMAGE_TO_VERSION map never emits
resolves to no value, so the template line silently falls back to
openstack_version. The service looks pinned but is not -- an inert
pin no error or log reveals, typically noticed only when the wrong
version ships.

Compare the two key spaces and nothing else: add the versions.yml.j2
template parser and the SBOM_IMAGE_TO_VERSION producer parser,
normalise hyphen vs underscore, and deliberately ignore
output-variable and image names (not keys; treating them as aliases
would mask real drift).

Classify each inert pin and route it to the right fix. A pin is an
ADD only when OSISM actually deploys a built image (enable_X truthy
in defaults AND a docker/X dir exists upstream): then the missing
SBOM key should be wired. Otherwise the template line is dead and
should be removed -- in the kolla-ansible template repo, not
container-images-kolla. DriftEntry's optional summary/remediation
carry the two distinct hints, and the add and remove buckets render
as separate report blocks, each naming the file to edit. Register
and enable the plugin.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
Re-home the retired check-kolla-inventory.py as a framework plugin: flag
ansible inventory groups in upstream kolla-ansible multinode (pinned
stable/2025.2) that are absent from the OSISM 50/51-kolla inventory in the
generics repo. One-way; each flagged group's upstream members ride in
`expected` so a maintainer knows what to add.

Add the ansible INI inventory group parser (configparser, as the original
script used), pin kolla_ansible to openstack/stable/2025.2, add the generics
path, enable the plugin, and seed the allowlist with the empirical delta: 7
exact base-infra groups the OSISM overlay omits (compute, control, network,
storage, monitoring, deployment, baremetal:children) plus 4 prefix
not-deployed services (cyborg, tacker, telegraf, collectd, prefix covering
their sub-groups). All 16 current delta items are suppressed and none is
stale; the original's 8 dead IGNORE prefixes are dropped.

The standalone script was never wired into CI or any tox env -- it was run
by hand during inventory maintenance -- and this plugin fully subsumes it,
so remove it.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Roger Luethi <luethi@osism.tech>
The check-kolla-drift.py detector existed in the repo but nothing ran
it: the kolla-drift-test tox env only exercises the unit tests, never
the detector against the live repos. Drift it is meant to catch (an
OSISM enable flag, version pin, or inventory group diverging from
upstream openstack/kolla and kolla-ansible) would therefore go
unnoticed until a deploy or gate broke.

Wire the detector into Zuul's periodic-daily pipeline:

  - add a kolla-drift tox env that runs `python3 src/check-kolla-drift.py`
    with no args, so every repo is read remotely from GitHub (no
    checkout needed) and the configured allowlist applies. The script
    exits 0 when clean, 1 on actionable drift or a stale allowlist
    entry, and 2 on an input error, which maps directly onto a CI gate.
  - add a generics-tox-kolla-drift job wrapping that env.
  - run the job in periodic-daily, which catches drift introduced
    upstream after a change merged, independent of any PR.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Roger Luethi <luethi@osism.tech>
The repo's yamllint Zuul job (check and periodic-daily pipelines) flagged
26 errors in the two drift config files: "too many spaces after colon" in
kolla-drift-config.yml and "too many spaces after comma" in
kolla-drift-allowlist.yml. Both stem from column-aligning the values for
readability, which exceeds yamllint's one-space cap on the colons and
commas rules. The files had never been linted before, so the violations
surfaced only once the branch reached CI.

Collapse the alignment padding to a single space after each colon and
comma, and add the `---` document-start header to both files. This is a
whitespace-only change: the parsed YAML (plugin enable flags and allowlist
entries) is identical, and the unit suite still passes (146 tests).

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Roger Luethi <luethi@osism.tech>
@ideaship ideaship force-pushed the drift-detection branch 2 times, most recently from 53cdb66 to 34b3207 Compare June 29, 2026 16:15
The drift detector shipped with per-commit slices of
docs/check-kolla-drift.md spread across the framework and plugin commits.
Those slices had drifted out of sync with the code: two of the seven
plugins (kolla_orphan_config, kolla_secrets_orphan) were never documented
at all, the "Adding a plugin" guide listed the wrong plugin protocol
(omitting the required SUMMARY/REMEDIATION names and showing the obsolete
allowlist.match/as_allowlisted idiom instead of allowlist.apply), and
several passages referenced removed concepts (the dropped paths.* config,
a "deferred" orphan signal, and a docs/findings/ file that does not exist
in this repo). Plugin sections also followed no common template and were
ordered inconsistently.

The slices were dropped from the earlier commits; this commit adds one
authoritative document in their place. It presents all seven plugins in
lifecycle order (enabled -> built -> version-pinned -> deployed, matching
the report) under a uniform template -- description, --plugin command,
the inputs it reads, and the fix/allowlist remediation -- and documents
the framework: running it, remote/--base-dir input resolution, per-repo
source overrides, the allowlist (fields, exact/prefix match, stale
detection), and an accurate "Adding a plugin" guide. Time-sensitive
"as of writing it flags X" examples were removed so the doc does not go
stale as the consumer repos are reconciled.

Documentation only; no code or test change.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Roger Luethi <luethi@osism.tech>
@ideaship ideaship marked this pull request as ready for review June 29, 2026 16:46
The kolla-drift-test tox environment runs the pytest unit suite for
the drift-detection code (tests/kolla_drift/, 146 tests, hermetic
fixture-based, no network). It was defined in tox.ini but referenced
by no Zuul job, so the unit tests never ran in CI.

Add a generics-tox-kolla-drift-test job and wire it into the check
pipeline so the unit tests gate every PR, plus periodic-daily for
parity with the other jobs. The existing generics-tox-kolla-drift job
(the live drift detector) stays periodic-only, which is correct: it
inspects live upstream state and is not suitable as a PR gate.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Roger Luethi <luethi@osism.tech>
@berendt berendt self-requested a review June 29, 2026 19:57
@ideaship ideaship marked this pull request as draft June 30, 2026 14:03
@ideaship ideaship removed the request for review from berendt July 1, 2026 04:25
@berendt berendt moved this from New to In progress in Human Board Jul 1, 2026
@ideaship

ideaship commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Replaced by osism/release#2561

@ideaship ideaship closed this Jul 2, 2026
@github-project-automation github-project-automation Bot moved this from In progress to Done in Human Board Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants