Loading ansible/tests/r2_e2e/SDK_ACCEPTANCE.md +22 −5 Changes for ansible/tests/r2_e2e/SDK_ACCEPTANCE.md: 22 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -24,11 +24,13 @@ of this run. No existing test is marked xfail or weakened. Compute `sdk.sha256` from the independently reviewed wheel: ```sh python tests/r2_e2e/sdk_artifact.py /disk/artifacts/openop_tfsdk-1.1.1-py3-none-any.whl python tests/r2_e2e/sdk_artifact.py /disk/artifacts/openop_tfsdk-2.0.0rc3-py3-none-any.whl ``` The same script runs read-only in the selected SRM pod. Python/chart contents and package version must match; version 1.1.1 alone cannot identify a commit. and package version must match; the version alone cannot identify a commit. Replace all three SDK fields with the selected candidate; the command shows the published rc3 filename as an example, not the final release candidate. Record the reviewed source SHA alongside the wheel fingerprint. The test verifies both SRM/OEG running image digests before creating a test client. Loading Loading @@ -59,9 +61,9 @@ Cleanup uses public OEG deletion, only for this run's application/instances/TI, then removes that client. Cleanup failures are reported alongside the test failure, and no namespace, platform release or unrelated resource is deleted. The current SDK baseline returns no endpoints, so this assertion is expected to fail once a compatible lab is available. That is not a live failure already reproduced by this change. Capture the red baseline before changing the SDK. The release SDK can return NodePort endpoints. This scenario still requires a compatible SRM/OEG image set, explicit control-path configuration and a real lab run; offline success does not qualify the selected live candidate. ## Complete the TI checkpoint separately Loading Loading @@ -114,3 +116,18 @@ JUnit and a qualification receipt record the selected checkpoint, source SHA, SDK wheel checksum and running SRM/OEG image digests. No credentials or complete settings/profile are exported as CI artifacts. A passing endpoints checkpoint cannot substitute for Traffic Influence or other release acceptance scenarios. ## Public container request Catalogue and SDK acceptance use the same container manifest builder. The public Kubernetes shape is requiredResources.infraKind=kubernetes with isStandalone=false and applicationResources.cpuPool containing integer numCPU, memory in MB and the required topology. Canonical SDK values such as100m belong at the SDK boundary, not in this public request. The default is one CPU core and256MB. An optional resource_requests profile object can select {"cpu":2,"memory_mb":257}; CPU must be an integer1–256 and memory_mb an integer1–16384. Invalid values fail during profile loading, before creating lab clients. This bounded repair fixes request construction; observing applied Deployment/Pod resources belongs to the next cumulative checkpoint. ansible/tests/r2_e2e/app_manifest.py 0 → 100644 +34 −0 Changes for ansible/tests/r2_e2e/app_manifest.py: 34 added lines, 0 removed lines. Original line number Diff line number Diff line """The supported Release 2 container shape shared by catalogue and workload tests.""" import secrets from uuid import uuid4 def validated_resource_requests(profile): requested = profile.get("resource_requests", {"cpu": 1, "memory_mb": 256}) if (not isinstance(requested, dict) or set(requested) != {"cpu", "memory_mb"} or type(requested.get("cpu")) is not int or not 1 <= requested["cpu"] <= 256 or type(requested.get("memory_mb")) is not int or not 1 <= requested["memory_mb"] <= 16384): raise ValueError("resource_requests requires integer CPU cores1–256 and memory_mb1–16384 for Kubernetes cpuPool") return requested def container_manifest(image="nginx:1.27-alpine", *, app_id=None, name=None, cpu=1, memory_mb=256): return { "appId": app_id or str(uuid4()), "name": name or f"e2e_web_{secrets.token_hex(3)}", "appProvider": "e2e_app_provider", "version": "1.0.0", "packageType": "CONTAINER", "appRepo": {"type": "PUBLICREPO", "imagePath": image}, "requiredResources": { "infraKind": "kubernetes", "isStandalone": False, "applicationResources": {"cpuPool": { "numCPU": cpu, "memory": memory_mb, "topology": {"minNumberOfNodes": 1, "minNodeCpu": 1, "minNodeMemory": 256}, }}, }, "componentSpec": [{"componentName": "web", "networkInterfaces": [ {"interfaceId": "http", "protocol": "TCP", "port": 80, "visibilityType": "VISIBILITY_EXTERNAL"}]}], } ansible/tests/r2_e2e/profiles/deploy_to_ti.example.json +3 −3 Changes for ansible/tests/r2_e2e/profiles/deploy_to_ti.example.json: 3 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ "image_digest": "sha256:REPLACE_WITH_REVIEWED_OEG_IMAGE_DIGEST" }, "sdk": { "revision": "c748ee15857eead260d933f75f59516e5698f929", "version": "2.0.0rc2", "sha256": "d5dfd568b89e8368217cb5094f9a48c1170c97aed87fd01f388b119409125eb9" "revision": "REPLACE_WITH_REVIEWED_SDK_SOURCE_SHA", "version": "REPLACE_WITH_REVIEWED_CANDIDATE_VERSION", "sha256": "REPLACE_WITH_REVIEWED_SDK_CONTENT_FINGERPRINT" }, "ti": { "application_wide_supported": false, Loading ansible/tests/r2_e2e/sdk_scenario.py +7 −22 Changes for ansible/tests/r2_e2e/sdk_scenario.py: 7 added lines, 22 removed lines. Original line number Diff line number Diff line Loading @@ -9,6 +9,8 @@ import time from urllib.parse import urlparse from uuid import uuid4 from app_manifest import container_manifest, validated_resource_requests def load_profile(path, checkpoint): profile = json.loads(Path(path).read_text()) Loading Loading @@ -39,6 +41,7 @@ def load_profile(path, checkpoint): or not 1 <= profile["timeout_seconds"] <= 900 ): raise ValueError("timeout_seconds must be between 1 and 900") validated_resource_requests(profile) if checkpoint == "ti": ti = profile["ti"] if ti["application_wide_supported"] is not True: Loading Loading @@ -229,28 +232,10 @@ class Scenario: active = [z for z in zones.json() if z["edgeCloudZoneStatus"] == "active"] assert len(active) == 1, "The dedicated lab must expose exactly one active local zone" self.zone_id = active[0]["edgeCloudZoneId"] manifest = { "appId": self.app_id, "name": self.name, "appProvider": "sdk_e2e_provider", "version": "1.0.0", "packageType": "CONTAINER", "appRepo": {"type": "PUBLICREPO", "imagePath": self.profile["application_image"]}, "requiredResources": {"infraKind": "container", "numCPU": "100m", "memory": 128}, "componentSpec": [ { "componentName": "web", "networkInterfaces": [ { "interfaceId": "http", "protocol": "TCP", "port": 80, "visibilityType": "VISIBILITY_EXTERNAL", } ], } ], } manifest = container_manifest( self.profile["application_image"], app_id=self.app_id, name=self.name, **validated_resource_requests(self.profile) ) # A unique caller-supplied ID permits bounded cleanup after a lost response. self.registered = True response = self.call("POST", self.apps, json=manifest) Loading ansible/tests/r2_e2e/test_apps.py +1 −21 Changes for ansible/tests/r2_e2e/test_apps.py: 1 added line, 21 removed lines. Original line number Diff line number Diff line """App Providers register applications at OEG, which keeps them in SRM's catalog.""" import secrets import pytest from app_manifest import container_manifest as _manifest from conftest import http APPS = "/edge-application-management/v0.1alpha1/apps" def _manifest(): return { "name": f"e2e_nginx_{secrets.token_hex(3)}", "appProvider": "e2e_app_provider", "version": "1.0.0", "packageType": "CONTAINER", "appRepo": {"type": "PUBLICREPO", "imagePath": "nginx:1.27-alpine"}, "requiredResources": { "infraKind": "kubernetes", "isStandalone": False, "applicationResources": {"cpuPool": { "numCPU": 1, "memory": 256, "topology": {"minNumberOfNodes": 1, "minNodeCpu": 1, "minNodeMemory": 256}, }}, }, "componentSpec": [{"componentName": "web", "networkInterfaces": [ {"interfaceId": "http", "protocol": "TCP", "port": 80, "visibilityType": "VISIBILITY_EXTERNAL"}]}], } @pytest.fixture def registered_app(operators, callers): Loading Loading
ansible/tests/r2_e2e/SDK_ACCEPTANCE.md +22 −5 Changes for ansible/tests/r2_e2e/SDK_ACCEPTANCE.md: 22 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -24,11 +24,13 @@ of this run. No existing test is marked xfail or weakened. Compute `sdk.sha256` from the independently reviewed wheel: ```sh python tests/r2_e2e/sdk_artifact.py /disk/artifacts/openop_tfsdk-1.1.1-py3-none-any.whl python tests/r2_e2e/sdk_artifact.py /disk/artifacts/openop_tfsdk-2.0.0rc3-py3-none-any.whl ``` The same script runs read-only in the selected SRM pod. Python/chart contents and package version must match; version 1.1.1 alone cannot identify a commit. and package version must match; the version alone cannot identify a commit. Replace all three SDK fields with the selected candidate; the command shows the published rc3 filename as an example, not the final release candidate. Record the reviewed source SHA alongside the wheel fingerprint. The test verifies both SRM/OEG running image digests before creating a test client. Loading Loading @@ -59,9 +61,9 @@ Cleanup uses public OEG deletion, only for this run's application/instances/TI, then removes that client. Cleanup failures are reported alongside the test failure, and no namespace, platform release or unrelated resource is deleted. The current SDK baseline returns no endpoints, so this assertion is expected to fail once a compatible lab is available. That is not a live failure already reproduced by this change. Capture the red baseline before changing the SDK. The release SDK can return NodePort endpoints. This scenario still requires a compatible SRM/OEG image set, explicit control-path configuration and a real lab run; offline success does not qualify the selected live candidate. ## Complete the TI checkpoint separately Loading Loading @@ -114,3 +116,18 @@ JUnit and a qualification receipt record the selected checkpoint, source SHA, SDK wheel checksum and running SRM/OEG image digests. No credentials or complete settings/profile are exported as CI artifacts. A passing endpoints checkpoint cannot substitute for Traffic Influence or other release acceptance scenarios. ## Public container request Catalogue and SDK acceptance use the same container manifest builder. The public Kubernetes shape is requiredResources.infraKind=kubernetes with isStandalone=false and applicationResources.cpuPool containing integer numCPU, memory in MB and the required topology. Canonical SDK values such as100m belong at the SDK boundary, not in this public request. The default is one CPU core and256MB. An optional resource_requests profile object can select {"cpu":2,"memory_mb":257}; CPU must be an integer1–256 and memory_mb an integer1–16384. Invalid values fail during profile loading, before creating lab clients. This bounded repair fixes request construction; observing applied Deployment/Pod resources belongs to the next cumulative checkpoint.
ansible/tests/r2_e2e/app_manifest.py 0 → 100644 +34 −0 Changes for ansible/tests/r2_e2e/app_manifest.py: 34 added lines, 0 removed lines. Original line number Diff line number Diff line """The supported Release 2 container shape shared by catalogue and workload tests.""" import secrets from uuid import uuid4 def validated_resource_requests(profile): requested = profile.get("resource_requests", {"cpu": 1, "memory_mb": 256}) if (not isinstance(requested, dict) or set(requested) != {"cpu", "memory_mb"} or type(requested.get("cpu")) is not int or not 1 <= requested["cpu"] <= 256 or type(requested.get("memory_mb")) is not int or not 1 <= requested["memory_mb"] <= 16384): raise ValueError("resource_requests requires integer CPU cores1–256 and memory_mb1–16384 for Kubernetes cpuPool") return requested def container_manifest(image="nginx:1.27-alpine", *, app_id=None, name=None, cpu=1, memory_mb=256): return { "appId": app_id or str(uuid4()), "name": name or f"e2e_web_{secrets.token_hex(3)}", "appProvider": "e2e_app_provider", "version": "1.0.0", "packageType": "CONTAINER", "appRepo": {"type": "PUBLICREPO", "imagePath": image}, "requiredResources": { "infraKind": "kubernetes", "isStandalone": False, "applicationResources": {"cpuPool": { "numCPU": cpu, "memory": memory_mb, "topology": {"minNumberOfNodes": 1, "minNodeCpu": 1, "minNodeMemory": 256}, }}, }, "componentSpec": [{"componentName": "web", "networkInterfaces": [ {"interfaceId": "http", "protocol": "TCP", "port": 80, "visibilityType": "VISIBILITY_EXTERNAL"}]}], }
ansible/tests/r2_e2e/profiles/deploy_to_ti.example.json +3 −3 Changes for ansible/tests/r2_e2e/profiles/deploy_to_ti.example.json: 3 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ "image_digest": "sha256:REPLACE_WITH_REVIEWED_OEG_IMAGE_DIGEST" }, "sdk": { "revision": "c748ee15857eead260d933f75f59516e5698f929", "version": "2.0.0rc2", "sha256": "d5dfd568b89e8368217cb5094f9a48c1170c97aed87fd01f388b119409125eb9" "revision": "REPLACE_WITH_REVIEWED_SDK_SOURCE_SHA", "version": "REPLACE_WITH_REVIEWED_CANDIDATE_VERSION", "sha256": "REPLACE_WITH_REVIEWED_SDK_CONTENT_FINGERPRINT" }, "ti": { "application_wide_supported": false, Loading
ansible/tests/r2_e2e/sdk_scenario.py +7 −22 Changes for ansible/tests/r2_e2e/sdk_scenario.py: 7 added lines, 22 removed lines. Original line number Diff line number Diff line Loading @@ -9,6 +9,8 @@ import time from urllib.parse import urlparse from uuid import uuid4 from app_manifest import container_manifest, validated_resource_requests def load_profile(path, checkpoint): profile = json.loads(Path(path).read_text()) Loading Loading @@ -39,6 +41,7 @@ def load_profile(path, checkpoint): or not 1 <= profile["timeout_seconds"] <= 900 ): raise ValueError("timeout_seconds must be between 1 and 900") validated_resource_requests(profile) if checkpoint == "ti": ti = profile["ti"] if ti["application_wide_supported"] is not True: Loading Loading @@ -229,28 +232,10 @@ class Scenario: active = [z for z in zones.json() if z["edgeCloudZoneStatus"] == "active"] assert len(active) == 1, "The dedicated lab must expose exactly one active local zone" self.zone_id = active[0]["edgeCloudZoneId"] manifest = { "appId": self.app_id, "name": self.name, "appProvider": "sdk_e2e_provider", "version": "1.0.0", "packageType": "CONTAINER", "appRepo": {"type": "PUBLICREPO", "imagePath": self.profile["application_image"]}, "requiredResources": {"infraKind": "container", "numCPU": "100m", "memory": 128}, "componentSpec": [ { "componentName": "web", "networkInterfaces": [ { "interfaceId": "http", "protocol": "TCP", "port": 80, "visibilityType": "VISIBILITY_EXTERNAL", } ], } ], } manifest = container_manifest( self.profile["application_image"], app_id=self.app_id, name=self.name, **validated_resource_requests(self.profile) ) # A unique caller-supplied ID permits bounded cleanup after a lost response. self.registered = True response = self.call("POST", self.apps, json=manifest) Loading
ansible/tests/r2_e2e/test_apps.py +1 −21 Changes for ansible/tests/r2_e2e/test_apps.py: 1 added line, 21 removed lines. Original line number Diff line number Diff line """App Providers register applications at OEG, which keeps them in SRM's catalog.""" import secrets import pytest from app_manifest import container_manifest as _manifest from conftest import http APPS = "/edge-application-management/v0.1alpha1/apps" def _manifest(): return { "name": f"e2e_nginx_{secrets.token_hex(3)}", "appProvider": "e2e_app_provider", "version": "1.0.0", "packageType": "CONTAINER", "appRepo": {"type": "PUBLICREPO", "imagePath": "nginx:1.27-alpine"}, "requiredResources": { "infraKind": "kubernetes", "isStandalone": False, "applicationResources": {"cpuPool": { "numCPU": 1, "memory": 256, "topology": {"minNumberOfNodes": 1, "minNodeCpu": 1, "minNodeMemory": 256}, }}, }, "componentSpec": [{"componentName": "web", "networkInterfaces": [ {"interfaceId": "http", "protocol": "TCP", "port": 80, "visibilityType": "VISIBILITY_EXTERNAL"}]}], } @pytest.fixture def registered_app(operators, callers): Loading