Skip to content

About

No description or website provided.

Topics

Resources

Security policy

Stars

9 stars

Watchers

11 watching

Forks

Latest commit

 

History

212 Commits

Folders and files

Repository files navigation

polytope-server

A Rust workspace containing a Polytope frontend plus separate worker crates, backed by Bits for request routing and processing, and Bobs for efficient data staging.

Static Badge

Important

This software is Incubating and subject to ECMWF's guidelines on Software Maturity.

Workspace layout

This repository is intentionally split so the frontend and workers can be moved independently later.

  • frontend/ — the Polytope HTTP frontend crate (polytope-server binary, frontend image)
  • workers/common/ — shared remote-worker runtime and protocol client
  • workers/polytope-fe-worker/ — Polytope worker crate
  • workers/fdb-worker/ — FDB worker crate
  • workers/mars-worker/ — Mars worker crate (requires native eckit/metkit C++ libraries; excluded from default workspace build)
  • workers/test-worker/ — Test worker crate for integration tests
  • loadgen/ — load-generation crate (loadgen binary, polytope-loadgen image)
  • observability/, client/, tests/integration/ — shared support crates (telemetry helpers, client library, integration test harness)

The frontend does not depend on worker crates. The only shared worker-side dependency is workers/common/.

Prerequisites

Build

Build the whole workspace (excludes mars-worker, which requires native C++ libraries):

cargo build --release

Build a single crate:

cargo build -p polytope-server
cargo build -p polytope-fe-worker
cargo build -p fdb-worker
cargo build -p mars-worker   # requires eckit/metkit C++ libraries installed

The workspace produces separate binaries under target/release/:

  • polytope-server
  • polytope-fe-worker
  • fdb-worker
  • mars-worker
  • test-worker
  • loadgen

Configuration

The frontend is configured with a single YAML file. The top-level server block controls the HTTP listener. The polytope block identifies the deployment with stable one-to-three-character lower-case alphanumeric site and env tags used for opaque request IDs. The bits block is passed directly to the bits routing engine. The collections block maps collection names to bits route pipelines — each collection gets its own route, sharing the same action registries and target instances.

server:
  host: "0.0.0.0"
  port: 3000

polytope:
  site: bol
  env: dev

bits:
  # Keep these in sync with polytope.site/env until server config plumbing
  # injects them automatically.
  site: bol
  env: dev

collections:
  climate:
    - target::http:
        url: "http://climate-backend/api"
  operational:
    - check::has_role:
        roles:
          ecmwf:
            - admin
    - target::http:
        url: "http://ops-backend/api"

See config.example.yaml for a starting point, docs/request-ids.md for request ID and rollout guidance, and the bits documentation for the full bits config schema.

Running

Frontend:

cargo run -p polytope-server -- config.yaml

Workers:

cargo run -p polytope-fe-worker -- --broker-url http://127.0.0.1:9001 --config-path worker-config.yaml
cargo run -p fdb-worker -- --broker-url http://127.0.0.1:9001 --config-path worker-config.yaml
cargo run -p mars-worker -- --broker-url http://127.0.0.1:9001 --config-path worker-config.yaml

Set RUST_LOG to control log verbosity:

RUST_LOG=info cargo run -p polytope-server -- config.yaml

Metrics

Build the frontend with --features telemetry and enable metrics: in the config to expose Prometheus at /metrics.

See docs/metrics.md for the raw metric reference and dev/otel/grafana/dashboards/raw-metrics.json for the raw Grafana dashboard.

API

The frontend exposes the legacy v1 and newer v2 HTTP APIs. Optional frontends can also expose OpenMeteo, EDR, and MCP-compatible facades over the same BITS routing engine.

v1 (legacy)

OpenAPI description: docs/openapi/v1.yaml. It is hand-maintained and checked against the running handlers by frontend/tests/v1_openapi_contract.rs.

  • GET /api/v1/test
  • GET /api/v1/collections
  • GET /api/v1/user
  • GET /api/v1/requests
  • POST /api/v1/requests/{collection}
  • GET /api/v1/requests/{id}
  • DELETE /api/v1/requests/{id}
  • GET /api/v1/downloads/{id} (deprecated)
  • GET, POST /api/v1/uploads/{id} (unsupported)

v2

  • GET /api/v2/health
  • GET /api/v2/collections
  • POST /api/v2/{collection}/requests
  • GET /api/v2/requests/{id}
  • DELETE /api/v2/requests/{id}

v2 routes requests through the named collection — each collection maps to a separate bits route pipeline. The collection name must match a key in the collections config block.

Request IDs returned by these APIs are opaque strings. Clients should pass them back unchanged to status, cancel, and download routes, but must not parse broker identity, site, environment, or ordering from the ID text.

Successful responses are streamed back to the client over HTTP.

MCP

When mcp: is configured, the frontend exposes a Model Context Protocol endpoint at /mcp. The MCP frontend is asynchronous-first: agents submit retrievals, poll opaque request IDs, and receive download URLs or small inline JSON/text results. See docs/mcp.md and examples/mcp-config.yaml.

Images

App images

skaffold.yaml builds separate images for the frontend, each worker, and the load generator from the same Rust workspace:

Image Binary Root crate
eccr.ecmwf.int/polytope/frontend polytope-server frontend
eccr.ecmwf.int/polytope/polytope-fe-worker polytope-fe-worker workers/polytope-fe-worker
eccr.ecmwf.int/polytope/fdb-worker fdb-worker workers/fdb-worker
eccr.ecmwf.int/polytope/mars-worker mars-worker workers/mars-worker
eccr.ecmwf.int/polytope/test-worker test-worker workers/test-worker
eccr.ecmwf.int/polytope/polytope-loadgen loadgen loadgen

Releases

The repository has a single version: the git tag of a GitHub release (semantic version without a prefix: 2.6.0, or 2.6.0-rc1 for a pre-release; dotted suffixes such as 2.6.0.dev0 are not releases). Images are built only by the release.yaml workflow, which runs when a semver tag is pushed. To cut a release, tag a commit on main:

git tag 2.6.0 <sha> && git push origin 2.6.0

The workflow then:

  1. computes a content fingerprint for every image with tools/release/image-fingerprint.sh — the image's resolved Rust dependency closure (exact crate versions and git revisions), the tree hash of every workspace crate in that closure, its Dockerfile, and the shared build inputs (skaffold.yaml, the root Cargo.toml, .dockerignore, the Buildx wrapper);
  2. compares each fingerprint with the one recorded by the previous published release (drafts are ignored);
  3. builds and pushes only the images whose fingerprint changed, tagged with the release tag, and carries the previous tag + digest forward for the rest (an image that has disappeared from the registry is rebuilt instead);
  4. creates the GitHub release as a draft on the tag, with generated notes, the image table and images.json attached.

A human reviews the draft and its images.json and publishes it from the release page (or gh release edit 2.6.0 --draft=false). Only a published release is deployable; deployment config should take tags (or digests) from the latest published release's images.json:

{
  "release": "2.6.1",
  "previous": "2.6.0",
  "images": {
    "frontend":    { "image": "eccr.ecmwf.int/polytope/frontend",    "tag": "2.6.1", "digest": "sha256:…", "fingerprint": "…", "revision": "<commit>", "changed": true },
    "mars-worker": { "image": "eccr.ecmwf.int/polytope/mars-worker", "tag": "2.6.0", "digest": "sha256:…", "fingerprint": "…", "revision": "<commit>", "changed": false }
  }
}

So an image tag always names the release whose source built it, and a release that only bumps a frontend dependency publishes only a new frontend. Tags — git and image — are immutable: if a release fails or the draft is rejected, fix forward and use the next version (delete the stray draft, if any, so it is not mistaken for a release). Re-running a failed workflow on the same tag is safe: images already pushed from that commit are skipped.

Pull requests and pushes to main do not build images: CI compiles the workspace (cargo check --workspace --all-targets --features polytope-server/telemetry) and lints every Dockerfile (docker buildx build --check) instead. mars-worker, utils/metkit and the frontend's metkit feature need native MARS libraries and are only compiled by the release build.

There is nothing to bump before cutting a release. The version field in each crate's Cargo.toml is not used for images — these crates are not published to crates.io. Binaries report the image tag they were built with as service.version (skaffold's VERSION build arg, compiled in via polytope_observability::service_version), so a carried-forward image keeps reporting the release that built it.

To see what a release would rebuild before cutting it, run the fingerprint script on a clean checkout and compare with the previous release's images.json:

tools/release/image-fingerprint.sh --all                # "<artifact> <fingerprint>" per image
tools/release/image-fingerprint.sh frontend --verbose   # one image, also lists the inputs

Things to know:

  • The first release after one without images.json rebuilds every image, and a tag on a commit that predates tools/release/ cannot be released by this workflow. The tagged commit must be on main.
  • Only inputs tracked in git are fingerprinted. Mutable external inputs — base images referenced by tag rather than digest, apt packages, unpinned Python requirements — are not, so an unchanged image is never rebuilt to pick up a base-image security fix. To force a rebuild, change the image's Dockerfile (ideally by pinning a newer base image digest).
  • The release notes (generated changelog plus the image table) can be edited freely before or after publishing the draft.

For dev builds, skaffold tags images with the current git commit SHA by default (tagPolicy.gitCommit). Set FIXED_TAG to override, or PREFIX to prepend to the SHA.

BuildKit cache and private dependencies

Application images fetch private Git dependencies, so local builds require a GitHub token with read access to those repositories:

export GH_TOKEN=...

The normal Skaffold build uses the local Docker cache:

skaffold build -b eccr.ecmwf.int/polytope/frontend

To additionally import the shared ECCR BuildKit cache produced by release builds, log in to ECCR and opt into the buildx-cache profile:

docker login eccr.ecmwf.int
skaffold build -p buildx-cache -b eccr.ecmwf.int/polytope/frontend

The profile is read-only by default; only release builds set BUILDKIT_CACHE_MODE=read-write to refresh the affected cache. Do not enable write mode for ordinary local builds. Cache entries are per image and platform, for example eccr.ecmwf.int/polytope/build-cache:frontend-linux-amd64-v1.

C++ library images

The fdb-worker, polytope-fe-worker, and mars-worker images depend on pre-built C++ library images (eckit, metkit, FDB, gribjump, MARS client). These are published separately and versioned independently of the app images — each has its own TAG file under docker/cpp-libs/<name>/TAG.

See docker/cpp-libs/README.md for the full list of images, how to build and publish them, and how to bump a C++ library version.

Each Dockerfile copies only its own crate's sources (not the whole workspace), so a change to the frontend does not rebuild the workers and vice versa.

Future extraction

The current layout is designed so the crates can be moved later with minimal churn:

  • the frontend is self-contained under frontend/
  • each worker is self-contained under its own directory
  • the only in-repo worker dependency is workers/common/

If a worker needs to move to its own repository later, it should mostly be a matter of copying that crate plus workers/common/ (or publishing workers/common/ as its own crate).

License

Apache License 2.0 In applying this licence, ECMWF does not waive the privileges and immunities granted to it by virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.

About

No description or website provided.

Topics

Resources

Security policy

Stars

9 stars

Watchers

11 watching

Forks

Releases

Packages

Used by

Contributors

Languages