117 lines
5.1 KiB
Python
117 lines
5.1 KiB
Python
# #region Test.Tooling.DockerBundle.Metadata [C:3] [TYPE Module] [SEMANTICS bundle,metadata,release]
|
|
# @TEST_INVARIANT The actual producer binds both generated deployment files before publishing manifestv2; any mismatch refuses before Docker.
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import shlex
|
|
import subprocess
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from test_import_docker_bundle import archive_bytes, importer
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
# #region Test.Tooling.DockerBundle.Metadata.Generator [C:3] [TYPE Function]
|
|
def generate(root, tag):
|
|
archive_dir = root / "inert-exports"
|
|
archive_dir.mkdir(exist_ok=True)
|
|
target = root / "bundle"
|
|
identity = None
|
|
for role in importer.ROLES:
|
|
archive, identity = archive_bytes(f"superset-tools-{role}:{tag}", modern=True)
|
|
(archive_dir / f"superset-tools-{role}.{tag}.tar.xz").write_bytes(archive)
|
|
text = (ROOT / "build.sh").read_text()
|
|
actual = text[text.index("generate_deploy_compose() {"):text.index("bundle_light() {")]
|
|
prefix = f"""
|
|
set -euo pipefail
|
|
umask 077
|
|
DIST_ROOT={shlex.quote(str(target))}
|
|
SCRIPT_DIR={shlex.quote(str(ROOT))}
|
|
FIXTURE_EXPORTS={shlex.quote(str(archive_dir))}
|
|
FIXTURE_ID={shlex.quote(identity)}
|
|
build_backend(){{ return 0; }}
|
|
build_frontend(){{ return 0; }}
|
|
ensure_image_present(){{ return 0; }}
|
|
run_bundle_smoke(){{ return 0; }}
|
|
docker(){{ [ "$1" = tag ] || return 99; }}
|
|
get_image_id(){{ printf '%s' "$FIXTURE_ID"; }}
|
|
get_repo_digest(){{ printf 'unavailable'; }}
|
|
export_image(){{ cp "$FIXTURE_EXPORTS/${{1%%:*}}.${{1#*:}}.tar.xz" "$2"; }}
|
|
"""
|
|
result = subprocess.run(["/usr/bin/bash", "-c", prefix + actual + "\nbundle_release " + shlex.quote(tag)],
|
|
stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
|
|
assert result.returncode == 0, result.stderr.decode()
|
|
return target
|
|
# #endregion Test.Tooling.DockerBundle.Metadata.Generator
|
|
|
|
# #region Test.Tooling.DockerBundle.Metadata.BeforeDocker [C:3] [TYPE Function]
|
|
def without_docker(directory, name, tag):
|
|
actual = subprocess.Popen
|
|
def boundary(argv, **kwargs):
|
|
assert argv[0] != "docker", "metadata refusal must precede even read-only Docker inspection"
|
|
return actual(argv, **kwargs)
|
|
with patch.object(importer.subprocess, "Popen", side_effect=boundary):
|
|
return importer.execute(directory, name, tag, verify_only=False)
|
|
# #endregion Test.Tooling.DockerBundle.Metadata.BeforeDocker
|
|
|
|
# #region Test.Tooling.DockerBundle.Metadata.Cases [C:3] [TYPE Function]
|
|
def test_actual_bundle_generator_publishes_v2_after_both_metadata_files(tmp_path):
|
|
root = generate(tmp_path, "fresh")
|
|
value = json.loads((root / "manifest.fresh.json").read_text())
|
|
assert value["manifest_version"] == 2
|
|
assert value["compose_sha256"] == hashlib.sha256((root / value["compose_file"]).read_bytes()).hexdigest()
|
|
assert value["env_template_sha256"] == hashlib.sha256((root / value["env_template"]).read_bytes()).hexdigest()
|
|
rc, result = importer.execute(root, "manifest.fresh.json", "fresh", True)
|
|
assert rc == 0, result
|
|
assert result["status"] == "verified"
|
|
|
|
|
|
def test_second_release_shared_compose_refuses_old_manifest_before_docker(tmp_path):
|
|
root = generate(tmp_path, "old")
|
|
before = (root / "manifest.old.json").read_bytes()
|
|
generate(tmp_path, "new")
|
|
assert (root / "manifest.old.json").read_bytes() == before
|
|
assert "superset-tools-backend:new" in (root / "docker-compose.enterprise-clean.yml").read_text()
|
|
rc, result = without_docker(root, "manifest.old.json", "old")
|
|
assert rc == 2
|
|
assert result["code"] == "deployment_metadata_checksum_conflict"
|
|
assert result["attempted_roles"] == result["loaded_roles"] == []
|
|
|
|
|
|
def test_env_template_tamper_refuses_before_docker(tmp_path):
|
|
root = generate(tmp_path, "fresh")
|
|
with (root / ".env.example").open("a") as output:
|
|
output.write("\n# Modified template\n")
|
|
rc, result = without_docker(root, "manifest.fresh.json", "fresh")
|
|
assert rc == 2
|
|
assert result["code"] == "deployment_metadata_checksum_conflict"
|
|
assert not result["partial_load_possible"]
|
|
|
|
|
|
def test_unbound_v1_has_no_fallback(tmp_path):
|
|
root = generate(tmp_path, "fresh")
|
|
path = root / "manifest.fresh.json"
|
|
value = json.loads(path.read_text())
|
|
value["manifest_version"] = 1
|
|
value.pop("compose_sha256", None)
|
|
value.pop("env_template_sha256", None)
|
|
path.write_text(json.dumps(value))
|
|
rc, result = without_docker(root, path.name, "fresh")
|
|
assert rc == 2
|
|
assert result["code"] == "unsupported_bundle_manifest"
|
|
|
|
|
|
@pytest.mark.parametrize("name", ["docker-compose.enterprise-clean.yml", ".env.example"])
|
|
def test_metadata_symlink_refuses_before_docker(tmp_path, name):
|
|
root = generate(tmp_path, "fresh")
|
|
original = root / (name + ".original")
|
|
(root / name).rename(original)
|
|
(root / name).symlink_to(original)
|
|
rc, result = without_docker(root, "manifest.fresh.json", "fresh")
|
|
assert rc == 2
|
|
assert result["loaded_roles"] == []
|
|
# #endregion Test.Tooling.DockerBundle.Metadata.Cases
|
|
# #endregion Test.Tooling.DockerBundle.Metadata
|