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
10 changes: 7 additions & 3 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 are implemented; Pull request checks are available; required-check rules are owner-configured. Compose and SRE integrations 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 and offline Compose rendering are available; required-check rules are owner-configured. Runtime integration and SRE handoff remain 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. The offline validator and opt-in Docker Hub metadata checks are implemented. GitHub Actions validation checks are available; required-check configuration remains an owner action. Compose 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 integration and SRE handoff remain planned.

## Responsibilities

Expand Down Expand Up @@ -91,7 +91,7 @@ See the [roadmap](docs/roadmap.md) for the planned implementation sequence.
- [Version 1 JSON Schema](schemas/release-v1.schema.json)
- [Non-deployable synthetic example](examples/release-v1.example.json)

The existing Dockerfiles and build/push scripts are empty, and Compose contains local-build placeholders. Their disposition is documented in the architecture; they are not a working release pipeline.
The existing Dockerfiles and build/push scripts are empty; root Compose contains legacy local-build placeholders. Use the [generated Compose workflow](docs/compose-rendering.md) for the supported profile. The scaffold is not a working release pipeline.

## Baseline candidate

Expand All @@ -116,3 +116,7 @@ Add `--registry` to inspect immutable manifest/config bytes and declared platfor
## Pull request checks

[GitHub Actions validation](docs/pull-request-validation.md) runs tests/package checks on Python 3.11 and 3.13 and a stable release check. New candidates receive opt-in registry validation; merged release files are immutable. No candidate changes produces an explicit successful result. The historical legacy baseline remains a negative regression fixture. Workflow availability does not configure required merge checks or grant release approval.

## 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.
2 changes: 2 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Legacy local-build scaffold; not the supported Online Shop Compose profile.
# Canonical workflow: generate Compose JSON as documented in docs/compose-rendering.md.
version: "3.8"

services:
Expand Down
43 changes: 43 additions & 0 deletions docs/compose-rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Offline Compose rendering

The canonical Compose path is generated JSON, not the root `docker-compose.yaml` legacy scaffold. Install the wheel or use `python -m pip install .` with Python 3.11+ and local Git history containing the candidate's inventory objects.

## Profile and inputs

The packaged `aml-online-shop-compose-v1` profile derives topology, ports and environment settings from [canonical AML Online Shop](https://github.com/DimitryZH/application-modernization-lab/tree/9960484b5c7f1568688fa6fc8eb256976d6c18a4/workloads/online-shop), pinned at `9960484b5c7f1568688fa6fc8eb256976d6c18a4`. The [profile resource](../src/container_platform/profiles/aml-online-shop-compose-v1.json) records immutable README, Kubernetes manifest, image mapping and upstream-record references. Render performs no source fetch, registry request or Docker call.

This Compose profile is distinct from inventory profile `sre-dev-online-shop-v1`. Inventory supplies required membership, repository allowlists and platforms; the candidate supplies every exact repository and digest. Matching images do not imply runtime configuration equivalence with SRE or Kubernetes.

All 12 components are required, including loadgenerator (10 users, rate 1). Compose DNS uses component IDs. Container ports replace Kubernetes Service translation: frontend `80 -> 8080` becomes `frontend:8080`, and email `5000 -> 8080` becomes `emailservice:8080`. Only frontend publishes `127.0.0.1:8080:8080`. Redis `/data` is ephemeral tmpfs, with no persistence or host bind. Assistant is disabled with the AML loopback sentinel; profiler settings follow AML. No credentials, assistant/cloud services, extra images or startup helper images are added.

`depends_on` expresses startup order only. No readiness guarantees or healthcheck tools are assumed. Loadgenerator can encounter initial connection failures and would send synthetic checkout traffic if run. Static configuration is not runtime compatibility evidence.

## Commands and results

```text
python -m container_platform.compose candidate.json --repository . --profile aml-online-shop-compose-v1 --output compose.generated.json
container-compose candidate.json --repository . --profile aml-online-shop-compose-v1 --output compose.generated.json
```

Ordinary render requires all offline validation stages, including traceability. Examples are always rejected. Unsupported profiles, incompatible membership/platforms and malformed configuration fail before output is created. Candidate fields cannot supply commands, environment, mounts or includes.

For explicit inspection of the committed legacy baseline:

```text
python -m container_platform.compose releases/baseline-2026-10-18.json --repository . --profile aml-online-shop-compose-v1 --output compose.generated.json --inspection-only
docker compose -f compose.generated.json config --format json
```

Inspection-only tolerates exclusively `candidate.traceability` failures with `legacy_ineligible`; every preceding stage must pass. Successful artifact creation returns renderer exit 0 while its separate stdout JSON report retains the complete negative validation result (baseline exit 1 and 12 legacy diagnostics). The artifact records release ID, inventory revision, profile version and inspection mode without timestamps or local paths. It grants no eligibility, exception, approval or deployment authorization. Existing validator and PR release-check exit semantics are unchanged.

Compose bytes go only to `--output`; diagnostics/report go to stdout. Invalid input leaves output untouched. Existing output requires `--overwrite`; candidate, inventory and packaged resource paths cannot be overwritten. Complete output is published atomically. Use an existing writable output directory. No `.env`, interpolation, external includes or runtime files are required.

## Verification and limits

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.

### Static observation: 2026-10-20

Docker Compose `v2.40.3-desktop.1` successfully normalized the inspection-only artifact generated from `releases/baseline-2026-10-18.json` at repository revision `d3cb1e755fabfabc82718106a86582074227c35f` with profile `aml-online-shop-compose-v1`. Normalized output contained exactly 12 services and the exact 12 candidate image references, frontend-only loopback publishing, no build directives and no additional images. Rendering returned 0; the embedded offline validation remained exit 1 with 12 legacy-ineligibility diagnostics. This is a configuration check, not integration/runtime validation or baseline approval.
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. **Integration (planned):** bounded Compose checks consume the selected published images. Record evidence tied to the exact candidate revision and image set. Structure 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. 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.
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. Required-check settings are owner-configured; Compose 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. Required-check settings are owner-configured; runtime integration remains unimplemented, so this milestone is not complete.

### Scope

Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ dependencies = ["jsonschema>=4.23,<5", "referencing>=0.35,<1"]

[project.scripts]
container-release = "container_platform.cli:main"
container-compose = "container_platform.compose:main"

[tool.setuptools]
packages = ["container_platform", "container_platform.schemas"]
packages = ["container_platform", "container_platform.schemas", "container_platform.profiles"]

[tool.setuptools.package-dir]
container_platform = "src/container_platform"
"container_platform.schemas" = "schemas"

[tool.setuptools.package-data]
"container_platform.schemas" = ["release-v1.schema.json"]
"container_platform.profiles" = ["*.json"]
17 changes: 17 additions & 0 deletions scripts/check_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ def check(repository, wheel_directory):
reports.append(report)
if reports[0] != reports[1]:
raise RuntimeError("Installed module and console results differ.")
profile = "aml-online-shop-compose-v1"
resource = run([python, "-c", "from importlib.resources import files; print(files('container_platform.profiles').joinpath('aml-online-shop-compose-v1.json').read_text(encoding='utf-8'), end='')"], outside, env)
if resource != (repo / "src/container_platform/profiles" / (profile + ".json")).read_text(encoding="utf-8"):
raise RuntimeError("Packaged Compose profile differs from its source.")
compose_console = str(binaries / ("container-compose.exe" if os.name == "nt" else "container-compose"))
artifacts = []
for index, command in enumerate(([python, "-m", "container_platform.compose"], [compose_console])):
destination = outside / f"compose-{index}.json"
report = json.loads(run([*command, str(repo / "releases/baseline-2026-10-18.json"),
"--repository", str(repo), "--profile", profile,
"--output", str(destination), "--inspection-only"], outside, env))
if report["exitCode"] != 0 or report["validation"]["exitCode"] != 1 or not report["inspectionOnly"]:
raise RuntimeError("Installed Compose inspection semantics changed.")
artifacts.append(destination.read_bytes())
if artifacts[0] != artifacts[1]:
raise RuntimeError("Installed rendering interfaces differ.")


def main():
Expand All @@ -67,6 +83,7 @@ def main():
"### Tests and package\n\n"
+ ("Pass" if passed else "Fail")
+ ": automated offline tests; clean installed wheel; module/console and schema outside checkout.\n"
"Compose: installed offline renderer/profile; baseline inspection preserves negative validation.\n"
"Baseline: expected negative regression, not release eligibility.\n",
encoding="utf-8",
)
Expand Down
Loading