feat(deploy): add Engine ontology SDK transition
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import importlib.machinery
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
PLATFORM_ROOT = SCRIPT_DIR.parent.parent
|
||||
RUNNER_PATH = SCRIPT_DIR / "nodedc-deploy"
|
||||
ENGINE_BUILDER = SCRIPT_DIR / "build-engine-mcp-ontology-sdk-artifact.mjs"
|
||||
PLATFORM_BUILDER = SCRIPT_DIR / "build-platform-gelios-provider-v2-artifact.mjs"
|
||||
ENGINE_ARTIFACT = (
|
||||
PLATFORM_ROOT
|
||||
/ "infra/deploy-artifacts/nodedc-engine-mcp-control-plane-20260718-005.tgz"
|
||||
)
|
||||
PLATFORM_ARTIFACT = (
|
||||
PLATFORM_ROOT
|
||||
/ "infra/deploy-artifacts/nodedc-platform-gelios-provider-v2-20260718-005.tgz"
|
||||
)
|
||||
ENGINE_SHA256 = "8d6e1581ebbba420984e2393b8c6f0f21e0b644f630c4ca6483a7cc4a0ae5c71"
|
||||
PLATFORM_SHA256 = "e1b838ec265369af735766fc7d1bf23c5e05bd2d15ef65e2446b4f7efa7a1baa"
|
||||
|
||||
|
||||
def load_runner():
|
||||
loader = importlib.machinery.SourceFileLoader("nodedc_engine_mcp_ontology_sdk", str(RUNNER_PATH))
|
||||
spec = importlib.util.spec_from_loader(loader.name, loader)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
RUNNER = load_runner()
|
||||
|
||||
|
||||
class EngineMcpOntologySdkTest(unittest.TestCase):
|
||||
def test_canonical_artifacts_have_pinned_digests_and_safe_members(self):
|
||||
for artifact, expected in (
|
||||
(ENGINE_ARTIFACT, ENGINE_SHA256),
|
||||
(PLATFORM_ARTIFACT, PLATFORM_SHA256),
|
||||
):
|
||||
self.assertEqual(hashlib.sha256(artifact.read_bytes()).hexdigest(), expected)
|
||||
self.assertEqual(artifact.read_bytes()[4:8], b"\0\0\0\0")
|
||||
with tarfile.open(artifact, "r:gz") as archive:
|
||||
for member in archive:
|
||||
self.assertTrue(member.isfile() or member.isdir())
|
||||
self.assertFalse(Path(member.name).name.startswith("._"))
|
||||
|
||||
def test_engine_builder_is_byte_reproducible(self):
|
||||
outputs = []
|
||||
with tempfile.TemporaryDirectory(prefix="nodedc-engine-mcp-ontology-sdk-") as directory:
|
||||
for index in range(2):
|
||||
target = Path(directory) / str(index)
|
||||
target.mkdir()
|
||||
env = os.environ.copy()
|
||||
env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(target)
|
||||
result = subprocess.run(
|
||||
["node", str(ENGINE_BUILDER), "20260718-005"],
|
||||
cwd=PLATFORM_ROOT,
|
||||
env=env,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
outputs.append(Path(json.loads(result.stdout)["artifact"]).read_bytes())
|
||||
self.assertEqual(outputs[0], outputs[1])
|
||||
self.assertEqual(outputs[0], ENGINE_ARTIFACT.read_bytes())
|
||||
|
||||
def test_platform_builder_is_byte_reproducible(self):
|
||||
outputs = []
|
||||
with tempfile.TemporaryDirectory(prefix="nodedc-platform-gelios-v2-") as directory:
|
||||
for index in range(2):
|
||||
target = Path(directory) / str(index)
|
||||
target.mkdir()
|
||||
env = os.environ.copy()
|
||||
env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(target)
|
||||
result = subprocess.run(
|
||||
["node", str(PLATFORM_BUILDER), "20260718-005"],
|
||||
cwd=PLATFORM_ROOT,
|
||||
env=env,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
outputs.append(Path(json.loads(result.stdout)["artifact"]).read_bytes())
|
||||
self.assertEqual(outputs[0], outputs[1])
|
||||
self.assertEqual(outputs[0], PLATFORM_ARTIFACT.read_bytes())
|
||||
|
||||
def test_engine_slice_is_exact_and_restarts_only_backend(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
manifest, entries, payload = RUNNER.load_artifact(ENGINE_ARTIFACT, root)
|
||||
descriptor = RUNNER.validate_engine_mcp_ontology_sdk_payload(payload, entries)
|
||||
self.assertEqual(manifest["component"], "engine")
|
||||
self.assertEqual(tuple(entries), RUNNER.ENGINE_MCP_ONTOLOGY_SDK_ARTIFACT_ENTRIES)
|
||||
self.assertEqual(RUNNER.component_services("engine", entries), ("nodedc-backend",))
|
||||
self.assertEqual(RUNNER.component_builds("engine", entries), ())
|
||||
self.assertEqual(
|
||||
descriptor["source"]["gatewaySha256"],
|
||||
RUNNER.ENGINE_MCP_ONTOLOGY_SDK_TARGET_SHA256[
|
||||
RUNNER.ENGINE_NODE_INTELLIGENCE_GATEWAY_REL
|
||||
],
|
||||
)
|
||||
|
||||
def test_platform_provider_package_is_metadata_only(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
manifest, entries, _payload = RUNNER.load_artifact(PLATFORM_ARTIFACT, root)
|
||||
self.assertEqual(manifest["component"], "platform")
|
||||
self.assertTrue(RUNNER.is_platform_provider_catalog_only(entries))
|
||||
self.assertEqual(RUNNER.component_services("platform", entries), ())
|
||||
self.assertEqual(RUNNER.component_builds("platform", entries), ())
|
||||
self.assertEqual(RUNNER.component_healthchecks("platform", entries, ()), ())
|
||||
|
||||
def test_engine_payload_tampering_is_rejected(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
RUNNER.safe_extract(ENGINE_ARTIFACT, root)
|
||||
entries = RUNNER.parse_files_list(root / "files.txt")
|
||||
catalog = root / "payload/nodedc-source/server/assets/provider-packages/v1/catalog.json"
|
||||
catalog.write_text("{}\n", encoding="utf-8")
|
||||
with self.assertRaisesRegex(RUNNER.DeployError, "target sha256 mismatch"):
|
||||
RUNNER.validate_engine_mcp_ontology_sdk_payload(root / "payload", entries)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in New Issue
Block a user