Skip to content
This repository was archived by the owner on Jun 17, 2026. It is now read-only.
Closed
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
5 changes: 1 addition & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@
# v0.0.1 - final release (from git tag)
# v0.0.1-rc.3 - release candidate (from release branch)
# abc1234 - specific commit build (for local testing only)
# PLACEMENT_MANAGER_VERSION=main
# SERVICE_PROVIDER_MANAGER_VERSION=main
# CATALOG_MANAGER_VERSION=main
# POLICY_MANAGER_VERSION=main
# CONTROL_PLANE_VERSION=main
# KUBEVIRT_SERVICE_PROVIDER_VERSION=main
# K8S_CONTAINER_SERVICE_PROVIDER_VERSION=main
# ACM_CLUSTER_SERVICE_PROVIDER_VERSION=main
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ check-config:
$(ENGINE) rm -f gateway-validate > /dev/null 2>&1 || \
{ echo "Config validation failed:"; $(ENGINE) logs gateway-validate 2>&1; $(ENGINE) rm -f gateway-validate > /dev/null 2>&1; exit 1; }

# Run full stack (gateway + managers) via Compose. Pulls images and starts the stack.
# Run full stack (gateway + control-plane) via Compose. Pulls images and starts the stack.
run:
$(ENGINE) compose up -d

Expand Down
70 changes: 31 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@ Central clearing house for the DCM control plane: single entry point (ingress) a

## Overview

- **Ingress:** Clients and frontends send REST requests to the gateway; the gateway routes them to internal managers (ServiceProviderManager, PlacementManager, PolicyManager, CatalogManager).
- **Ingress:** Clients and frontends send REST requests to the gateway; the gateway routes them to the **control-plane** monolith (`quay.io/dcm-project/control-plane`).
- **Egress:** Outbound calls from DCM to external systems are intended to go through the gateway (see [Egress](#egress) below). Placeholders only in this deliverable.
- **Stateless:** No server-side sessions; each request is independent.
- **Auth:** Not in scope for the first deliverable; Keycloak (or another IdP) will be added later.

```mermaid
flowchart LR
Client --> Gateway["API Gateway<br>Traefik :9080"]
Gateway --> SPM["ServiceProviderManager<br>service-provider-manager:8080"]
Gateway --> Catalog["CatalogManager<br>catalog-manager:8080"]
Gateway --> Policy["PolicyManager<br>policy-manager:8080"]
Gateway --> Placement["PlacementManager<br>placement-manager:8080"]
Gateway --> CP["control-plane<br>catalog + policy + placement + SP :8080"]
```

## Running the gateway
Expand All @@ -30,7 +27,7 @@ flowchart LR
make validate-config
```

### Run locally (gateway and managers)
### Run locally (gateway and control-plane)

From the `api-gateway` directory, start the **default** Compose stack:

Expand All @@ -39,8 +36,8 @@ cd api-gateway
make run
```

`make run` is equivalent to `podman compose up -d` or `docker compose up -d` (whichever engine you use).
It starts **Traefik**, **PostgreSQL**, **NATS**, and the four **managers** (ServiceProviderManager, CatalogManager, PolicyManager, PlacementManager).
`make run` is equivalent to `podman compose up -d` or `docker compose up -d` (whichever engine you use).
It starts **Traefik**, **PostgreSQL**, **NATS**, and **control-plane** (monolith on `:8080`).

It does **not** start the optional **service provider** containers (`kubevirt-service-provider`, `k8s-container-service-provider`, `acm-cluster-service-provider`).
Those services are declared with [Compose profiles](https://docs.docker.com/compose/how-tos/profiles/) in `compose.yaml` (`kubevirt`, `k8s-container`, `acm-cluster`, or `providers`
Expand All @@ -53,7 +50,8 @@ The gateway is at `http://localhost:9080`. Stop with `make compose-down`. To run

### Image versions

Each DCM manager image defaults to `:main` but can be pinned to a specific version via environment variables in `.env`.
The control-plane image defaults to `:main` but can be pinned via `CONTROL_PLANE_VERSION` in `.env`.
Requires [control-plane](https://github.com/dcm-project/control-plane) image on Quay (see control-plane PR for CI push).

#### Available tag formats

Expand All @@ -73,18 +71,14 @@ Browse available tags for a service at `https://quay.io/repository/dcm-project/<
Set the corresponding variable in `.env` (see `.env.example` for the full list):

```bash
PLACEMENT_MANAGER_VERSION=v0.0.1
CATALOG_MANAGER_VERSION=v0.0.1-rc.3
CONTROL_PLANE_VERSION=v0.0.1
```

Omitting a variable (or leaving it commented out) defaults to `main`.
Omitting the variable defaults to `main`.

| Variable | Service |
|---|---|
| `SERVICE_PROVIDER_MANAGER_VERSION` | service-provider-manager |
| `CATALOG_MANAGER_VERSION` | catalog-manager |
| `POLICY_MANAGER_VERSION` | policy-manager |
| `PLACEMENT_MANAGER_VERSION` | placement-manager |
| `CONTROL_PLANE_VERSION` | control-plane monolith |
| `KUBEVIRT_SERVICE_PROVIDER_VERSION` | kubevirt-service-provider (`kubevirt` or `providers` profile) |
| `K8S_CONTAINER_SERVICE_PROVIDER_VERSION` | k8s-container-service-provider (`k8s-container` or `providers` profile) |
| `ACM_CLUSTER_SERVICE_PROVIDER_VERSION` | acm-cluster-service-provider (`acm-cluster` or `providers` profile) |
Expand All @@ -102,11 +96,11 @@ podman compose config | grep "quay.io/dcm-project"
1. Check available tags on [quay.io/dcm-project](https://quay.io/organization/dcm-project) for the service you want to update.
2. Set the version variable in `.env`:
```bash
PLACEMENT_MANAGER_VERSION=v0.0.1
CONTROL_PLANE_VERSION=v0.0.1
```
3. Restart the stack to pull the new image:
```bash
make run # core stack (gateway + managers)
make run # core stack (gateway + control-plane)
make run-with-providers # or, if running with service providers
```

Expand Down Expand Up @@ -137,7 +131,7 @@ Then mount the ConfigMap into the Traefik pod at `/etc/traefik/traefik.yml` and

### Testing locally

1. **Validate and start the core stack (gateway + managers)**
1. **Validate and start the core stack (gateway + control-plane)**
```bash
make validate-config
make run
Expand All @@ -149,29 +143,27 @@ Then mount the ConfigMap into the Traefik pod at `/etc/traefik/traefik.yml` and
```bash
curl -s http://localhost:9080/ping
```
3. **Health checks (gateway + managers)**
After `make run`, the health endpoints only cover the four managers that are part of the default stack.
For example: `curl -s http://localhost:9080/api/v1alpha1/health/providers`. Stop with `make compose-down`.
3. **Health checks (gateway + control-plane)**
After `make run`, legacy per-domain health paths still work and map to the monolith health endpoint. For example: `curl -s http://localhost:9080/api/v1alpha1/health/providers` or `curl -s http://localhost:9080/api/v1alpha1/health`. Stop with `make compose-down`.

## Route mapping

| Path prefix | Backend |
|------------------------------------------|------------------------|
| `/api/v1alpha1/health/providers` | ServiceProviderManager |
| `/api/v1alpha1/health/catalog` | CatalogManager |
| `/api/v1alpha1/health/policies` | PolicyManager |
| `/api/v1alpha1/health/placement` | PlacementManager |
| `/api/v1alpha1/providers` | ServiceProviderManager |
| `/api/v1alpha1/service-type-instances` | ServiceProviderManager |
| `/api/v1alpha1/service-types` | CatalogManager |
| `/api/v1alpha1/catalog-items` | CatalogManager |
| `/api/v1alpha1/catalog-item-instances` | CatalogManager |
| `/api/v1alpha1/policies` | PolicyManager |
| `/api/v1alpha1/resources` | PlacementManager |

Health paths above are GET-only; other paths support multiple methods (GET, POST, PUT, PATCH, DELETE as per the API). The `catalog-item-instances` prefix also covers AEP custom methods such as `POST /api/v1alpha1/catalog-item-instances/{id}:rehydrate`. See `config/dynamic/routes.yml` for the full list.

**Health:** Backend health is exposed through the gateway. Use `GET /api/v1alpha1/health/providers`, `/health/catalog`, `/health/policies`, `/health/placement` to check each manager (e.g. `curl http://localhost:9080/api/v1alpha1/health/catalog`). Traefik also exposes `GET /ping` for the gateway process only.
| Path prefix | Backend |
|------------------------------------------|----------------|
| `/api/v1alpha1/health` | control-plane |
| `/api/v1alpha1/health/providers` | control-plane |
| `/api/v1alpha1/health/catalog` | control-plane |
| `/api/v1alpha1/health/policies` | control-plane |
| `/api/v1alpha1/providers` | control-plane |
| `/api/v1alpha1/service-type-instances` | control-plane |
| `/api/v1alpha1/service-types` | control-plane |
| `/api/v1alpha1/catalog-items` | control-plane |
| `/api/v1alpha1/catalog-item-instances` | control-plane |
| `/api/v1alpha1/policies` | control-plane |

Placement `/resources` and policy evaluation HTTP are **not** exposed; provisioning runs in-process inside control-plane.

Health paths above are GET-only; other paths support multiple methods. The `catalog-item-instances` prefix also covers `POST ...:rehydrate`. See `config/dynamic/routes.yml` for the full list. Traefik exposes `GET /ping` for the gateway process only.

## Egress

Expand Down
11 changes: 5 additions & 6 deletions RUN.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

## Quick start

Start all core services (gateway, postgres, nats, and all managers):
Start all core services (gateway, postgres, nats, and control-plane):

```bash
make run
Expand Down Expand Up @@ -135,12 +135,14 @@ podman compose ps # or: docker compose ps
Check health endpoints through the gateway:

```bash
curl http://localhost:9080/api/v1alpha1/health
curl http://localhost:9080/api/v1alpha1/health/providers
curl http://localhost:9080/api/v1alpha1/health/catalog
curl http://localhost:9080/api/v1alpha1/health/policies
curl http://localhost:9080/api/v1alpha1/health/placement
```

Legacy per-domain health paths map to the monolith `GET /api/v1alpha1/health`.

Check health endpoint through DCM UI:

```bash
Expand Down Expand Up @@ -173,10 +175,7 @@ make compose-down
| `ACM_CLUSTER_SP_PULL_SECRET` | _(required)_ | Base64-encoded dockerconfigjson pull secret for ACM hosted clusters |
| `ACM_CLUSTER_SP_DEFAULT_INFRA_ENV` | _(none)_ | **BareMetal only.** Default InfraEnv name; can be overridden per-request via `provider_hints.acm.infra_env` |
| `ACM_CLUSTER_SP_AGENT_NAMESPACE` | _(none)_ | **BareMetal only.** Namespace where Agent resources are located |
| `SERVICE_PROVIDER_MANAGER_VERSION` | `main` | Image tag for service-provider-manager |
| `CATALOG_MANAGER_VERSION` | `main` | Image tag for catalog-manager |
| `POLICY_MANAGER_VERSION` | `main` | Image tag for policy-manager |
| `PLACEMENT_MANAGER_VERSION` | `main` | Image tag for placement-manager |
| `CONTROL_PLANE_VERSION` | `main` | Image tag for control-plane monolith |
| `KUBEVIRT_SERVICE_PROVIDER_VERSION` | `main` | Image tag for kubevirt-service-provider |
| `K8S_CONTAINER_SERVICE_PROVIDER_VERSION` | `main` | Image tag for k8s-container-service-provider |
| `ACM_CLUSTER_SERVICE_PROVIDER_VERSION` | `main` | Image tag for acm-cluster-service-provider |
Expand Down
80 changes: 18 additions & 62 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# DCM API Gateway and managers. Manager images are pulled from quay.io/dcm-project.
# DCM API Gateway and control-plane monolith. Image from quay.io/dcm-project/control-plane.
#
# Image versions: each manager defaults to :main. Pin specific versions by
# setting <SERVICE>_VERSION in .env (see .env.example).
# Image version: defaults to :main. Pin with CONTROL_PLANE_VERSION in .env.
#
# Credentials: set POSTGRES_USER and POSTGRES_PASSWORD in the environment or in
# a .env file (see .env.example). Defaults are for local dev only.

# Shared DB connection env (YAML anchor); services merge it with <<: *db-common and add DB_NAME.
x-db-common: &db-common
DB_TYPE: pgsql
DB_HOST: postgres
Expand All @@ -15,6 +13,8 @@ x-db-common: &db-common
DB_PASS: ${POSTGRES_PASSWORD:-adminpass}
DB_PASSWORD: ${POSTGRES_PASSWORD:-adminpass}

x-dcm-registration-url: &dcm-registration-url "http://control-plane:8080/api/v1alpha1"

services:
gateway:
image: docker.io/traefik:v3.4
Expand All @@ -26,10 +26,7 @@ services:
- ./config/traefik.yml:/etc/traefik/traefik.yml:ro,z
- ./config/dynamic:/etc/traefik/dynamic:ro,z
depends_on:
service-provider-manager: { condition: service_started }
catalog-manager: { condition: service_started }
policy-manager: { condition: service_started }
placement-manager: { condition: service_started }
control-plane: { condition: service_started }

postgres:
image: docker.io/library/postgres:16-alpine
Expand Down Expand Up @@ -57,45 +54,20 @@ services:
volumes:
- nats_data:/data

service-provider-manager:
image: quay.io/dcm-project/service-provider-manager:${SERVICE_PROVIDER_MANAGER_VERSION:-main}
pull_policy: always
environment:
<<: *db-common
DB_NAME: service-provider
SVC_ADDRESS: ":8080"
NATS_URL: "nats://nats:4222"
expose:
- "8080"
depends_on:
postgres: { condition: service_healthy }
nats: { condition: service_started }

catalog-manager:
image: quay.io/dcm-project/catalog-manager:${CATALOG_MANAGER_VERSION:-main}
pull_policy: always
environment:
<<: *db-common
BIND_ADDRESS: ":8080"
DB_NAME: catalog-manager
PLACEMENT_MANAGER_URL: "http://placement-manager:8080"
expose:
- "8080"
depends_on:
postgres: { condition: service_healthy }

policy-manager:
image: quay.io/dcm-project/policy-manager:${POLICY_MANAGER_VERSION:-main}
control-plane:
image: quay.io/dcm-project/control-plane:${CONTROL_PLANE_VERSION:-main}
pull_policy: always
environment:
<<: *db-common
BIND_ADDRESS: ":8080"
DB_NAME: control-plane
LOG_LEVEL: info
DB_NAME: policy-manager
NATS_URL: "nats://nats:4222"
expose:
- "8080"
depends_on:
postgres: { condition: service_healthy }
nats: { condition: service_started }

kubevirt-service-provider:
container_name: ${KUBEVIRT_PROVIDER_NAME:-kubevirt-service-provider}
Expand All @@ -104,29 +76,15 @@ services:
pull_policy: always
environment:
NATS_URL: "nats://nats:4222"
SERVICE_MANAGER_ENDPOINT: "http://service-provider-manager:8080/api/v1alpha1"
SERVICE_MANAGER_ENDPOINT: *dcm-registration-url
PROVIDER_NAME: ${KUBEVIRT_PROVIDER_NAME:-kubevirt-service-provider}
PROVIDER_ENDPOINT: "http://${KUBEVIRT_PROVIDER_NAME:-kubevirt-service-provider}:8081/api/v1alpha1/vms"
KUBERNETES_NAMESPACE: ${KUBEVIRT_NAMESPACE:-default}
KUBERNETES_KUBECONFIG: /kubeconfig
volumes:
- ${KUBEVIRT_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z
depends_on:
service-provider-manager: { condition: service_started }

placement-manager:
image: quay.io/dcm-project/placement-manager:${PLACEMENT_MANAGER_VERSION:-main}
pull_policy: always
environment:
<<: *db-common
BIND_ADDRESS: ":8080"
DB_NAME: placement-manager
POLICY_MANAGER_EVALUATION_URL: "http://policy-manager:8081"
SP_RESOURCE_MANAGER_URL: "http://service-provider-manager:8080"
expose:
- "8080"
depends_on:
postgres: { condition: service_healthy }
control-plane: { condition: service_started }

k8s-container-service-provider:
profiles: ["providers", "k8s-container", "three-tier"]
Expand All @@ -135,7 +93,7 @@ services:
environment:
SP_NAME: ${K8S_CONTAINER_SP_NAME:-k8s-container-provider}
SP_ENDPOINT: "http://k8s-container-service-provider:8080"
DCM_REGISTRATION_URL: "http://service-provider-manager:8080/api/v1alpha1"
DCM_REGISTRATION_URL: *dcm-registration-url
SP_NATS_URL: "nats://nats:4222"
SP_K8S_NAMESPACE: ${K8S_CONTAINER_SP_NAMESPACE:-default}
SP_K8S_KUBECONFIG: /kubeconfig
Expand All @@ -145,7 +103,7 @@ services:
volumes:
- ${K8S_CONTAINER_SP_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z
depends_on:
service-provider-manager: { condition: service_started }
control-plane: { condition: service_started }
nats: { condition: service_started }

acm-cluster-service-provider:
Expand All @@ -155,13 +113,11 @@ services:
environment:
SP_NAME: ${ACM_CLUSTER_SP_NAME:-acm-cluster-sp}
SP_ENDPOINT: "http://acm-cluster-service-provider:8080"
DCM_REGISTRATION_URL: "http://service-provider-manager:8080/api/v1alpha1"
DCM_REGISTRATION_URL: *dcm-registration-url
SP_NATS_URL: "nats://nats:4222"
SP_CLUSTER_NAMESPACE: ${ACM_CLUSTER_SP_NAMESPACE:-default}
SP_BASE_DOMAIN: ${ACM_CLUSTER_SP_BASE_DOMAIN}
SP_PULL_SECRET: ${ACM_CLUSTER_SP_PULL_SECRET}
# BareMetal platform only — not used by KubeVirt.
# Can be omitted when SP_ENABLED_PLATFORMS=kubevirt.
SP_DEFAULT_INFRA_ENV: ${ACM_CLUSTER_SP_DEFAULT_INFRA_ENV}
SP_AGENT_NAMESPACE: ${ACM_CLUSTER_SP_AGENT_NAMESPACE}
KUBECONFIG: /kubeconfig
Expand All @@ -170,7 +126,7 @@ services:
volumes:
- ${ACM_CLUSTER_SP_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z
depends_on:
service-provider-manager: { condition: service_started }
control-plane: { condition: service_started }
nats: { condition: service_started }

dcm-ui:
Expand All @@ -194,7 +150,7 @@ services:
CONTAINER_SP_URL: "http://k8s-container-service-provider:8080"
SP_NAME: ${THREE_TIER_SP_NAME:-three-tier-provider}
SP_ENDPOINT: "http://three-tier-demo-service-provider:8080"
DCM_REGISTRATION_URL: "http://service-provider-manager:8080/api/v1alpha1"
DCM_REGISTRATION_URL: *dcm-registration-url
SP_NATS_URL: "nats://nats:4222"
SP_K8S_NAMESPACE: ${K8S_CONTAINER_SP_NAMESPACE:-default}
SP_K8S_KUBECONFIG: /kubeconfig
Expand All @@ -203,7 +159,7 @@ services:
volumes:
- ${K8S_CONTAINER_SP_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z
depends_on:
service-provider-manager: { condition: service_started }
control-plane: { condition: service_started }
nats: { condition: service_started }
k8s-container-service-provider: { condition: service_started }
postgres: { condition: service_healthy }
Expand Down
Loading