fix: Kubernetes serialises an empty item list as null, not []
ci / lint (push) Successful in 30s
ci / types (push) Successful in 36s
ci / unit (push) Successful in 25s
ci / security (push) Successful in 39s
ci / dockerfile (push) Successful in 6s
ci / chart (push) Successful in 8s
ci / integration (push) Successful in 39s
ci / image (api) (push) Successful in 1m43s
ci / image (reconciler) (push) Successful in 2m7s
ci / image (worker) (push) Successful in 2m0s
ci / bump (push) Successful in 21s
ci / lint (push) Successful in 30s
ci / types (push) Successful in 36s
ci / unit (push) Successful in 25s
ci / security (push) Successful in 39s
ci / dockerfile (push) Successful in 6s
ci / chart (push) Successful in 8s
ci / integration (push) Successful in 39s
ci / image (api) (push) Successful in 1m43s
ci / image (reconciler) (push) Successful in 2m7s
ci / image (worker) (push) Successful in 2m0s
ci / bump (push) Successful in 21s
The drift check failed on every tick with:
TypeError: 'NoneType' object is not iterable
`resp.json().get("items", [])` cannot defend against this. The key IS present,
with the value null, so the default never fires. An empty PartialObjectMetadataList
comes back as `"items": null`, which is exactly what happens once the release
selector matches nothing — the normal state of a cluster with no tenant
releases provisioned yet.
`or []` handles both shapes.
The tests could not have caught this: all of them passed a real empty list,
which is the one shape that works. The new test sends `"items": null` as the
API server actually sends it, and is control-tested — restoring the old
expression fails it with the same TypeError seen in production.
Worth recording why this survived review. The API read was verified against a
live cluster before shipping, but that cluster had 25 matching release secrets,
so the empty case never ran. A measurement on real data proved the fast path
worked and said nothing about the path taken when there is no data.
This commit is contained in:
@@ -379,7 +379,11 @@ class HelmProvisioner:
|
||||
},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
items: Any = resp.json().get("items", [])
|
||||
# `or []`, not `.get("items", [])`. Kubernetes serialises an empty list as
|
||||
# `"items": null`, so the key is present and the default never fires. This
|
||||
# shipped and failed in production on the first tick that matched no
|
||||
# releases: TypeError: 'NoneType' object is not iterable.
|
||||
items: Any = resp.json().get("items") or []
|
||||
except (httpx.HTTPError, json.JSONDecodeError) as exc:
|
||||
# Raise, do not fall back to helm. The fallback is for "there is no
|
||||
# ServiceAccount here", which is a fact about the environment and is known
|
||||
|
||||
Reference in New Issue
Block a user