Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Container Platform defines the contract for an **Online Shop** release composed

The project does not build application code or operate a Kubernetes cluster. Its target workflow turns images published to Docker Hub into a versioned release bundle pinned by digests for review, validation and promotion to the SRE Platform.

> **Status:** Release contract v1 is defined; a digest-pinned baseline candidate and profile inventory are available for review. Offline and opt-in registry metadata checks, PR checks and offline Compose rendering are available; required-check rules are owner-configured. Runtime integration and SRE handoff remain planned.
> **Status:** Release contract v1 is defined; a digest-pinned baseline candidate and profile inventory are available for review. Offline and opt-in registry metadata checks, PR checks, Compose rendering and an opt-in runtime runner are available; required-check rules are owner-configured. Runtime evidence remains subject to recorded live results; SRE handoff remains planned.

## Why this platform exists

Expand Down Expand Up @@ -39,7 +39,7 @@ The two paths into the SRE Platform have different roles:
- Docker Hub is the data path: Kubernetes nodes pull the approved images from `dmitryzhuravlev/online-shop-*`.
- Container Platform is the release-control path: it identifies the exact image digests that are approved together as one Online Shop release.

The diagram describes target relationships. Offline, opt-in Docker Hub metadata, PR checks and Compose rendering are implemented; required-check configuration remains an owner action. Runtime integration and SRE handoff remain planned.
The diagram describes target relationships. Offline, opt-in Docker Hub metadata, PR checks and Compose rendering are implemented; required-check configuration remains an owner action. Runtime evidence is reported separately; SRE handoff remains planned.

## Responsibilities

Expand Down Expand Up @@ -120,3 +120,7 @@ Add `--registry` to inspect immutable manifest/config bytes and declared platfor
## Compose configuration

[Offline Compose rendering](docs/compose-rendering.md) generates all 12 digest-pinned services from a candidate and the packaged AML profile. Legacy baseline rendering requires explicit `--inspection-only`; its validation remains negative. Static `docker compose config` checks do not establish runtime compatibility or release approval.

## Optional runtime smoke

[Bounded runtime smoke](docs/runtime-smoke.md) requires explicit `--execute`, consumes the rendered candidate snapshot and cleans up its unique Compose project. It checks service process state and a synthetic catalog/cart/checkout flow. Inspection-only baseline execution preserves legacy ineligibility. See the recorded live outcome and limitations; runtime success is separate from release approval and deployment authorization.
2 changes: 1 addition & 1 deletion docs/compose-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Compose bytes go only to `--output`; diagnostics/report go to stdout. Invalid in

Offline tests cover deterministic bytes, exact mapping and candidate digests, service addresses, loopback exposure, safe model fields, invalid inputs, inspection restrictions and output protection. Package checks exercise both rendering interfaces and profile/schema resources from a clean installed wheel outside checkout.

The actual baseline static Compose check result is recorded below. No image pull, container execution, build or publication is part of these checks. Registry availability remains a separate opt-in validator operation; runtime smoke tests, resource sizing, shutdown/cleanup, application compatibility and deployment authorization remain future work.
The actual baseline static Compose check result is recorded below. No image pull, container execution, build or publication is part of these checks. Registry availability remains a separate opt-in validator operation. The [separate runtime runner](runtime-smoke.md) requires explicit execution opt-in and records its own result; static evidence does not establish application compatibility or deployment authorization.

### Static observation: 2026-10-20

Expand Down
2 changes: 1 addition & 1 deletion docs/release-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The pull reference is `repository@digest`. Tags are not accepted selectors. For

1. **Structure:** parse strictly and validate against the pinned v1 schema. This establishes field shape only.
2. **Offline release policy and opt-in registry metadata (implemented):** resolve the pinned local inventory, validate its structure and exact membership/allowlists, identity uniqueness, declared platforms and traceability eligibility. Reject examples and unresolved references. The CLI does not establish inventory approval. With explicit registry opt-in, manifest/config availability, bytes and kind/platform metadata are checked after shared prerequisites pass, independently of traceability eligibility. Layer availability is not checked.
3. **Configuration and integration:** [offline Compose rendering](compose-rendering.md) generates digest-pinned configuration and supports static Compose checks. Explicit legacy inspection preserves negative release validation. Runtime integration remains planned; it must record evidence tied to the exact candidate revision and image set. Structure, static configuration and registry success do not establish service compatibility.
3. **Configuration and integration:** [offline Compose rendering](compose-rendering.md) generates digest-pinned configuration and supports static Compose checks. The [opt-in runtime runner](runtime-smoke.md) records bounded service/HTTP evidence for the exact candidate bytes and image set. Explicit legacy inspection preserves negative release validation. Runner availability is distinct from a successful live observation. Structure, static configuration and registry success do not establish service compatibility.
4. **Container-release review:** maintainers review composition, policy results and required integration evidence. Approval is external review evidence tied to the exact manifest/revision, not a self-asserted manifest flag. Only a release PR reviewed by authorized maintainers with all documented mandatory checks passing may use merge as the approval event. Git merge itself performs no validation. Until those checks and review gates are implemented, merging this contract or an example approves no deployable release.
5. **SRE promotion (planned):** SRE separately authorizes suitability for a specific environment through a reviewed GitOps change. It must record the full immutable Container Platform commit SHA and manifest path, and consume exactly that image set. A moving branch/tag is insufficient. Checking rendered SRE values against the manifest, making that check mandatory and protecting merges remain future implementation; bypasses are not blocked today.
6. **Deployment and runtime:** Argo CD applies environment configuration; Kubernetes pulls from Docker Hub. SRE evaluates rollout health, observability, data/state constraints and operational acceptance.
Expand Down
2 changes: 1 addition & 1 deletion docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Invalid, incomplete, mutable, or unavailable releases cannot be presented as val

**Goal:** Make release validation repeatable and prove that the published images compose into an application.

[Pull request checks](pull-request-validation.md) provide tests/package and new-candidate validation. [Offline Compose rendering](compose-rendering.md) provides deterministic configuration from digest-pinned candidates. Required-check settings are owner-configured; runtime integration remains unimplemented, so this milestone is not complete.
[Pull request checks](pull-request-validation.md) provide tests/package and new-candidate validation. [Offline Compose rendering](compose-rendering.md) provides deterministic configuration from digest-pinned candidates. An [opt-in bounded runtime runner](runtime-smoke.md) is available; its live result and limitations are recorded separately. Required-check settings are owner-configured; runner availability does not complete this milestone or prove baseline runtime compatibility.

### Scope

Expand Down
92 changes: 92 additions & 0 deletions docs/runtime-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Opt-in Online Shop runtime smoke

`container-smoke` / `python -m container_platform.smoke` runs one temporary Compose project. This is integration evidence for a selected image set, not a long-lived environment or SRE deployment. The existing validator and renderer never start Docker.

## Prerequisites and execution

Install the package with Python 3.11+ and Git. Supply local inventory Git objects, a local Docker Engine with native Linux/amd64 runtime, and Docker Compose v2 supporting JSON config/ps and pull policy options. Only local Unix socket/named pipe transports are supported; SSH/TCP remote contexts are rejected before mutations. The runner does not install, start or reconfigure system tools. Loopback port 8080 must be free; a conflict never stops another project. A race after the port check is reported as startup failure.

```text
python -m container_platform.smoke candidate.json --repository . --profile aml-online-shop-compose-v1 --execute
container-smoke releases/baseline-2026-10-18.json --repository . --profile aml-online-shop-compose-v1 --execute --inspection-only
```

Without `--execute`, no candidate read or Docker call occurs (exit 2). Ordinary execution requires successful offline validation. Inspection-only permits exclusively the renderer's existing legacy traceability failures; all other prerequisites must pass. It preserves the full negative offline result and grants no eligibility exception. Baseline eligibility remains negative even if runtime succeeds.

The input is capped at 1 MiB and read once into a private snapshot. The existing renderer validates that snapshot and generates the only accepted Compose file. Report SHA-256 identifies its exact bytes. No external Compose file is accepted. All 12 required services use candidate `repository@sha256:...` references and `linux/amd64`. Pull downloads these images; up prohibits builds and further pulls. Images remain cached after the test.

Each execution generates a unique `cp-smoke-<random hex>` project and checks its resource labels before creating resources. Commands use an explicit Compose file, project directory, empty env file and project name. Inherited `COMPOSE_*` selectors and `DOCKER_DEFAULT_PLATFORM` are removed. Docker client context/transport configuration remains the operator's responsibility; no credentials or environment are emitted in reports. Frontend-only loopback exposure, ephemeral Redis storage and the AML-derived [profile](compose-rendering.md) are preserved. No foreign networks, named volumes, host binds, Docker socket, privileged containers or auxiliary images are requested.

## Budgets and checks

All retries share monotonic deadlines; they never restart a stage budget.

| Operation | Limit |
|---|---:|
| Overall coordination | 1,200 seconds |
| Prerequisites | 30 seconds |
| Pull | 600 seconds |
| Startup | 120 seconds |
| Readiness | 180 seconds |
| Entire semantic smoke sequence | 60 seconds |
| Each HTTP request, including redirects/body | 10 seconds |
| Diagnostics | 20 seconds |
| Cleanup, including resource verification | 120 seconds |

Work stops before the overall deadline to reserve diagnostics and cleanup time. Child process termination/reaping can add at most ten seconds per timed-out process; one HTTP socket read can add up to one second. These are bounded termination grace, not retry budgets. Process output is capped at 1 MiB per command; HTTP responses at 2 MiB. Raw process output, response bodies and cookies are not included in the report. Diagnostics collect only sanitized service states; no application logs or payment/session payloads are published.

Readiness requires exactly 12 expected service records, the exact image references, every service running and a frontend catalog response. This establishes process state plus frontend availability, not independent health evidence for every backend. Host-side semantic assertions then exercise catalog browsing, a selected product, empty/add/remove/re-add cart operations and synthetic checkout. A final service-state check detects an exit during the sequence. Loadgenerator traffic is independent and cannot satisfy these assertions.

Routes/forms and assertions derive from immutable AML revision [`9960484b5c7f1568688fa6fc8eb256976d6c18a4`](https://github.com/DimitryZH/application-modernization-lab/tree/9960484b5c7f1568688fa6fc8eb256976d6c18a4/workloads/online-shop/src): frontend routes/handlers, product/cart/order templates and loadgenerator. Pinned AML source implements a pretend charge and dummy email default; legacy image runtime behavior remains unverified until live checks pass. The runner uses a standard test card number, reserved `.invalid` email and synthetic address, without payment/mail credentials. It checks selected product ID, cart quantity, order confirmation/tracking and post-checkout cart clearance. It does not fetch browser assets, execute scripts or follow redirects outside `http://127.0.0.1:8080`. This is a narrow demo flow, not verification of real payment/email systems or image provenance.

## Report and exit codes

Stdout contains a separate versioned JSON report: release ID and exact candidate-byte SHA-256, inventory reference, Compose profile, exact image set, inspection mode, original offline validation, check/reason codes, sanitized service states, smoke assertions, runtime outcome, cleanup outcome and overall exit code. Input or resource failures may leave unavailable identity fields null. No output paths, environment dump or raw Docker errors are included.

| Exit | Meaning for this command |
|---|---|
| 0 | Runtime flow and verified cleanup succeeded |
| 1 | Release validation or application/semantic failure |
| 2 | Missing opt-in, prerequisite/execution error, or cleanup failure after runtime success |
| 3 | Deadline/request timeout |
| 130 | Handled interruption |

These codes do not change validator/render codes. A report can have runtime success and exit 2 when cleanup fails. A cleanup failure never overwrites the original runtime failure; inspect both fields. No outcome grants release approval or deployment authorization.

## Cleanup and interruptions

After an attempted startup, a finally block runs project-scoped `down --volumes --remove-orphans --timeout 10` and checks containers, networks and volumes by the unique project label. No global prune or image deletion is performed. Before startup, cleanup is `not_required`: only read operations and possibly an image pull have occurred. Failed cleanup is explicit and cannot produce overall success. Ctrl+C and SIGTERM are handled; a second interruption can prevent completion and is reported where possible.

Power loss, forced process termination or an unavailable Engine can prevent cleanup. Each report includes `project` and an exact `manualCleanupPowerShell` command using only that project's label, independent of temporary files. Preserve the project identifier if terminating externally. If no final report was emitted, locate only labels starting with `cp-smoke-`, identify the interrupted run, and substitute its exact identifier below; never substitute another project or omit the filter:

```powershell
$smokeProject = 'cp-smoke-<exact-run-hex>'
$smokeLabel = "label=com.docker.compose.project=$smokeProject"
docker ps -aq --filter $smokeLabel | ForEach-Object { docker rm -f -v $_ }
docker network ls -q --filter $smokeLabel | ForEach-Object { docker network rm $_ }
docker volume ls -q --filter $smokeLabel | ForEach-Object { docker volume rm $_ }
```

## Validation evidence

Automated tests use mock Docker/HTTP adapters and isolated local Git fixtures; the ordinary suite requires no Docker or registry requests. Installed-wheel checks exercise both smoke interfaces outside checkout without runtime opt-in. No mandatory runtime PR job is added. Live results are recorded separately below; runtime integration cannot be claimed from mocks or static configuration alone.

### Live observation: 2026-10-21

- Implementation: `f5ea3b8e2b2f48e014cd985c3a20412bbdce0956`, installed-wheel code/profile/schema matched before execution.
- Input: `releases/baseline-2026-10-18.json`, byte SHA-256 `005293954afebcf1581116b2e7e6bc5ec6313a658496c88df3186aa84a46a99f`; profile `aml-online-shop-compose-v1`, explicit inspection-only.
- Render passed; prerequisites failed with `docker_engine_unavailable`. Runtime outcome: `execution_error`, exit 2. Pull/startup and HTTP smoke were not attempted.
- Cleanup: `not_required`; no project resources were created.
- Offline validation remained exit 1 with 12 `legacy_ineligible` failures. No approval or eligibility was granted.

One bounded live attempt was made. Provide a running local Linux/amd64 Docker Engine and repeat the documented baseline command to verify readiness, semantic checkout and cleanup after actual startup. Those live behaviors remain unverified; mock tests do not replace them.

### Live observation: 2026-10-21 — successful follow-up

- Implementation: `9f28d77df64106ddefa52c2934c132d9cb022bb0`; installed code, profile and schema matched the commit before execution.
- Input: `baseline-2026-10-18-001`, `releases/baseline-2026-10-18.json`, byte SHA-256 `005293954afebcf1581116b2e7e6bc5ec6313a658496c88df3186aa84a46a99f`; inventory `inventories/online-shop-sre-dev-v1.json` at `480bd0a5769ae61313c4bd0da846ba3a6fc0ce51`; profile `aml-online-shop-compose-v1`, inspection-only.
- One bounded run passed in 45.8 seconds: prerequisites, pull, startup, all 12 services running with exact candidate digest references, readiness and semantic catalog/product/cart/synthetic-checkout assertions. Runtime exit 0.
- Cleanup passed; an independent label-scoped query confirmed zero remaining project containers, networks and volumes. Cached images were retained.
- Offline validation remained exit 1 with 12 `legacy_ineligible` failures. The earlier negative observation is retained above.

This confirms the selected demo flow for these exact inputs in one run. It does not establish production readiness, performance, full backend coverage, provenance, release approval or deployment authorization. No mandatory runtime CI job was added.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies = ["jsonschema>=4.23,<5", "referencing>=0.35,<1"]
[project.scripts]
container-release = "container_platform.cli:main"
container-compose = "container_platform.compose:main"
container-smoke = "container_platform.smoke:main"

[tool.setuptools]
packages = ["container_platform", "container_platform.schemas", "container_platform.profiles"]
Expand Down
6 changes: 6 additions & 0 deletions scripts/check_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ def check(repository, wheel_directory):
artifacts.append(destination.read_bytes())
if artifacts[0] != artifacts[1]:
raise RuntimeError("Installed rendering interfaces differ.")
smoke_console = str(binaries / ("container-smoke.exe" if os.name == "nt" else "container-smoke"))
for command in ([python, "-m", "container_platform.smoke"], [smoke_console]):
report = json.loads(run([*command, str(repo / "releases/baseline-2026-10-18.json"),
"--repository", str(repo), "--profile", profile], outside, env, expected=2))
if report["diagnostics"] != ["runtime_opt_in_required"] or report["runtimeOutcome"] != "not_started":
raise RuntimeError("Installed smoke interface must require explicit runtime opt-in.")


def main():
Expand Down
Loading