Compare commits

..

1 Commits

Author SHA1 Message Date
jenkins
4236538c8f hermes: add daemonless agent image release lane 2026-08-16 23:26:28 -03:00
10 changed files with 28 additions and 472 deletions

View File

@ -9,7 +9,6 @@ metadata:
labels:
atlas.bstein.dev/workload: hermes-agent-image-builder
spec:
serviceAccountName: hermes-image-builder
automountServiceAccountToken: false
enableServiceLinks: false
restartPolicy: Never
@ -241,32 +240,10 @@ spec:
}
}
post {
success {
sh '''
set -eu
expected_files="$(printf '%s\n' \
build/hermes-agent.destination \
build/hermes-agent.digest \
build/hermes-agent.image \
build/hermes-agent-release/hermes-agent-image.json \
build/hermes-agent-release/hermes-image-update.patch \
build/hermes-agent-release/hermes-kustomization.yaml \
| LC_ALL=C sort)"
actual_files="$(find build -type f -print | LC_ALL=C sort)"
test "${actual_files}" = "${expected_files}"
destination="$(cat build/hermes-agent.destination)"
python3 ci/scripts/hermes_image_release.py verify-evidence \
--digest-file build/hermes-agent.digest \
--image-file build/hermes-agent.image \
--source-revision "${EXPECTED_SOURCE_REVISION}" \
--build-number "${BUILD_NUMBER}" \
--destination "${destination}" \
--kustomization services/hermes/kustomization.yaml \
--output-dir build/hermes-agent-release
'''
always {
archiveArtifacts(
artifacts: 'build/hermes-agent.destination,build/hermes-agent.digest,build/hermes-agent.image,build/hermes-agent-release/hermes-agent-image.json,build/hermes-agent-release/hermes-image-update.patch,build/hermes-agent-release/hermes-kustomization.yaml',
allowEmptyArchive: false,
artifacts: 'build/hermes-agent.digest,build/hermes-agent.image,build/hermes-agent.destination,build/hermes-agent-release/**',
allowEmptyArchive: true,
fingerprint: true
)
}

View File

@ -335,6 +335,7 @@ def write_release_artifacts(
source_revision, build_number = validate_destination(
destination, source_revision, build_number
)
source = kustomization.read_text(encoding="utf-8")
rendered = render_kustomization(source, digest)
relative_name = kustomization.name
@ -348,6 +349,7 @@ def write_release_artifacts(
)
if not patch:
raise ValueError("published digest already matches the Flux manifest")
output_dir.mkdir(parents=True, exist_ok=True)
(output_dir / "hermes-kustomization.yaml").write_text(rendered, encoding="utf-8")
(output_dir / "hermes-image-update.patch").write_text(patch, encoding="utf-8")
@ -365,64 +367,6 @@ def write_release_artifacts(
return metadata
def validate_release_artifacts(
*,
digest_file: Path,
image_file: Path,
source_revision: str,
build_number: str,
destination: str,
kustomization: Path,
output_dir: Path,
) -> None:
"""Revalidate the exact successful-build evidence without rewriting it."""
digest = validate_kaniko_evidence(
digest_text=digest_file.read_text(encoding="utf-8"),
image_text=image_file.read_text(encoding="utf-8"),
destination=destination,
)
source_revision, build_number = validate_destination(
destination, source_revision, build_number
)
expected_names = {
"hermes-agent-image.json",
"hermes-image-update.patch",
"hermes-kustomization.yaml",
}
entries = list(output_dir.iterdir())
if {entry.name for entry in entries} != expected_names or not all(
entry.is_file() and not entry.is_symlink() for entry in entries
):
raise ValueError("release output must contain exactly three evidence files")
source = kustomization.read_text(encoding="utf-8")
rendered = render_kustomization(source, digest)
relative_name = kustomization.name
patch = "".join(
difflib.unified_diff(
source.splitlines(keepends=True),
rendered.splitlines(keepends=True),
fromfile=f"a/services/hermes/{relative_name}",
tofile=f"b/services/hermes/{relative_name}",
)
)
metadata = {
"build_number": build_number,
"digest": digest,
"flux_image": f"{DEFAULT_IMAGE}@{digest}",
"image": DEFAULT_IMAGE,
"published_tag": destination,
"source_revision": source_revision,
}
expected = {
"hermes-agent-image.json": json.dumps(metadata, indent=2, sort_keys=True) + "\n",
"hermes-image-update.patch": patch,
"hermes-kustomization.yaml": rendered,
}
for name, expected_text in expected.items():
if (output_dir / name).read_text(encoding="utf-8") != expected_text:
raise ValueError(f"release evidence is incomplete or mismatched: {name}")
def _credentials() -> tuple[str, str]:
"""Read the masked, runtime-only Jenkins credential environment."""
username = os.environ.get("HARBOR_USER", "")
@ -450,27 +394,9 @@ def main() -> int:
render.add_argument("--image-file", required=True, type=Path)
render.add_argument("--kustomization", required=True, type=Path)
render.add_argument("--output-dir", required=True, type=Path)
verify = commands.add_parser("verify-evidence")
_common_arguments(verify)
verify.add_argument("--digest-file", required=True, type=Path)
verify.add_argument("--image-file", required=True, type=Path)
verify.add_argument("--kustomization", required=True, type=Path)
verify.add_argument("--output-dir", required=True, type=Path)
args = parser.parse_args()
validate_destination(args.destination, args.source_revision, args.build_number)
if args.command == "verify-evidence":
validate_release_artifacts(
digest_file=args.digest_file,
image_file=args.image_file,
source_revision=args.source_revision,
build_number=args.build_number,
destination=args.destination,
kustomization=args.kustomization,
output_dir=args.output_dir,
)
return 0
username, password = _credentials()
if args.command == "assert-absent":
verify_immutable_policy(username=username, password=password)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Replay the reviewed Hermes Dockerfile heredocs for pinned Kaniko v1.23.2.
"""Replay the reviewed Hermes Dockerfile heredocs for pinned Kaniko.
Kaniko v1.23.2 parses shell heredoc bodies into ``RunCommand.Files`` but its RUN
Kaniko v1.24 parses shell heredoc bodies into ``RunCommand.Files`` but its RUN
implementation executes only ``CmdLine``. The interpreters therefore receive
empty stdin and exit successfully. Docker and BuildKit execute these blocks
normally, so this compatibility runner is enabled only by the Kaniko pipeline.
@ -22,17 +22,8 @@ BLOCKS = (
EXPECTED_COMMANDS = ("node", *("python",) * 7, "node")
def _continued_run_header(lines: list[str], index: int) -> tuple[str, int]:
"""Return one logical RUN header, including backslash continuations."""
header = lines[index]
while header.rstrip().endswith("\\") and index + 1 < len(lines):
index += 1
header = f"{header}\n{lines[index]}"
return header, index
def extract_blocks(source: str) -> list[tuple[tuple[str, ...], str]]:
"""Inventory every RUN heredoc, then return only the reviewed contract."""
"""Return only exact supported RUN heredocs in their reviewed order."""
markers = {start: (end, command) for start, end, command in BLOCKS}
lines = source.splitlines()
blocks: list[tuple[tuple[str, ...], str]] = []
@ -40,15 +31,6 @@ def extract_blocks(source: str) -> list[tuple[tuple[str, ...], str]]:
while index < len(lines):
marker = markers.get(lines[index])
if marker is None:
stripped = lines[index].lstrip()
if stripped[:3].upper() == "RUN" and (
len(stripped) == 3 or stripped[3].isspace()
):
header, index = _continued_run_header(lines, index)
if "<<" in header:
raise ValueError(
"unsupported RUN heredoc outside the exact reviewed contract"
)
index += 1
continue
end, command = marker

View File

@ -105,8 +105,7 @@ pretend direct Jenkins access succeeded.
## Publishing an agent image after review
The `hermes-agent-image` Jenkins job is the only supported agent image builder.
It runs daemonless Kaniko v1.23.2 under the unbound `hermes-image-builder`
ServiceAccount without a service-account token, host socket,
It runs daemonless Kaniko without a service-account token, host socket,
privileged container, or writable Git credential. It accepts only an exact
40-character revision that is both the checked-out commit and current
`atlas/titan-iac` `main`, so a human must merge the source PR first.
@ -124,16 +123,15 @@ root-in-the-build-pod risk, bounded to disposable `emptyDir` storage and a
human-reviewed `main` revision; Dockerfile changes require the same scrutiny as
executable cluster code.
Pinned Kaniko v1.23.2 parses Dockerfile `RUN` heredocs but does not materialize their
Pinned Kaniko parses Dockerfile `RUN` heredocs but does not materialize their
inline files when executing a command. The Jenkins lane therefore enables a
bounded compatibility replay after copying the reviewed Dockerfile and runner
from the same checked-out commit. The runner inventories all `RUN` heredoc
headers before launching an interpreter, rejects every form outside the exact
nine Node/Python blocks, and launches each accepted body in a separate process.
Docker and BuildKit keep their native behavior because the compatibility
argument defaults off. The final TypeScript build, Python compile, and source
assertions remain mandatory, so an omitted or drifted replay fails before any
image can pass release verification.
from the same checked-out commit. The runner accepts only the exact nine
Node/Python heredocs in their expected order and launches each in a separate
interpreter process. Docker and BuildKit keep their native behavior because the
compatibility argument defaults off. The final TypeScript build, Python compile,
and source assertions remain mandatory, so an omitted or drifted replay fails
before any image can pass release verification.
From agent.hermes, trigger that one fixed job with:
@ -147,10 +145,8 @@ also requires its fixed publish confirmation. Each run claims the unique tag
`git-<reviewed-sha>-build-<jenkins-build-number>` and refuses to overwrite an
existing tag. It cross-checks Kaniko's digest and tagged-image evidence, then
independently reads the pushed tag from Harbor before archiving a JSON record
and Flux digest patch. A successful post-condition then revalidates the exact
six-file evidence set and archives those six explicit paths with empty archives
forbidden. Apply that patch on a new branch and submit it for human review; the
build never changes Git, reconciles Flux, or deploys by itself.
and Flux digest patch. Apply that patch on a new branch and submit it for human
review; the build never changes Git, reconciles Flux, or deploys by itself.
The helper posts only to the fixed HTTPS Build Token Root endpoint and accepts
only its real queue responses: HTTP 201 or a non-followed HTTP 303 with an exact
same-origin `/queue/item/<number>/` location. An unauthenticated request is

View File

@ -1,7 +0,0 @@
# services/jenkins/hermes-image-builder-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: hermes-image-builder
namespace: jenkins
automountServiceAccountToken: false

View File

@ -5,7 +5,6 @@ namespace: jenkins
resources:
- namespace.yaml
- serviceaccount.yaml
- hermes-image-builder-serviceaccount.yaml
- vault-serviceaccount.yaml
- pvc.yaml
- cache-pvc.yaml

View File

@ -1,7 +1,6 @@
#!/usr/bin/env sh
set -eu
secret_path="kv/atlas/hermes/developer-jenkins"
secret_api_path="kv/data/atlas/hermes/developer-jenkins"
jwt_file="${VAULT_K8S_JWT_FILE:-/var/run/secrets/kubernetes.io/serviceaccount/token}"
vault_role="${VAULT_K8S_ROLE:-hermes-jenkins-token-seed}"
@ -58,28 +57,14 @@ read_build_token() {
secret_status=$?
set -e
if [ "${secret_status}" -eq 0 ]; then
if printf '%s' "${secret_json}" | grep -Eq '"build_token"[[:space:]]*:[[:space:]]*"[0-9a-f]{64}"'; then
unset secret_json
return 0
fi
unset secret_json
set +e
build_token="$(vault kv get -field=build_token "${secret_path}" 2>&1)"
field_status=$?
set -e
if [ "${field_status}" -eq 0 ]; then
case "${build_token}" in
''|*[!0-9a-f]*) unset build_token; return 11 ;;
esac
if [ "${#build_token}" -eq 64 ]; then
unset build_token
return 0
fi
unset build_token
return 11
fi
if printf '%s' "${build_token}" | grep -q 'No value found'; then
unset build_token
return 11
fi
unset build_token
elif printf '%s' "${secret_json}" | grep -Eq 'Code: 404|No value found at'; then
return 11
fi
if printf '%s' "${secret_json}" | grep -Eq 'Code: 404|No value found at'; then
unset secret_json
return 10
fi
@ -124,14 +109,7 @@ new_token="$(retry_command vault write -field=random_bytes \
log "Vault random token generation failed after retries"
exit 1
}
case "${new_token}" in
''|*[!0-9a-f]*)
unset new_token
log "Vault returned an invalid random token"
exit 1
;;
esac
if [ "${#new_token}" -ne 64 ]; then
if ! printf '%s' "${new_token}" | grep -Eq '^[0-9a-f]{64}$'; then
unset new_token
log "Vault returned an invalid random token"
exit 1

View File

@ -59,7 +59,6 @@ def test_builder_pod_is_daemonless_and_kernel_bounded() -> None:
"""The builder gets only proven build caps, never host, daemon, or K8s access."""
source = PIPELINE_PATH.read_text(encoding="utf-8")
spec = _pod_spec()
assert spec["serviceAccountName"] == "hermes-image-builder"
assert spec["automountServiceAccountToken"] is False
assert spec["enableServiceLinks"] is False
assert "hostPath" not in source
@ -67,7 +66,6 @@ def test_builder_pod_is_daemonless_and_kernel_bounded() -> None:
assert "tcp://" not in source
assert "buildkitd" not in source.lower()
assert "dind" not in source.lower()
assert "serviceAccountToken" not in source
containers = {item["name"]: item for item in spec["containers"]}
kaniko = containers["kaniko"]

View File

@ -1,236 +0,0 @@
"""Fresh fail-closed review cases for the Hermes image builder."""
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
import pytest
import yaml
REPO_ROOT = Path(__file__).resolve().parents[2]
PIPELINE = REPO_ROOT / "ci/Jenkinsfile.hermes-agent-image"
DOCKERFILE = REPO_ROOT / "dockerfiles/Dockerfile.hermes-agent"
RUNNER = REPO_ROOT / "dockerfiles/hermes-kaniko-heredoc-runner.py"
RELEASE = REPO_ROOT / "ci/scripts/hermes_image_release.py"
BUILDER_SA = REPO_ROOT / "services/jenkins/hermes-image-builder-serviceaccount.yaml"
def _load(path: Path, name: str):
spec = importlib.util.spec_from_file_location(name, path)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def _pod_spec() -> dict:
source = PIPELINE.read_text(encoding="utf-8")
pod_yaml = source.split('yaml """', 1)[1].split('"""', 1)[0]
return yaml.safe_load(pod_yaml)["spec"]
@pytest.mark.parametrize(
"unsupported",
[
"RUN cat <<EOF\nbody\nEOF",
"run cat <<'EOF'\nbody\nEOF",
' RUN cat <<"EOF"\nbody\nEOF',
"RUN <<-EOF\n\tbody\n\tEOF",
'RUN ["/bin/sh", "-c"] <<EOF\nbody\nEOF',
"RUN echo before <<EOF after\nbody\nEOF",
"RUN set -eu \\\n && cat <<'EOF'\nbody\nEOF",
],
)
def test_all_unsupported_run_heredoc_forms_reject_before_execution(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, unsupported: str
) -> None:
"""Any RUN heredoc outside the reviewed nine is rejected before replay."""
module = _load(RUNNER, f"heredoc_reject_{abs(hash(unsupported))}")
dockerfile = tmp_path / "Dockerfile"
dockerfile.write_text(
DOCKERFILE.read_text(encoding="utf-8") + "\n" + unsupported + "\n",
encoding="utf-8",
)
calls = []
monkeypatch.setattr(module.subprocess, "run", lambda *_args, **_kwargs: calls.append(1))
with pytest.raises(ValueError, match="unsupported RUN heredoc"):
module.replay(dockerfile)
assert calls == []
def test_exact_reviewed_heredocs_remain_buildkit_native_by_default() -> None:
"""Current source inventories cleanly and enables replay only for Kaniko."""
module = _load(RUNNER, "heredoc_positive_contract")
source = DOCKERFILE.read_text(encoding="utf-8")
assert len(module.extract_blocks(source)) == 9
assert source.count("ARG HERMES_KANIKO_HEREDOC_COMPAT=0") == 1
assert "Kaniko v1.23.2" in RUNNER.read_text(encoding="utf-8")
pipeline = PIPELINE.read_text(encoding="utf-8")
assert pipeline.count("--build-arg=HERMES_KANIKO_HEREDOC_COMPAT=1") == 1
def test_appended_tenth_reviewed_form_rejects_before_execution(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""Even an otherwise supported marker cannot expand the nine-block set."""
module = _load(RUNNER, "heredoc_tenth_block")
dockerfile = tmp_path / "Dockerfile"
dockerfile.write_text(
DOCKERFILE.read_text(encoding="utf-8")
+ "\nRUN node <<'NODE'\nconsole.log('tenth');\nNODE\n",
encoding="utf-8",
)
calls = []
monkeypatch.setattr(module.subprocess, "run", lambda *_args, **_kwargs: calls.append(1))
with pytest.raises(ValueError, match="contract changed"):
module.replay(dockerfile)
assert calls == []
def test_builder_service_account_is_explicit_tokenless_and_unbound() -> None:
"""The build Pod selects one tokenless SA that no tracked RBAC grants bind."""
account = yaml.safe_load(BUILDER_SA.read_text(encoding="utf-8"))
assert account == {
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": {"name": "hermes-image-builder", "namespace": "jenkins"},
"automountServiceAccountToken": False,
}
kustomization = yaml.safe_load(
(REPO_ROOT / "services/jenkins/kustomization.yaml").read_text(encoding="utf-8")
)
assert "hermes-image-builder-serviceaccount.yaml" in kustomization["resources"]
spec = _pod_spec()
assert spec["serviceAccountName"] == "hermes-image-builder"
assert spec["automountServiceAccountToken"] is False
for volume in spec.get("volumes", []):
projected = volume.get("projected", {})
assert all("serviceAccountToken" not in item for item in projected.get("sources", []))
for manifest in (REPO_ROOT / "services/jenkins").glob("*.yaml"):
for document in yaml.safe_load_all(manifest.read_text(encoding="utf-8")):
if not isinstance(document, dict) or document.get("kind") not in {
"RoleBinding",
"ClusterRoleBinding",
}:
continue
subjects = document.get("subjects") or []
assert not any(
item.get("kind") == "ServiceAccount"
and item.get("name") == "hermes-image-builder"
for item in subjects
)
def _release_fixture(tmp_path: Path):
module = _load(RELEASE, f"release_evidence_{tmp_path.name}")
digest = "sha256:" + "7" * 64
revision = "8" * 40
build = "23"
destination = f"{module.DEFAULT_IMAGE}:git-{revision}-build-{build}"
digest_file = tmp_path / "hermes-agent.digest"
image_file = tmp_path / "hermes-agent.image"
manifest = tmp_path / "kustomization.yaml"
output = tmp_path / "release"
digest_file.write_text(digest + "\n", encoding="utf-8")
image_file.write_text(f"{destination}@{digest}\n", encoding="utf-8")
manifest.write_text(
"images:\n"
f" - name: {module.DEFAULT_IMAGE}\n"
" digest: sha256:" + "0" * 64 + "\n",
encoding="utf-8",
)
module.write_release_artifacts(
digest=digest,
source_revision=revision,
build_number=build,
destination=destination,
kustomization=manifest,
output_dir=output,
)
kwargs = {
"digest_file": digest_file,
"image_file": image_file,
"source_revision": revision,
"build_number": build,
"destination": destination,
"kustomization": manifest,
"output_dir": output,
}
return module, kwargs
def test_success_evidence_revalidation_accepts_only_exact_complete_set(
tmp_path: Path,
) -> None:
"""Exact evidence passes; an extra, missing, or altered file fails closed."""
module, kwargs = _release_fixture(tmp_path)
module.validate_release_artifacts(**kwargs)
extra = kwargs["output_dir"] / "unexpected"
extra.write_text("surprise\n", encoding="utf-8")
with pytest.raises(ValueError, match="exactly three"):
module.validate_release_artifacts(**kwargs)
extra.unlink()
metadata = kwargs["output_dir"] / "hermes-agent-image.json"
original = metadata.read_text(encoding="utf-8")
metadata.write_text(original.replace('"build_number": "23"', '"build_number": "24"'))
with pytest.raises(ValueError, match="incomplete or mismatched"):
module.validate_release_artifacts(**kwargs)
metadata.write_text(original, encoding="utf-8")
(kwargs["output_dir"] / "hermes-image-update.patch").unlink()
with pytest.raises(ValueError, match="exactly three"):
module.validate_release_artifacts(**kwargs)
def test_verify_evidence_cli_needs_no_runtime_registry_credential(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Post-success validation is deterministic and cannot mask missing creds."""
module, kwargs = _release_fixture(tmp_path)
monkeypatch.delenv("HARBOR_USER", raising=False)
monkeypatch.delenv("HARBOR_PASSWORD", raising=False)
monkeypatch.setattr(
sys,
"argv",
[
"hermes_image_release.py",
"verify-evidence",
"--digest-file",
str(kwargs["digest_file"]),
"--image-file",
str(kwargs["image_file"]),
"--source-revision",
kwargs["source_revision"],
"--build-number",
kwargs["build_number"],
"--destination",
kwargs["destination"],
"--kustomization",
str(kwargs["kustomization"]),
"--output-dir",
str(kwargs["output_dir"]),
],
)
assert module.main() == 0
def test_pipeline_success_post_requires_and_archives_exact_six_files() -> None:
"""Missing or partial post-success evidence must change the build to failed."""
source = PIPELINE.read_text(encoding="utf-8")
post = source.split(" post {", 1)[1]
assert "success {" in post
assert "always {" not in post
assert "verify-evidence" in post
assert 'allowEmptyArchive: false' in post
archive = post.split("artifacts: '", 1)[1].split("'", 1)[0]
paths = archive.split(",")
assert len(paths) == 6
assert len(set(paths)) == 6
assert all("*" not in path for path in paths)
assert "find build -type f" in post

View File

@ -109,9 +109,7 @@ def test_seed_job_is_revisioned_bounded_and_tracks_fail_closed_script() -> None:
assert security["capabilities"]["drop"] == ["ALL"]
source = SEEDER.read_text(encoding="utf-8")
assert '"options":{"cas":0}' in source
assert 'vault kv get -field=build_token "${secret_path}"' in source
assert "kv/data/atlas/hermes/developer-jenkins" in source
assert '"build_token"[[:space:]]' not in source
assert "kv patch" not in source
assert "kv delete" not in source
assert SEEDER.name not in VAULT_CONFIG.read_text(encoding="utf-8")
@ -147,34 +145,6 @@ if [ "$1" = "write" ] && [ "$2" = "-field=random_bytes" ] && [ "$3" = "sys/tools
printf '%064d\n' 0
exit 0
fi
if [ "$1" = "kv" ] && [ "$2" = "get" ] && [ "$3" = "-field=build_token" ]; then
case "$scenario" in
existing)
printf '%064d\n' 1
exit 0
;;
cas-race)
printf '%064d\n' 2
exit 0
;;
field-prefix)
printf 'prefix%064d\n' 3
exit 0
;;
field-suffix)
printf '%064dsuffix\n' 4
exit 0
;;
field-multiline)
printf '%064d\nextra\n' 5
exit 0
;;
missing-field|metadata-only|custom-metadata)
echo 'No value found at build_token' >&2
exit 2
;;
esac
fi
if [ "$1" = "write" ] && [ "$2" = "kv/data/atlas/hermes/developer-jenkins" ]; then
payload="${3#@}"
cp "$payload" "$FAKE_STDIN_CAPTURE"
@ -188,25 +158,13 @@ fi
if [ "$1" = "read" ] && [ "$2" = "-format=json" ]; then
case "$scenario" in
existing)
printf '{"data":{"data":{"build_token":"%064d"},"metadata":{"build_token":"decoy"}}}\n' 1
printf '{"data":{"data":{"build_token":"%064d"}}}\n' 1
exit 0
;;
missing-field)
printf '{"data":{"data":{"other":"preserve-me"}}}\n'
exit 0
;;
metadata-only)
printf '{"data":{"data":{"other":"preserve-me"},"metadata":{"build_token":"%064d"}}}\n' 6
exit 0
;;
custom-metadata)
printf '{"data":{"data":{"other":"preserve-me"},"metadata":{"custom_metadata":{"build_token":"%064d"}}}}\n' 7
exit 0
;;
field-prefix|field-suffix|field-multiline)
printf '{"data":{"data":{"build_token":"present"}}}\n'
exit 0
;;
absent)
echo 'Code: 404' >&2
exit 2
@ -272,10 +230,7 @@ def _run_seeder(
return result, call_text
@pytest.mark.parametrize(
"scenario",
["existing", "missing-field", "metadata-only", "custom-metadata", "read-error"],
)
@pytest.mark.parametrize("scenario", ["existing", "missing-field", "read-error"])
def test_seeder_never_overwrites_existing_or_ambiguous_state(
tmp_path: Path, scenario: str
) -> None:
@ -286,18 +241,6 @@ def test_seeder_never_overwrites_existing_or_ambiguous_state(
assert "sys/tools/random" not in calls
@pytest.mark.parametrize("scenario", ["field-prefix", "field-suffix", "field-multiline"])
def test_seeder_requires_the_entire_exact_data_field(
tmp_path: Path, scenario: str
) -> None:
"""Prefix, suffix, or multiline values cannot satisfy readiness."""
result, calls = _run_seeder(tmp_path, scenario)
assert result.returncode != 0
assert "kv get -field=build_token kv/atlas/hermes/developer-jenkins" in calls
assert "write kv/data/atlas/hermes/developer-jenkins" not in calls
assert "sys/tools/random" not in calls
def test_seeder_uses_single_cas_create_and_stdin_for_absent_secret(
tmp_path: Path,
) -> None: