Verify image publication without Python and bind local managed package
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
parent
0a05383195
commit
84c78e072c
4 changed files with 121 additions and 8 deletions
|
|
@ -33,17 +33,33 @@
|
|||
"kid"
|
||||
],
|
||||
"proposed_catalog_workload_reference": {
|
||||
"name": "flex-auth",
|
||||
"declaration_ref": "access-engine/tenancy.yaml#/workload_identity",
|
||||
"rapp_id": "rapp-flex-auth-secrets-engine",
|
||||
"name": "flex-auth-secrets-engine",
|
||||
"deployable": "flex-auth-secrets-engine",
|
||||
"declaration_ref": "rapp-flex-auth-secrets-engine/declarations/rapp.yaml",
|
||||
"declaration_commit": "d52f811ed5ec0be27c9c367e71d476125b3228be",
|
||||
"exact_binding": "system:serviceaccount:flex-auth:flex-auth-secrets-engine",
|
||||
"resolution": "unknown until owner custody and managed-package admission; do not mark resolvable merely from source binding"
|
||||
"resolution": "Local source tuple resolves draft. This is an owner-proposed catalog pointer only; actual Platform catalog/custody is unchanged and remains pending admission."
|
||||
},
|
||||
"key_created_or_admitted": false
|
||||
},
|
||||
"managed_package_limit": {
|
||||
"rapp_declaration_ref": null,
|
||||
"status": "Existing managed-deployable migration debt remains unknown under security-zones_v0.1 \u00a76; no matching authoritative rapp declaration was established. Existing source tenancy declaration supplies owner identity binding but cannot substitute for managed-package admission.",
|
||||
"no_fiction": "Do not invent a rapp tuple or describe this Helm-managed Deployment as an independent operational unit to bypass the rule."
|
||||
"rapp_declaration_ref": "rapp-flex-auth-secrets-engine/declarations/rapp.yaml",
|
||||
"status": "Canonical local package declaration now exists at exact recorded commit/hash. Repo Manager resolves the exact tuple/deployable under /home/worsch with ownership_repo access-engine and readiness draft. Existing unrelated rapp-forgejo declaration_missing remains visible. This source resolution does not claim central registration, private source access/placement, reef admission, signer custody, native deployment or independent consumer trust.",
|
||||
"no_fiction": "Do not invent a rapp tuple or describe this Helm-managed Deployment as an independent operational unit to bypass the rule.",
|
||||
"owner_selected_reference": {
|
||||
"rapp_id": "rapp-flex-auth-secrets-engine",
|
||||
"name": "flex-auth-secrets-engine",
|
||||
"deployable": "flex-auth-secrets-engine",
|
||||
"declaration_ref": "rapp-flex-auth-secrets-engine/declarations/rapp.yaml",
|
||||
"declaration_commit": "d52f811ed5ec0be27c9c367e71d476125b3228be",
|
||||
"declaration_tree": "d4fbad71bdc8ef870603526ccd1f53195d0322af",
|
||||
"declaration_sha256": "0b722431bc5162dd86f72b15f12424e149aca8f2a8790adf2c57e8511ea73b4c",
|
||||
"local_resolver_root": "/home/worsch",
|
||||
"local_resolution": "resolved",
|
||||
"readiness_state": "draft",
|
||||
"scope": "Committed canonical local declaration and exact local source resolution only; no central/private read-placement, reef binding, custody, live deployment or consumer trust admission."
|
||||
}
|
||||
},
|
||||
"unchanged_limits": [
|
||||
"tenancy and zones grades are unchanged",
|
||||
|
|
|
|||
64
tests/test_image_publisher.py
Normal file
64
tests/test_image_publisher.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
"""Execute publisher against a Docker metadata fixture with no Python on PATH."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
REVISION = '0a05383195213e7eaacd08388ce1d7892ce756df'
|
||||
DIGEST = 'sha256:' + 'a'*64
|
||||
DOCKER = '''#!/bin/bash
|
||||
set -eu
|
||||
if [[ "$1" == image && "$2" == inspect ]]; then
|
||||
[[ "${CASE:-}" != inspect_error ]] || exit 29
|
||||
if [[ "$4" == *RepoDigests* ]]; then
|
||||
image=${5%:main-*}
|
||||
case "${CASE:-}" in
|
||||
missing) printf '%s@sha256:%064d\\n' other/repository 0 ;;
|
||||
duplicate) printf '%s@%s\\n%s@%s\\n' "$image" "$DIGEST" "$image" "$DIGEST" ;;
|
||||
malformed) printf '%s@sha256:BAD\\n' "$image" ;;
|
||||
prefix_collision) printf '%s-extra@%s\\n' "$image" "$DIGEST" ;;
|
||||
mismatch) if [[ "$image" == */access-engine ]]; then printf '%s@sha256:%064d\\n' "$image" 0; else printf '%s@%s\\n' "$image" "$DIGEST"; fi ;;
|
||||
*) printf '%s@%s\\n' "$image" "$DIGEST" ;;
|
||||
esac
|
||||
else
|
||||
if [[ "${CASE:-}" == wrong_revision ]]; then printf '%040d\\n' 0; else printf '%s\\n' "$REVISION"; fi
|
||||
fi
|
||||
fi
|
||||
'''
|
||||
|
||||
|
||||
class PublisherMetadataChecks(unittest.TestCase):
|
||||
def invoke(self, case='', mode='legacy'):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
docker = Path(tmp)/'docker'
|
||||
docker.write_text(DOCKER)
|
||||
docker.chmod(0o755)
|
||||
env = {'PATH':tmp, 'CASE':case, 'REVISION':REVISION, 'DIGEST':DIGEST}
|
||||
return subprocess.run(['/bin/bash',str(ROOT/'tools/publish-runtime-images.sh'),
|
||||
REVISION,mode,tmp],env=env,capture_output=True,text=True)
|
||||
|
||||
def test_existing_bash_docker_only_runner_and_exact_receipt(self):
|
||||
for mode,count in [('legacy',1),('paired',2)]:
|
||||
result = self.invoke(mode=mode)
|
||||
self.assertEqual(result.returncode,0,result.stderr)
|
||||
self.assertIn('manifest_digest='+DIGEST,result.stdout)
|
||||
self.assertIn('verified_repositories='+str(count),result.stdout)
|
||||
|
||||
def test_missing_duplicate_malformed_foreign_revision_and_inspect_failure_refuse(self):
|
||||
for case in ('missing','duplicate','malformed','prefix_collision','wrong_revision','inspect_error'):
|
||||
with self.subTest(case=case):
|
||||
result = self.invoke(case)
|
||||
self.assertNotEqual(result.returncode,0)
|
||||
self.assertNotIn('publication mode=',result.stdout)
|
||||
|
||||
def test_existing_paired_digest_equality_is_preserved(self):
|
||||
result = self.invoke('mismatch','paired')
|
||||
self.assertNotEqual(result.returncode,0)
|
||||
self.assertIn('manifest digests differ',result.stderr)
|
||||
self.assertNotIn('publication mode=',result.stdout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -27,8 +27,22 @@ done
|
|||
digests=()
|
||||
for image in "${images[@]}"; do
|
||||
docker pull "$image:main-$short"
|
||||
raw=$(docker image inspect --format '{{json .RepoDigests}}' "$image:main-$short")
|
||||
digest=$(printf '%s' "$raw" | python3 -c 'import json,re,sys; x=json.load(sys.stdin); assert isinstance(x,list); matches=[v.removeprefix(sys.argv[1]+"@") for v in x if isinstance(v,str) and v.startswith(sys.argv[1]+"@")]; assert len(matches)==1 and re.fullmatch(r"sha256:[a-f0-9]{64}",matches[0]); print(matches[0])' "$image")
|
||||
# The existing container-build runner supplies Bash and Docker, not Python.
|
||||
# Capture inspect first so command failure is not hidden by process substitution.
|
||||
raw=$(docker image inspect --format '{{range .RepoDigests}}{{println .}}{{end}}' "$image:main-$short")
|
||||
digest=""
|
||||
matches=0
|
||||
while IFS= read -r reference; do
|
||||
if [[ "$reference" == "$image@"* ]]; then
|
||||
candidate=${reference#"$image@"}
|
||||
[[ "$candidate" =~ ^sha256:[a-f0-9]{64}$ ]] || { echo 'invalid published registry manifest digest' >&2; exit 1; }
|
||||
digest=$candidate
|
||||
matches=$((matches + 1))
|
||||
fi
|
||||
done <<< "$raw"
|
||||
[[ "$matches" == 1 ]] || { echo 'expected exactly one published repository digest' >&2; exit 1; }
|
||||
actual_revision=$(docker image inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' "$image:main-$short")
|
||||
[[ "$actual_revision" == "$revision" ]] || { echo 'published image revision differs from exact source' >&2; exit 1; }
|
||||
digests+=("$digest")
|
||||
done
|
||||
if [[ "$mode" == paired && "${digests[0]}" != "${digests[1]}" ]]; then
|
||||
|
|
|
|||
|
|
@ -283,3 +283,22 @@ process contention/crash release, capacity/prefix/restart and ENOSPC/fsync failu
|
|||
proofs are part of the owner package. No new service, live mount, deletion or
|
||||
custody grant; exact published successor, new PVC/PV retention/managed-package
|
||||
owner disposition and independent consumer admission remain prerequisites.
|
||||
|
||||
2026-10-16 owner package extraction record: canonical local package
|
||||
`rapp-flex-auth-secrets-engine` now exists at commit
|
||||
`d52f811ed5ec0be27c9c367e71d476125b3228be`, tree
|
||||
`d4fbad71bdc8ef870603526ccd1f53195d0322af`; authoritative declaration
|
||||
`declarations/rapp.yaml` SHA256 `0b722431bc5162dd86f72b15f12424e149aca8f2a8790adf2c57e8511ea73b4c`.
|
||||
Repo Manager exact local resolution under `/home/worsch` returns
|
||||
`rapp-flex-auth-secrets-engine` / workload `flex-auth-secrets-engine` /
|
||||
deployable `flex-auth-secrets-engine`, ownership `access-engine`, readiness
|
||||
`draft`. The retained lifecycle PDP is the only rollback boundary claimed;
|
||||
tenant/user/canary and retired `rapp-secrets-engine` remain excluded. Normative
|
||||
focused family validation passes; existing unrelated global rapp-forgejo error
|
||||
and reef projection warning remain visible. Package extraction keeps source
|
||||
`2b064eee510640224274ccdc7cefceb1ae13162e` and current image
|
||||
`sha256:05a03a8790c2210c48ea92391441c77ddf640d0cd32f5ec09838f5393171fcbd`
|
||||
unchanged. Exact local source resolution is no central/private read-placement,
|
||||
reef binding, custody, native rollout or consumer trust admission. Future signer
|
||||
image/config adoption remains separate. T06 remains `progress`; no second work
|
||||
item duplicates this bounded dependency.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue