Pass the composed release index into the weekly assessment.
The in-cluster runner and the request file both load config/release-index.json, so a claimed workload's recommendation names the unit id and the declaration owner.
This commit is contained in:
parent
c0227108d2
commit
82b2024727
5 changed files with 573 additions and 10 deletions
488
config/release-index.json
Normal file
488
config/release-index.json
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
{
|
||||
"schema": "release-registry.index.v1",
|
||||
"units": [
|
||||
{
|
||||
"declaration": {
|
||||
"composition": {
|
||||
"member_repos": [
|
||||
{
|
||||
"deployables": [
|
||||
"flex-auth-secrets-engine"
|
||||
],
|
||||
"repo": "access-engine",
|
||||
"role": "application source"
|
||||
}
|
||||
],
|
||||
"purpose": "Decides secrets-engine policy for one flex-auth consumer."
|
||||
},
|
||||
"contract_version": "1.0.0",
|
||||
"criticality": "high",
|
||||
"data_classification": "restricted",
|
||||
"kind": "release-unit",
|
||||
"owner": "access-engine",
|
||||
"pins": {
|
||||
"component": {
|
||||
"name": "ocm.railiance.dev/flex-auth-secrets-engine",
|
||||
"version": "0.2.0"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:05a03a8790c2210c48ea92391441c77ddf640d0cd32f5ec09838f5393171fcbd"
|
||||
},
|
||||
"name": "flex-auth",
|
||||
"type": "ociImage",
|
||||
"version": "0.2.0"
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-1",
|
||||
"normalisationAlgorithm": "gitCommit/v1",
|
||||
"value": "0344b2a962e4ed977ea622b08cf20bf49d25b603"
|
||||
},
|
||||
"name": "access-engine",
|
||||
"type": "git",
|
||||
"version": "0.2.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rollback_contract": {
|
||||
"order": [
|
||||
"helm-rollback"
|
||||
]
|
||||
},
|
||||
"rollout_contract": {
|
||||
"default_mode": "helm-upgrade-install"
|
||||
},
|
||||
"smoke_contract": {
|
||||
"required": [
|
||||
"http-ready"
|
||||
]
|
||||
},
|
||||
"unit_id": "flex-auth-secrets-engine",
|
||||
"workloads": [
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "flex-auth-secrets-engine",
|
||||
"namespace": "flex-auth"
|
||||
}
|
||||
]
|
||||
},
|
||||
"owner": "access-engine",
|
||||
"path": "/home/worsch/access-engine/registry/release-units/flex-auth-secrets-engine.yaml",
|
||||
"source": "dir:/home/worsch/access-engine",
|
||||
"unit_id": "flex-auth-secrets-engine"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"composition": {
|
||||
"member_repos": [
|
||||
{
|
||||
"deployables": [
|
||||
"forge",
|
||||
"forgejo-runner"
|
||||
],
|
||||
"repo": "railiance-apps",
|
||||
"role": "helm values and runner manifest"
|
||||
},
|
||||
{
|
||||
"deployables": [
|
||||
"forge",
|
||||
"forgejo-runner"
|
||||
],
|
||||
"repo": "railiance-forge",
|
||||
"role": "forge operating contracts"
|
||||
}
|
||||
],
|
||||
"purpose": "Serves Forgejo git, packages, and the in-cluster Actions runner as one rollout."
|
||||
},
|
||||
"contract_version": "1.0.0",
|
||||
"criticality": "critical",
|
||||
"data_classification": "restricted",
|
||||
"kind": "release-unit",
|
||||
"owner": "railiance-apps",
|
||||
"pins": {
|
||||
"component": {
|
||||
"name": "ocm.railiance.dev/forgejo",
|
||||
"version": "11.0.3"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba"
|
||||
},
|
||||
"name": "forgejo",
|
||||
"type": "ociImage",
|
||||
"version": "11.0.3"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:5071e6832313bafe71577e05631bece88caff08fcfb372193e4a21941f4ed54b"
|
||||
},
|
||||
"name": "forgejo-runner",
|
||||
"type": "ociImage",
|
||||
"version": "11.0.3"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:aa3df78ecf320f5fafdce71c659f1629e96e9de0968305fe1de670e0ca9176ce"
|
||||
},
|
||||
"name": "docker",
|
||||
"type": "ociImage",
|
||||
"version": "11.0.3"
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-1",
|
||||
"normalisationAlgorithm": "gitCommit/v1",
|
||||
"value": "9ecb123451717a4d70ec50d490a228c30ea5ba88"
|
||||
},
|
||||
"name": "railiance-apps",
|
||||
"type": "git",
|
||||
"version": "11.0.3"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-1",
|
||||
"normalisationAlgorithm": "gitCommit/v1",
|
||||
"value": "2afdff68b1e22c48c4e7d5f20457f6cc420cb25c"
|
||||
},
|
||||
"name": "railiance-forge",
|
||||
"type": "git",
|
||||
"version": "11.0.3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rollback_contract": {
|
||||
"order": [
|
||||
"helm-rollback"
|
||||
]
|
||||
},
|
||||
"rollout_contract": {
|
||||
"default_mode": "helm-upgrade-install"
|
||||
},
|
||||
"smoke_contract": {
|
||||
"required": [
|
||||
"http-ready"
|
||||
]
|
||||
},
|
||||
"unit_id": "forgejo",
|
||||
"workloads": [
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "forge",
|
||||
"namespace": "forgejo"
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "forgejo-runner",
|
||||
"namespace": "forgejo"
|
||||
}
|
||||
]
|
||||
},
|
||||
"owner": "railiance-apps",
|
||||
"path": "/home/worsch/railiance-apps/registry/release-units/forgejo.yaml",
|
||||
"source": "dir:/home/worsch/railiance-apps",
|
||||
"unit_id": "forgejo"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"composition": {
|
||||
"member_repos": [
|
||||
{
|
||||
"deployables": [
|
||||
"knative"
|
||||
],
|
||||
"repo": "rail-knative",
|
||||
"role": "substrate declaration"
|
||||
},
|
||||
{
|
||||
"deployables": [
|
||||
"knative"
|
||||
],
|
||||
"repo": "railiance-cluster",
|
||||
"role": "cluster install"
|
||||
}
|
||||
],
|
||||
"purpose": "Serves Knative workloads through the shared Serving and Kourier substrate.",
|
||||
"upstream_components": [
|
||||
{
|
||||
"name": "knative-serving",
|
||||
"source": "gcr.io/knative-releases",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
{
|
||||
"name": "net-kourier",
|
||||
"source": "gcr.io/knative-releases",
|
||||
"version": "1.22.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"contract_version": "1.0.0",
|
||||
"criticality": "critical",
|
||||
"data_classification": "internal",
|
||||
"kind": "release-unit",
|
||||
"owner": "rail-knative",
|
||||
"pins": {
|
||||
"component": {
|
||||
"name": "ocm.railiance.dev/knative",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:8f7e9df2642a8ba715ebc55b62abee17b0984b6b6af0c6ec00dabaadf5f8cccb"
|
||||
},
|
||||
"name": "activator",
|
||||
"type": "ociImage",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:ec66d97244395b57a67923d4a8c5a95f0264805f1fdb2ee0cdb49ec0f3bd7855"
|
||||
},
|
||||
"name": "autoscaler",
|
||||
"type": "ociImage",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:6833f2c83e9f356c274ddb2de40e5471ad2b1c08882c9bd11f5afa240e86d574"
|
||||
},
|
||||
"name": "controller",
|
||||
"type": "ociImage",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:c505888f7ee34ccadc1b9a6877d6d49691f633ea28cf8cbe03fcfa6d3afcd584"
|
||||
},
|
||||
"name": "webhook",
|
||||
"type": "ociImage",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:a2e8f568a4bf68ef6391bc3afea956d11a8698f6a22c4be50844b1da4f8bcc74"
|
||||
},
|
||||
"name": "net-kourier-controller",
|
||||
"type": "ociImage",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:1c2b79776c6e3b38e8b0113b825e6a599f9bfc08d680c199d80bf8964856c529"
|
||||
},
|
||||
"name": "kourier-gateway",
|
||||
"type": "ociImage",
|
||||
"version": "1.22.0"
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-1",
|
||||
"normalisationAlgorithm": "gitCommit/v1",
|
||||
"value": "affe67a4bc0fbdde9460edd8208cb8f895a23bbd"
|
||||
},
|
||||
"name": "rail-knative",
|
||||
"type": "git",
|
||||
"version": "1.22.0"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-1",
|
||||
"normalisationAlgorithm": "gitCommit/v1",
|
||||
"value": "a6742635824a7568cdae4aebdd743a6457c20e33"
|
||||
},
|
||||
"name": "railiance-cluster",
|
||||
"type": "git",
|
||||
"version": "1.22.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rollback_contract": {
|
||||
"order": [
|
||||
"restore-previous-overlay"
|
||||
]
|
||||
},
|
||||
"rollout_contract": {
|
||||
"default_mode": "kustomize-overlay"
|
||||
},
|
||||
"smoke_contract": {
|
||||
"required": [
|
||||
"deployment-available"
|
||||
]
|
||||
},
|
||||
"unit_id": "knative",
|
||||
"workloads": [
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "activator",
|
||||
"namespace": "knative-serving"
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "autoscaler",
|
||||
"namespace": "knative-serving"
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "controller",
|
||||
"namespace": "knative-serving"
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "webhook",
|
||||
"namespace": "knative-serving"
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "net-kourier-controller",
|
||||
"namespace": "knative-serving"
|
||||
},
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "3scale-kourier-gateway",
|
||||
"namespace": "kourier-system"
|
||||
}
|
||||
]
|
||||
},
|
||||
"owner": "rail-knative",
|
||||
"path": "/home/worsch/rail-knative/registry/release-units/knative.yaml",
|
||||
"source": "dir:/home/worsch/rail-knative",
|
||||
"unit_id": "knative"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"composition": {
|
||||
"member_repos": [
|
||||
{
|
||||
"deployables": [
|
||||
"vergabe"
|
||||
],
|
||||
"repo": "railiance-apps",
|
||||
"role": "rollout owner"
|
||||
},
|
||||
{
|
||||
"deployables": [
|
||||
"vergabe-teilnahme"
|
||||
],
|
||||
"repo": "vergabe-teilnahme",
|
||||
"role": "application source"
|
||||
}
|
||||
],
|
||||
"purpose": "Serves the Vergabe participation application for one company."
|
||||
},
|
||||
"contract_version": "1.0.0",
|
||||
"criticality": "high",
|
||||
"data_classification": "confidential",
|
||||
"depends_on": [
|
||||
{
|
||||
"readiness": "available",
|
||||
"unit_id": "vergabe-db",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"kind": "release-unit",
|
||||
"owner": "railiance-apps",
|
||||
"pins": {
|
||||
"component": {
|
||||
"name": "ocm.railiance.dev/vergabe",
|
||||
"version": "0.2.1"
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"componentName": "vergabe-db",
|
||||
"name": "database",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-256",
|
||||
"normalisationAlgorithm": "ociArtifactDigest/v1",
|
||||
"value": "sha256:a26444f59c259698159c69ccb96f73dc648a261ece4c86bb2037a9d977870d91"
|
||||
},
|
||||
"name": "vergabe-teilnahme",
|
||||
"type": "ociImage",
|
||||
"version": "0.2.1"
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-1",
|
||||
"normalisationAlgorithm": "gitCommit/v1",
|
||||
"value": "e9d2b5f39a6fe1c4accb5c54e3ba0b37d05b23ee"
|
||||
},
|
||||
"name": "railiance-apps",
|
||||
"type": "git",
|
||||
"version": "0.2.1"
|
||||
},
|
||||
{
|
||||
"digest": {
|
||||
"hashAlgorithm": "SHA-1",
|
||||
"normalisationAlgorithm": "gitCommit/v1",
|
||||
"value": "dca6683a8ab7d4aa0de2a537924f73318b199f60"
|
||||
},
|
||||
"name": "vergabe-teilnahme",
|
||||
"type": "git",
|
||||
"version": "0.2.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rollback_contract": {
|
||||
"order": [
|
||||
"helm-rollback"
|
||||
]
|
||||
},
|
||||
"rollout_contract": {
|
||||
"default_mode": "helm-upgrade-install"
|
||||
},
|
||||
"smoke_contract": {
|
||||
"required": [
|
||||
"http-ready"
|
||||
]
|
||||
},
|
||||
"unit_id": "vergabe",
|
||||
"workloads": [
|
||||
{
|
||||
"kind": "Deployment",
|
||||
"name": "vergabe-teilnahme",
|
||||
"namespace": "vergabe-demo-company"
|
||||
}
|
||||
]
|
||||
},
|
||||
"owner": "railiance-apps",
|
||||
"path": "/home/worsch/railiance-apps/registry/release-units/vergabe.yaml",
|
||||
"source": "dir:/home/worsch/railiance-apps",
|
||||
"unit_id": "vergabe"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
"provenance": null,
|
||||
"previous": null,
|
||||
"reviews": null,
|
||||
"release_index": "release-index.json",
|
||||
"as_of": null,
|
||||
"output": null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,16 @@ def run(args, *, now: datetime, get, query) -> dict:
|
|||
"weekly_allocation_incluster": Path(__file__).resolve(),
|
||||
"queries": args.queries, "ownership": args.ownership, "inventory": args.inventory,
|
||||
}
|
||||
release_index_path = getattr(args, "release_index", None)
|
||||
if release_index_path is None:
|
||||
raise ValueError("release index must be bound")
|
||||
release_index_path = Path(release_index_path)
|
||||
sources["release_index"] = release_index_path
|
||||
release_index = json.loads(release_index_path.read_text())
|
||||
if not isinstance(release_index, dict) or (
|
||||
"units" not in release_index and "findings" not in release_index
|
||||
):
|
||||
raise ValueError("release index is not a composed index or coverage report")
|
||||
weekly = load(sources["weekly_allocation_report"], "weekly_allocation_report")
|
||||
reconcile = load(sources["reconcile_railiance_allocation"], "reconcile_railiance_allocation")
|
||||
observer = load(sources["observe_cluster_resources"], "observe_cluster_resources")
|
||||
|
|
@ -280,7 +290,8 @@ def run(args, *, now: datetime, get, query) -> dict:
|
|||
report = weekly.build_report(
|
||||
**inputs, as_of=iso(now), max_age_hours=args.max_age_hours,
|
||||
previous=json.loads(previous_path.read_text()) if previous_path else None,
|
||||
reviews=json.loads(args.reviews.read_text()) if args.reviews and args.reviews.exists() else None)
|
||||
reviews=json.loads(args.reviews.read_text()) if args.reviews and args.reviews.exists() else None,
|
||||
release_index=release_index)
|
||||
weekly.retain(target, report)
|
||||
failed = sorted(n for n, q in telemetry["queries"].items() if q["response"].get("status") != "success")
|
||||
return {**receipt, "status": "retained", "report_sha256": sha256(target),
|
||||
|
|
@ -302,6 +313,8 @@ def parser() -> argparse.ArgumentParser:
|
|||
p.add_argument("--ownership", type=Path, default=env("ASSESSMENT_OWNERSHIP") and Path(env("ASSESSMENT_OWNERSHIP")))
|
||||
p.add_argument("--inventory", type=Path, default=env("ASSESSMENT_INVENTORY") and Path(env("ASSESSMENT_INVENTORY")))
|
||||
p.add_argument("--reviews", type=Path, default=env("ASSESSMENT_REVIEWS") and Path(env("ASSESSMENT_REVIEWS")))
|
||||
p.add_argument("--release-index", type=Path, default=Path(env(
|
||||
"ASSESSMENT_RELEASE_INDEX", str(HERE.parent / "config/release-index.json"))))
|
||||
p.add_argument("--prometheus-url", default=env("PROMETHEUS_URL", "http://telemetry-prometheus.telemetry.svc:9090"))
|
||||
p.add_argument("--hub-url", default=env("STATE_HUB_URL"), help="State Hub API; unset disables delivery")
|
||||
p.add_argument("--executor", default=env("ASSESSMENT_EXECUTOR", "unspecified"))
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import math
|
|||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
SCHEMA = "custodian.weekly-allocation.v1"
|
||||
|
|
@ -95,14 +94,35 @@ def recommendation_id(resource, namespace, workload):
|
|||
|
||||
|
||||
def release_lookup(index, *, namespace, name, label=None):
|
||||
"""Call the release-registry claim lookup. A missing index assigns nothing."""
|
||||
if not index:
|
||||
"""Unit id and declaration owner for one workload.
|
||||
|
||||
A composed index is the authority: one claim on namespace and name.
|
||||
``label`` is ``railiance.io/release-unit`` and is not used. A coverage
|
||||
report has no declaration owner, so findings alone assign neither.
|
||||
The same rule lives in release_registry.ownership.lookup_release. This
|
||||
copy stays in the assessment so the slim in-cluster image can run it.
|
||||
"""
|
||||
del label
|
||||
matches = []
|
||||
for unit in (index or {}).get("units") or []:
|
||||
if not isinstance(unit, dict):
|
||||
continue
|
||||
declaration = unit.get("declaration") or {}
|
||||
if not isinstance(declaration, dict):
|
||||
continue
|
||||
unit_id = str(unit.get("unit_id") or declaration.get("unit_id") or "")
|
||||
owner = declaration.get("owner")
|
||||
if not unit_id or not isinstance(owner, str) or not owner:
|
||||
continue
|
||||
for claim in declaration.get("workloads") or []:
|
||||
if not isinstance(claim, dict):
|
||||
continue
|
||||
if str(claim.get("namespace") or "") == namespace and str(claim.get("name") or "") == name:
|
||||
matches.append((unit_id, owner))
|
||||
if len(matches) != 1:
|
||||
return {"unit_id": None, "owner": None}
|
||||
registry = Path(__file__).resolve().parents[1].parent / "release-registry"
|
||||
if str(registry) not in sys.path:
|
||||
sys.path.insert(0, str(registry))
|
||||
from release_registry.ownership import lookup_release
|
||||
return lookup_release(index, namespace=namespace, name=name, label=label)
|
||||
unit_id, owner = matches[0]
|
||||
return {"unit_id": unit_id, "owner": owner}
|
||||
|
||||
|
||||
def build_report(cluster, allocation, telemetry, inventory, provenance, *, as_of,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ class InClusterRunnerTests(unittest.TestCase):
|
|||
self.args = argparse.Namespace(
|
||||
data_dir=self.data, source_dir=ROOT / "scripts", observer=base / "observer.py",
|
||||
queries=ROOT / "config/weekly-allocation-queries.json", ownership=base / "ownership.json",
|
||||
inventory=base / "inventory.json", reviews=None, prometheus_url="http://prom.test:9090",
|
||||
inventory=base / "inventory.json", reviews=None,
|
||||
release_index=ROOT / "config/release-index.json",
|
||||
prometheus_url="http://prom.test:9090",
|
||||
executor="test", max_age_hours=48.0)
|
||||
|
||||
def tearDown(self):
|
||||
|
|
@ -89,6 +91,45 @@ class InClusterRunnerTests(unittest.TestCase):
|
|||
self.assertEqual(incluster.missed_weeks("2026-W52", "2027-W01"), ["2026-W53"]) # 2026 has 53 ISO weeks
|
||||
self.assertEqual(incluster.missed_weeks("2026-W53", "2027-W01"), [])
|
||||
|
||||
def test_claimed_workload_names_unit_and_declaration_owner(self):
|
||||
observer = (Path(self.tmp.name) / "observer.py").read_text()
|
||||
(Path(self.tmp.name) / "observer.py").write_text(observer.replace(
|
||||
'"workload": p["metadata"]["labels"]["app"]',
|
||||
'"workload": p["metadata"]["labels"]["app"], "labels": p["metadata"].get("labels") or {}',
|
||||
))
|
||||
self.get = self._claimed_get
|
||||
(Path(self.tmp.name) / "ownership.json").write_text(json.dumps({
|
||||
"host": {"cluster_resource_id": RESOURCE},
|
||||
"namespaces": {"flex-auth": {"owner": "namespace-map", "service": "flex-auth", "tenant": "unknown"}}}))
|
||||
receipt = self.run_at(datetime(2026, 10, 19, 6, 0, 30, tzinfo=timezone.utc))
|
||||
report = json.loads((self.data / "reports/2026-W43.json").read_text())
|
||||
named = {item["workload"]: item for item in report["recommendations"]}
|
||||
claimed = named["flex-auth-secrets-engine"]
|
||||
self.assertEqual(claimed["release_unit"], "flex-auth-secrets-engine")
|
||||
self.assertEqual(claimed["owner"], "access-engine")
|
||||
self.assertEqual(claimed["release_owner"], "access-engine")
|
||||
self.assertIsNone(named["unclaimed"]["release_unit"])
|
||||
self.assertIsNone(named["labeled"]["release_unit"])
|
||||
self.assertIsNone(named["labeled"]["release_owner"])
|
||||
self.assertEqual(named["labeled"]["owner"], "namespace-map")
|
||||
self.assertEqual(receipt["status"], "retained")
|
||||
|
||||
def _claimed_get(self, path):
|
||||
self.reads.append(path)
|
||||
if path.startswith("/apis/metrics.k8s.io"):
|
||||
if path.endswith("/nodes"):
|
||||
return {"items": [{"metadata": {"name": "railiance01"}, "usage": {"cpu": "1500000000n", "memory": "2048Ki"}}]}
|
||||
return {"items": []}
|
||||
if path == "/api/v1/pods":
|
||||
labeled = pod("flex-auth", "labeled-1", "labeled")
|
||||
labeled["metadata"]["labels"]["railiance.io/release-unit"] = "flex-auth-secrets-engine"
|
||||
return {"items": [
|
||||
pod("flex-auth", "flex-auth-secrets-engine-1", "flex-auth-secrets-engine"),
|
||||
pod("flex-auth", "other-1", "unclaimed"),
|
||||
labeled,
|
||||
]}
|
||||
return {"items": []}
|
||||
|
||||
def test_reads_only_collector_paths_and_retains_report(self):
|
||||
receipt = self.run_at(datetime(2026, 10, 19, 6, 0, 30, tzinfo=timezone.utc))
|
||||
self.assertEqual(receipt["status"], "retained")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue