feat(deploy): extend canon for managed EDP and Engine grants
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
BUILDER = SCRIPT_DIR / "build-engine-data-product-publish-grant-artifact.mjs"
|
||||
EXPECTED_ENTRIES = [
|
||||
"nodedc-source/server/assets/provider-packages/v1/catalog.json",
|
||||
"nodedc-source/server/dataProductPublishGrant",
|
||||
"nodedc-source/server/engineAgents/store.js",
|
||||
"nodedc-source/server/routes/engineAgentGateway.js",
|
||||
"nodedc-source/server/routes/n8n.js",
|
||||
"nodedc-source/services/backend/data-product-publish-grant/docker-compose.immutable-runtime.yml",
|
||||
]
|
||||
PATCH_ID = "engine-data-product-publish-grant-20990101-001"
|
||||
SUCCESSFUL_CREDENTIAL_SINK_INDEX_SHA256 = (
|
||||
"b2b790b02839570d967a2ca68b00e2724485a99389ac9b3a589a1b22302a36b8"
|
||||
)
|
||||
|
||||
|
||||
class EnginePublishGrantArtifactTest(unittest.TestCase):
|
||||
def build(self, artifact_dir, patch_id):
|
||||
environment = os.environ.copy()
|
||||
environment["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(artifact_dir)
|
||||
result = subprocess.run(
|
||||
["node", str(BUILDER), patch_id],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=environment,
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
|
||||
def test_artifact_is_narrow_secret_free_and_deterministic(self):
|
||||
with tempfile.TemporaryDirectory(prefix="nodedc-engine-publish-artifact-") as directory:
|
||||
root = Path(directory)
|
||||
first_dir = root / "first"
|
||||
second_dir = root / "second"
|
||||
first_dir.mkdir()
|
||||
second_dir.mkdir()
|
||||
first = self.build(first_dir, PATCH_ID)
|
||||
artifact = Path(first["artifact"])
|
||||
first_bytes = artifact.read_bytes()
|
||||
second = self.build(second_dir, PATCH_ID)
|
||||
second_bytes = Path(second["artifact"]).read_bytes()
|
||||
|
||||
self.assertEqual(first["entries"], EXPECTED_ENTRIES)
|
||||
self.assertEqual(first["sha256"], hashlib.sha256(first_bytes).hexdigest())
|
||||
self.assertEqual(first["sha256"], second["sha256"])
|
||||
self.assertEqual(first_bytes, second_bytes)
|
||||
|
||||
with tarfile.open(artifact, "r:gz") as archive:
|
||||
names = {member.name for member in archive.getmembers()}
|
||||
files = archive.extractfile("files.txt").read().decode("utf-8").splitlines()
|
||||
manifest = archive.extractfile("manifest.env").read().decode("utf-8")
|
||||
n8n_source = archive.extractfile(
|
||||
"payload/nodedc-source/server/routes/n8n.js"
|
||||
).read().decode("utf-8")
|
||||
gateway_source = archive.extractfile(
|
||||
"payload/nodedc-source/server/routes/engineAgentGateway.js"
|
||||
).read().decode("utf-8")
|
||||
runtime_override = archive.extractfile(
|
||||
"payload/nodedc-source/services/backend/data-product-publish-grant/"
|
||||
"docker-compose.immutable-runtime.yml"
|
||||
).read().decode("utf-8")
|
||||
|
||||
self.assertEqual(files, EXPECTED_ENTRIES)
|
||||
self.assertEqual(
|
||||
manifest,
|
||||
f"id={PATCH_ID}\ncomponent=engine\ntype=app-overlay\n",
|
||||
)
|
||||
forbidden_roots = (
|
||||
"payload/docker-compose.yml",
|
||||
"payload/nodedc-source/server/index.js",
|
||||
"payload/nodedc-source/server/credentialPolicies/ndcPrivateNode.js",
|
||||
"payload/nodedc-source/server/credentialSink/",
|
||||
"payload/nodedc-source/server/routes/engineCredentialSink.js",
|
||||
"payload/nodedc-source/server/middleware/demoAccess.js",
|
||||
"payload/nodedc-source/server/routes/ndcAgentMcp.js",
|
||||
"payload/nodedc-source/server/tests/",
|
||||
"payload/nodedc-source/dist/",
|
||||
"payload/nodedc-source/server/data/",
|
||||
)
|
||||
self.assertFalse(any(
|
||||
name == root.rstrip("/") or name.startswith(root)
|
||||
for name in names
|
||||
for root in forbidden_roots
|
||||
))
|
||||
self.assertNotIn("from '../credentialSink/", n8n_source)
|
||||
self.assertIn("engineCredentialSinkN8nAdapter", n8n_source)
|
||||
self.assertIn("engineDataProductPublishGrantN8nAdapter", n8n_source)
|
||||
for tool in (
|
||||
"engine_plan_data_product_publish_grant",
|
||||
"engine_apply_data_product_publish_grant",
|
||||
"engine_accept_data_product_publish_grant",
|
||||
"engine_rollback_data_product_publish_grant",
|
||||
):
|
||||
self.assertIn(tool, gateway_source)
|
||||
self.assertIn(" nodedc-backend:", runtime_override)
|
||||
self.assertNotIn(" n8n:", runtime_override)
|
||||
self.assertIn(
|
||||
"source: /volume2/nodedc-demo/nodedc-control-plane/publish-grants",
|
||||
runtime_override,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"source: /volume2/nodedc-demo/nodedc-control-plane\n",
|
||||
runtime_override,
|
||||
)
|
||||
self.assertIn("read_only: true", runtime_override)
|
||||
self.assertEqual(runtime_override.count("create_host_path: false"), 2)
|
||||
|
||||
def test_builder_requires_a_fresh_never_issued_patch_id(self):
|
||||
with tempfile.TemporaryDirectory(prefix="nodedc-engine-publish-id-") as directory:
|
||||
artifact_dir = Path(directory)
|
||||
environment = os.environ.copy()
|
||||
environment["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(artifact_dir)
|
||||
cases = (
|
||||
([], "fresh-patch-id"),
|
||||
(["engine-data-product-publish-grant-20260716-001"], "patch_id_already_issued"),
|
||||
(["engine-data-product-publish-grant-20260717-001"], "patch_id_already_issued"),
|
||||
(["engine-data-product-publish-grant-20260717-002"], "patch_id_already_issued"),
|
||||
(["engine-publish-grant-unit-001"], "fresh-patch-id"),
|
||||
)
|
||||
for arguments, expected in cases:
|
||||
with self.subTest(arguments=arguments):
|
||||
result = subprocess.run(
|
||||
["node", str(BUILDER), *arguments],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=environment,
|
||||
)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn(expected, result.stderr)
|
||||
|
||||
self.build(artifact_dir, PATCH_ID)
|
||||
duplicate = subprocess.run(
|
||||
["node", str(BUILDER), PATCH_ID],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=environment,
|
||||
)
|
||||
self.assertNotEqual(duplicate.returncode, 0)
|
||||
self.assertIn("artifact_already_exists", duplicate.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in New Issue
Block a user