diff --git a/.gitignore b/.gitignore index b871ece..3dc21ea 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ go.work.sum # env file .env +deploy/.kube/ # Editor/IDE # .vscode/ diff --git a/Makefile b/Makefile index 11cb62a..5e0143c 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,11 @@ endif COMPOSE_FILE := deploy/compose.yaml COMPOSE_PROJECT_NAME ?= control-plane COMPOSE_NETWORK := $(COMPOSE_PROJECT_NAME)_default +UTILITIES_DIR ?= ../utilities +KIND_SCRIPTS_DIR ?= $(UTILITIES_DIR)/scripts/kind +COMPOSE_SCRIPTS_DIR ?= $(UTILITIES_DIR)/scripts/compose +KUBEVIRT_SCRIPTS_DIR ?= $(UTILITIES_DIR)/scripts/kubevirt +COMPOSE_NETWORKS ?= deploy_default $(COMPOSE_NETWORK) PROFILES ?= providers COMPOSE ?= $(shell command -v podman-compose >/dev/null 2>&1 && echo podman-compose || \ @@ -53,38 +58,38 @@ run-dev: compose-up: $(COMPOSE) -f $(COMPOSE_FILE) up -d --build -# Platform stack with optional service providers (see deploy/RUN.md). -compose-up-with-providers: - $(COMPOSE) -f $(COMPOSE_FILE) --profile $(PROFILES) up -d --build - -# Tear down the compose stack. Kind (or other externals) joined to the compose -# network block "compose down" from removing it — disconnect them first. -# Network cleanup uses podman- or docker-specific commands (not portable flags). -compose-down: - @for network in deploy_default $(COMPOSE_NETWORK); do \ - if [ "$(CONTAINER_ENGINE)" = podman ]; then \ - if podman network exists "$$network" 2>/dev/null; then \ - for c in $$(podman ps -a --filter network=$$network -q 2>/dev/null); do \ - podman network disconnect -f "$$network" "$$c" 2>/dev/null || true; \ - done; \ - fi; \ - elif [ "$(CONTAINER_ENGINE)" = docker ]; then \ - if docker network inspect "$$network" >/dev/null 2>&1; then \ - for c in $$(docker ps -a --filter network=$$network -q 2>/dev/null); do \ - docker network disconnect "$$network" "$$c" --force 2>/dev/null || true; \ - done; \ - fi; \ - fi; \ - done; \ - COMPOSE_PROJECT_NAME=deploy $(COMPOSE) -f $(COMPOSE_FILE) down -v --remove-orphans 2>/dev/null || true; \ +# Platform stack + environment-agent profile (see deploy/RUN.md). +compose-up-with-agent: + $(COMPOSE) -f $(COMPOSE_FILE) --profile environment-agent up -d --build + +# Local dev helpers (scripts in dcm-project/utilities — see deploy/docs/environment-agent-kind.md). +install-kubevirt: + bash $(KUBEVIRT_SCRIPTS_DIR)/install-kubevirt.sh + +kubeconfig-for-compose: + DEPLOY_ROOT="$(CURDIR)" bash $(KIND_SCRIPTS_DIR)/kubeconfig-for-compose.sh + +kind-connect: + COMPOSE_NETWORK=$(COMPOSE_NETWORK) CONTAINER_ENGINE=$(CONTAINER_ENGINE) \ + bash $(KIND_SCRIPTS_DIR)/kind-connect.sh + +kind-disconnect: + @COMPOSE_NETWORK=$(COMPOSE_NETWORK) CONTAINER_ENGINE=$(CONTAINER_ENGINE) \ + bash $(KIND_SCRIPTS_DIR)/kind-disconnect.sh || true + +disconnect-compose-networks: + @COMPOSE_NETWORKS="$(COMPOSE_NETWORKS)" CONTAINER_ENGINE=$(CONTAINER_ENGINE) \ + bash $(COMPOSE_SCRIPTS_DIR)/network-teardown.sh disconnect || true + +remove-compose-networks: + @COMPOSE_NETWORKS="$(COMPOSE_NETWORKS)" CONTAINER_ENGINE=$(CONTAINER_ENGINE) \ + bash $(COMPOSE_SCRIPTS_DIR)/network-teardown.sh remove || true + +# Tear down the compose stack. Disconnect Kind and other externals first so networks can be removed. +compose-down: kind-disconnect disconnect-compose-networks + @COMPOSE_PROJECT_NAME=deploy $(COMPOSE) -f $(COMPOSE_FILE) down -v --remove-orphans 2>/dev/null || true; \ $(COMPOSE) -f $(COMPOSE_FILE) down -v --remove-orphans; \ - for network in deploy_default $(COMPOSE_NETWORK); do \ - if [ "$(CONTAINER_ENGINE)" = podman ]; then \ - podman network rm -f "$$network" 2>/dev/null || true; \ - elif [ "$(CONTAINER_ENGINE)" = docker ]; then \ - docker network rm "$$network" 2>/dev/null || true; \ - fi; \ - done + $(MAKE) remove-compose-networks image-build: $(CONTAINER_ENGINE) build -f Containerfile -t $(CONTAINER_IMAGE_NAME):$(CONTAINER_IMAGE_TAG) . @@ -112,6 +117,8 @@ test: tidy: go mod tidy -.PHONY: build run run-dev compose-up compose-up-with-providers compose-down image-build \ - clean fmt vet lint test test-catalog test-placement test-policy test-sp tidy \ +.PHONY: build run run-dev compose-up compose-up-with-agent compose-down \ + install-kubevirt kubeconfig-for-compose kind-connect kind-disconnect \ + disconnect-compose-networks remove-compose-networks \ + image-build clean fmt vet lint test test-catalog test-placement test-policy test-sp tidy \ helm-chart-sync helm-chart-verify-sync helm-chart-verify-admin-subject helm-chart-verify helm-chart-verify-schema helm-chart-lint helm-chart-template helm-chart-check diff --git a/README.md b/README.md index 96d51c5..89d285f 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,10 @@ in shared-workflows for tag behavior and version conventions. Full-stack Compose and Helm packaging live under `deploy/`: -- **Compose:** control-plane, postgres, nats, keycloak, dcm-ui, and optional service providers +- **Compose:** control-plane, postgres, nats, keycloak, dcm-ui, and optional environment-agent profile - **Helm:** Kubernetes/OpenShift chart at `deploy/helm/dcm` (optional auth via `auth.enabled`) -See [deploy/RUN.md](deploy/RUN.md) for local stack usage, authentication, and service provider profiles. +See [deploy/RUN.md](deploy/RUN.md) for local stack usage, authentication, and the environment-agent profile. See [deploy/helm/dcm/README.md](deploy/helm/dcm/README.md) for cluster installs. Authentication is disabled by default (`AUTH_DISABLED=true`). The CLI forwards diff --git a/deploy/.env.example b/deploy/.env.example index c47d017..bdb1248 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -1,4 +1,4 @@ -# Optional: copy to .env in the repo root and set for local overrides. +# Optional: copy to deploy/.env for local overrides (loaded by deploy/compose.yaml). # Used by deploy/compose.yaml. Defaults (admin / adminpass) are for local dev only. # POSTGRES_USER=admin # POSTGRES_PASSWORD=adminpass @@ -15,32 +15,21 @@ # KEYCLOAK_ADMIN_PASSWORD=admin # DCM_DEV_USER_PASSWORD=admin -# K8s container service provider (profile: k8s-container) -# K8S_CONTAINER_SP_KUBECONFIG=/path/to/kubeconfig -# K8S_CONTAINER_SP_NAMESPACE=default -# K8S_CONTAINER_SP_NAME=k8s-container-provider -# K8S_CONTAINER_SP_EXTERNAL_SVC_TYPE=NodePort - -# K8s storage service provider (profile: storage) -# K8S_STORAGE_SP_KUBECONFIG=/path/to/kubeconfig -# K8S_STORAGE_SP_NAMESPACE=default -# K8S_STORAGE_SP_NAME=k8s-storage-provider -# K8S_STORAGE_SP_DEFAULT_STORAGE_CLASS= -# K8S_STORAGE_SP_DEFAULT_ACCESS_MODE=ReadWriteOnce - -# Three-tier demo app service provider (profile: three-tier) -# Same kubeconfig + cluster namespace as k8s-container (K8S_CONTAINER_SP_* above). -# THREE_TIER_SP_NAME=three-tier-provider - -# ACM cluster service provider (profile: acm-cluster) -# Supports two platforms: KubeVirt (default) and BareMetal. -# Both enabled by default (SP_ENABLED_PLATFORMS=kubevirt,baremetal). -# ACM_CLUSTER_SP_NAME=acm-cluster-sp -# ACM_CLUSTER_SP_BASE_DOMAIN=apps.example.com -# ACM_CLUSTER_SP_PULL_SECRET= -# BareMetal platform only — not used by KubeVirt: -# ACM_CLUSTER_SP_DEFAULT_INFRA_ENV=my-infra-env -# ACM_CLUSTER_SP_AGENT_NAMESPACE=my-agent-namespace +# Environment agent (profile: environment-agent) — required when using the agent profile +# See deploy/docs/environment-agent-kind.md +# AGENT_NAME=local-agent +# AGENT_EMBEDDED_SPS=container,vm # required; compose has no default (e.g. container, vm, cluster, storage) +# AGENT_KUBECONFIG_HOST=.kube/config # written by make kubeconfig-for-compose +# SP_CONTAINER_NAMESPACE=default +# SP_K8S_EXTERNAL_SVC_TYPE=NodePort +# SP_VM_NAMESPACE=default +# SP_CLUSTER_NAMESPACE=clusters +# SP_PULL_SECRET= +# SP_BASE_DOMAIN=apps.example.com +# SP_STORAGE_NAMESPACE=default +# SP_K8S_DEFAULT_STORAGE_CLASS= +# SP_K8S_DEFAULT_ACCESS_MODE=ReadWriteOnce +# ENVIRONMENT_AGENT_VERSION=main # --- Service image versions --- # Pin specific versions instead of "main". Valid values: @@ -49,9 +38,5 @@ # v0.0.1-rc.3 - release candidate (from release branch) # abc1234 - specific commit build (for local testing only) # CONTROL_PLANE_VERSION=main -# KUBEVIRT_SERVICE_PROVIDER_VERSION=main -# K8S_CONTAINER_SERVICE_PROVIDER_VERSION=main -# K8S_STORAGE_SERVICE_PROVIDER_VERSION=main -# ACM_CLUSTER_SERVICE_PROVIDER_VERSION=main -# THREE_TIER_DEMO_SERVICE_PROVIDER_VERSION=main +# ENVIRONMENT_AGENT_VERSION=main # DCM_UI_VERSION=main diff --git a/deploy/RUN.md b/deploy/RUN.md index d410671..ea138a8 100644 --- a/deploy/RUN.md +++ b/deploy/RUN.md @@ -3,13 +3,21 @@ ## Prerequisites - [Podman](https://podman.io/) or [Docker](https://www.docker.com/) (the Makefile auto-detects which engine is available) -- (Optional) A Kubernetes cluster with KubeVirt for the kubevirt-service-provider -- (Optional) A Kubernetes cluster for the k8s-container-service-provider -- (Optional) An OpenShift cluster with ACM/MCE and HyperShift for the acm-cluster-service-provider +- (Optional) [Kind](https://kind.sigs.k8s.io/) with KubeVirt for the environment-agent embedded `vm` SP +- (Optional) A Kubernetes cluster for environment-agent embedded `container` and `cluster` SPs +- (Optional) [utilities](https://github.com/dcm-project/utilities) repo as a sibling directory for Kind helper scripts (`../utilities`) ## Quick start -Start the core platform (postgres, nats, keycloak, control-plane, and dcm-ui): +### With environment-agent (Service Providers) + +An [environment-agent](https://github.com/dcm-project/environment-agent) must be running and +registered with the control-plane in order to use Service Providers. Follow the guide, +[environment-agent-kind.md](docs/environment-agent-kind.md) for the full setup. + +### Control-plane and UI only + +If you only need the API and UI (no workload provisioning): ```bash make compose-up @@ -31,133 +39,10 @@ The CLI forwards bearer tokens to the control-plane API. Run `dcm login` for int OIDC device authorization (Keycloak `dcm-cli` client), or set `DCM_TOKEN` / `--token` for CI and scripting. -## Running with service providers - -Service providers are behind compose profiles and do not start by default. - -### KubeVirt service provider - -To include the `kubevirt-service-provider`, set the required environment variables and -activate the `kubevirt` profile: - -```bash -export KUBERNETES_NAMESPACE=vms -export KUBEVIRT_KUBECONFIG="/path/to/kubeconfig" -make compose-up-with-providers PROFILES=kubevirt -``` - -### K8s container service provider - -To include the `k8s-container-service-provider`, set the required environment variables and -activate the `k8s-container` profile: - -```bash -export K8S_CONTAINER_SP_KUBECONFIG="/path/to/kubeconfig" -make compose-up-with-providers PROFILES=k8s-container -``` - -If using Kind, see [K8s Container SP with Kind](docs/k8s-container-sp-kind.md) for additional network setup. - -Optionally override the provider name or external service type: - -```bash -export K8S_CONTAINER_SP_NAME=my-provider -export K8S_CONTAINER_SP_EXTERNAL_SVC_TYPE=LoadBalancer -``` - -### K8s storage service provider - -To include the `k8s-storage-service-provider`, set the required environment variables and -activate the `storage` profile: - -```bash -export K8S_STORAGE_SP_KUBECONFIG="/path/to/kubeconfig" -make compose-up-with-providers PROFILES=storage -``` - -Optionally override the provider name, namespace, and default PVC behavior: - -```bash -export K8S_STORAGE_SP_NAME=my-storage-provider -export K8S_STORAGE_SP_NAMESPACE=default -export K8S_STORAGE_SP_DEFAULT_STORAGE_CLASS=ceph-rbd -export K8S_STORAGE_SP_DEFAULT_ACCESS_MODE=ReadWriteOnce -``` - -### ACM cluster service provider - -To include the `acm-cluster-service-provider`, set the required environment variables and -activate the `acm-cluster` profile: +## Running with Environment Agent -```bash -export ACM_CLUSTER_SP_KUBECONFIG="/path/to/kubeconfig" -export ACM_CLUSTER_SP_PULL_SECRET="" -make compose-up-with-providers PROFILES=acm-cluster -``` - -Optionally override the provider name, namespace, or base domain: - -```bash -export ACM_CLUSTER_SP_NAME=my-acm-provider -export ACM_CLUSTER_SP_NAMESPACE=clusters -export ACM_CLUSTER_SP_BASE_DOMAIN="apps.example.com" -``` - -For BareMetal provisioning, also set: - -```bash -export ACM_CLUSTER_SP_DEFAULT_INFRA_ENV="my-infra-env" -export ACM_CLUSTER_SP_AGENT_NAMESPACE="my-agent-namespace" -``` - -### Three-tier demo app service provider - -To include the `three-tier-demo-service-provider`, set the required environment variables and -activate the `three-tier` profile: - -```bash -export K8S_CONTAINER_SP_KUBECONFIG="/path/to/kubeconfig" -make compose-up-with-providers PROFILES=three-tier -``` - -When using Kind, complete the k8s-container setup (steps 1–5 in [K8s Container -SP with Kind](docs/k8s-container-sp-kind.md)) first. -For Pet Clinic usage, see [Three-Tier Demo App with Kind](docs/three-tier-app-kind.md). - -Optionally override the provider name or cluster namespace (`K8S_CONTAINER_SP_NAMESPACE` applies -to both k8s-container and three-tier SPs): - -```bash -export THREE_TIER_SP_NAME=my-provider -export K8S_CONTAINER_SP_NAMESPACE=default -``` - -### All providers - -To start all providers at once, set the required environment variables and run: - -```bash -export KUBEVIRT_KUBECONFIG="/path/to/kubeconfig" -export K8S_CONTAINER_SP_KUBECONFIG="/path/to/kubeconfig" -export K8S_STORAGE_SP_KUBECONFIG="/path/to/kubeconfig" -export ACM_CLUSTER_SP_KUBECONFIG="/path/to/kubeconfig" -export ACM_CLUSTER_SP_PULL_SECRET="" -# BareMetal only: -export ACM_CLUSTER_SP_DEFAULT_INFRA_ENV="my-infra-env" -export ACM_CLUSTER_SP_AGENT_NAMESPACE="my-agent-namespace" -make compose-up-with-providers -``` - -This defaults to the `providers` Compose profile (all service providers, including the -three-tier demo SP). To start a single provider instead, pass `PROFILES=`: - -```bash -make compose-up-with-providers PROFILES=kubevirt -make compose-up-with-providers PROFILES=k8s-container -make compose-up-with-providers PROFILES=storage -make compose-up-with-providers PROFILES=acm-cluster -make compose-up-with-providers PROFILES=three-tier -``` +See [environment-agent-kind.md](docs/environment-agent-kind.md) for Kind setup, the +`environment-agent` compose profile, configuration, and verification. ## Authentication @@ -167,8 +52,8 @@ JWKS endpoint using OIDC discovery (no external auth proxy required). A proxy-he fallback path (`X-Auth-Proxy-Secret` + `X-Forwarded-User`) is also supported. Authentication is disabled by default (`AUTH_DISABLED=true`). When enabled, the CLI -(`dcm login` / bearer token) and direct JWT API calls work; service providers do not -forward authentication headers yet, so SP ↔ control-plane traffic may fail. +(`dcm login` / bearer token) and direct JWT API calls work; the environment-agent does not +forward authentication headers yet, so SP workflows may fail. To enable authentication (Compose): @@ -176,12 +61,15 @@ To enable authentication (Compose): AUTH_DISABLED=false AUTH_ISSUER_URL=http://keycloak:8080/realms/dcm make compose-up ``` +`control-plane` waits for Keycloak to pass its health check before starting, so OIDC discovery +succeeds when `AUTH_ISSUER_URL` is set. + For Helm chart installs, see [helm/dcm/README.md](helm/dcm/README.md#authentication) (`auth.enabled=true`). -> **Warning:** Service providers do not forward authentication headers yet, so enabling -> auth can break SP workflows. The CLI (`dcm login` / bearer token) and direct API -> calls with a valid Keycloak JWT work. +> **Warning:** The environment-agent does not forward authentication headers yet, so enabling +> auth can break SP workflows. The CLI (`dcm login` / bearer token) and direct API calls with a +> valid Keycloak JWT work. When enabled, the control-plane authenticates requests via two paths (tried in order): @@ -249,7 +137,7 @@ make compose-down ``` This stops all compose services and removes volumes. If Kind was connected to -the compose network (see [k8s-container-sp-kind.md](docs/k8s-container-sp-kind.md)), +the compose network (see [environment-agent-kind.md](docs/environment-agent-kind.md)), `compose-down` disconnects external containers and removes both `control-plane_default` and legacy `deploy_default` networks. @@ -267,32 +155,23 @@ the compose network (see [k8s-container-sp-kind.md](docs/k8s-container-sp-kind.m | `DCM_DEV_USER_PASSWORD` | `admin` | Password for the `dcm-admin` dev user in Keycloak | | `POSTGRES_USER` | `admin` | PostgreSQL username | | `POSTGRES_PASSWORD` | `adminpass` | PostgreSQL password | -| `KUBERNETES_NAMESPACE` | `default` | Kubernetes namespace for KubeVirt VMs | -| `KUBEVIRT_KUBECONFIG` | `~/.kube/config` | Path to kubeconfig on the host | -| `KUBEVIRT_PROVIDER_NAME` | `kubevirt-service-provider` | Provider name and Compose service `container_name` | -| `K8S_CONTAINER_SP_KUBECONFIG` | `~/.kube/config` | Path to kubeconfig on the host for the k8s-container-service-provider | -| `K8S_CONTAINER_SP_NAMESPACE` | `default` | Kubernetes namespace for k8s containers | -| `K8S_CONTAINER_SP_NAME` | `k8s-container-provider` | Provider name for the k8s-container-service-provider | -| `K8S_CONTAINER_SP_EXTERNAL_SVC_TYPE` | `NodePort` | Kubernetes Service type for external ports (`NodePort` or `LoadBalancer`) | -| `K8S_STORAGE_SP_KUBECONFIG` | `~/.kube/config` | Path to kubeconfig on the host for the k8s-storage-service-provider | -| `K8S_STORAGE_SP_NAMESPACE` | `default` | Kubernetes namespace used by the k8s-storage-service-provider | -| `K8S_STORAGE_SP_NAME` | `k8s-storage-provider` | Provider name for the k8s-storage-service-provider | -| `K8S_STORAGE_SP_DEFAULT_STORAGE_CLASS` | _(empty)_ | Optional fallback StorageClass when request hints do not set one | -| `K8S_STORAGE_SP_DEFAULT_ACCESS_MODE` | `ReadWriteOnce` | Optional fallback access mode when request hints do not set one | -| `ACM_CLUSTER_SP_KUBECONFIG` | `~/.kube/config` | Path to kubeconfig on the host for the acm-cluster-service-provider | -| `ACM_CLUSTER_SP_NAMESPACE` | `default` | Kubernetes namespace for ACM hosted clusters | -| `ACM_CLUSTER_SP_NAME` | `acm-cluster-sp` | Provider name for the acm-cluster-service-provider | -| `ACM_CLUSTER_SP_BASE_DOMAIN` | _(none)_ | Base DNS domain for hosted clusters; can be overridden per-request via `provider_hints.acm.base_domain` | -| `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 | +| `AGENT_NAME` | `local-agent` | Agent name for environment-agent profile | +| `AGENT_ENVIRONMENT` | `dev` | Environment classification for environment-agent | +| `AGENT_COST` | `low` | Cost classification for environment-agent | +| `AGENT_PORT` | `8081` | Host port for environment-agent HTTP API | +| `AGENT_EMBEDDED_SPS` | _(empty)_ | **Required in `deploy/.env`** when using the agent profile. Comma-separated: `container`, `vm`, `cluster`, `storage` | +| `AGENT_KUBECONFIG_HOST` | `~/.kube/config` | Host kubeconfig bind mount; use `.kube/config` in `deploy/.env` with Kind (`make kubeconfig-for-compose`) | +| `SP_CONTAINER_NAMESPACE` | `default` | Container SP workload namespace (environment-agent) | +| `SP_K8S_EXTERNAL_SVC_TYPE` | `NodePort` | Container SP external service type (environment-agent) | +| `SP_VM_NAMESPACE` | `default` | VM SP workload namespace (environment-agent) | +| `SP_CLUSTER_NAMESPACE` | _(required for cluster SP)_ | ACM cluster namespace (environment-agent cluster SP) | +| `SP_PULL_SECRET` | _(required for cluster SP)_ | Base64-encoded dockerconfigjson for environment-agent cluster SP | +| `SP_BASE_DOMAIN` | _(none)_ | Base domain for hosted clusters (environment-agent cluster SP) | +| `SP_STORAGE_NAMESPACE` | `default` | Storage SP workload namespace (environment-agent) | +| `SP_K8S_DEFAULT_STORAGE_CLASS` | _(none)_ | Default storage class for environment-agent storage SP | +| `SP_K8S_DEFAULT_ACCESS_MODE` | `ReadWriteOnce` | Default PVC access mode for environment-agent storage SP | +| `ENVIRONMENT_AGENT_VERSION` | `main` | Image tag for environment-agent | | `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 | -| `K8S_STORAGE_SERVICE_PROVIDER_VERSION` | `main` | Image tag for k8s-storage-service-provider | -| `ACM_CLUSTER_SERVICE_PROVIDER_VERSION` | `main` | Image tag for acm-cluster-service-provider | -| `THREE_TIER_DEMO_SERVICE_PROVIDER_VERSION` | `main` | Image tag for three-tier-demo-service-provider | -| `THREE_TIER_SP_NAME` | `three-tier-provider` | Provider name for the three-tier-demo-service-provider | | `DCM_UI_VERSION` | `main` | Image tag for dcm-ui | See [Image versions](../README.md#image-versions) in the README for available tag formats and how to update. diff --git a/deploy/compose.override.example.yaml b/deploy/compose.override.example.yaml deleted file mode 100644 index 58c91c5..0000000 --- a/deploy/compose.override.example.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# OpenShift: Route-based web exposure for three-tier-demo-service-provider. -# For Kind / default kubernetes exposure, do not install an override (remove -# deploy/compose.override.yaml if present). -# -# cp deploy/compose.override.example.yaml deploy/compose.override.yaml -# -# Match SP_OPENSHIFT_ROUTE_NAMESPACE to the k8s container SP namespace (same as -# K8S_CONTAINER_SP_NAMESPACE in .env). See deploy/docs/three-tier-app-kind.md. - -services: - three-tier-demo-service-provider: - environment: - SP_WEB_EXPOSURE: openshift - SP_OPENSHIFT_ROUTE_NAMESPACE: ${K8S_CONTAINER_SP_NAMESPACE:-default} - SP_OPENSHIFT_KUBECONFIG: /openshift-kubeconfig - volumes: - - ${KUBECONFIG:-$HOME/.kube/config}:/openshift-kubeconfig:ro,z diff --git a/deploy/compose.yaml b/deploy/compose.yaml index 1877580..db28e92 100644 --- a/deploy/compose.yaml +++ b/deploy/compose.yaml @@ -92,95 +92,6 @@ services: keycloak: condition: service_healthy - kubevirt-service-provider: - container_name: ${KUBEVIRT_PROVIDER_NAME:-kubevirt-service-provider} - profiles: ["providers", "kubevirt"] - image: quay.io/dcm-project/kubevirt-service-provider:${KUBEVIRT_SERVICE_PROVIDER_VERSION:-main} - pull_policy: always - environment: - NATS_URL: "nats://nats:4222" - 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: ${KUBERNETES_NAMESPACE:-default} - KUBERNETES_KUBECONFIG: /kubeconfig - volumes: - - ${KUBEVIRT_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z - depends_on: - control-plane: - condition: service_started - - k8s-container-service-provider: - profiles: ["providers", "k8s-container", "three-tier"] - image: quay.io/dcm-project/k8s-container-service-provider:${K8S_CONTAINER_SERVICE_PROVIDER_VERSION:-main} - pull_policy: always - environment: - SP_NAME: ${K8S_CONTAINER_SP_NAME:-k8s-container-provider} - SP_ENDPOINT: "http://k8s-container-service-provider:8080" - DCM_REGISTRATION_URL: *dcm-registration-url - SP_NATS_URL: "nats://nats:4222" - SP_K8S_NAMESPACE: ${K8S_CONTAINER_SP_NAMESPACE:-default} - SP_K8S_KUBECONFIG: /kubeconfig - SP_K8S_EXTERNAL_SVC_TYPE: ${K8S_CONTAINER_SP_EXTERNAL_SVC_TYPE:-NodePort} - expose: - - "8080" - volumes: - - ${K8S_CONTAINER_SP_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z - depends_on: - control-plane: - condition: service_started - nats: - condition: service_started - - acm-cluster-service-provider: - profiles: ["providers", "acm-cluster"] - image: quay.io/dcm-project/acm-cluster-service-provider:${ACM_CLUSTER_SERVICE_PROVIDER_VERSION:-main} - pull_policy: always - environment: - SP_NAME: ${ACM_CLUSTER_SP_NAME:-acm-cluster-sp} - SP_ENDPOINT: "http://acm-cluster-service-provider:8080" - 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} - SP_DEFAULT_INFRA_ENV: ${ACM_CLUSTER_SP_DEFAULT_INFRA_ENV} - SP_AGENT_NAMESPACE: ${ACM_CLUSTER_SP_AGENT_NAMESPACE} - KUBECONFIG: /kubeconfig - expose: - - "8080" - volumes: - - ${ACM_CLUSTER_SP_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z - depends_on: - control-plane: - condition: service_started - nats: - condition: service_started - - k8s-storage-service-provider: - profiles: ["providers", "storage"] - image: quay.io/dcm-project/k8s-storage-service-provider:${K8S_STORAGE_SERVICE_PROVIDER_VERSION:-main} - pull_policy: always - environment: - SP_NAME: ${K8S_STORAGE_SP_NAME:-k8s-storage-provider} - SP_ENDPOINT: "http://k8s-storage-service-provider:8080" - SP_SERVER_ADDRESS: ":8080" - DCM_REGISTRATION_URL: *dcm-registration-url - SP_NATS_URL: "nats://nats:4222" - SP_K8S_NAMESPACE: ${K8S_STORAGE_SP_NAMESPACE:-default} - SP_K8S_KUBECONFIG: /kubeconfig - SP_K8S_DEFAULT_STORAGE_CLASS: ${K8S_STORAGE_SP_DEFAULT_STORAGE_CLASS:-} - SP_K8S_DEFAULT_ACCESS_MODE: ${K8S_STORAGE_SP_DEFAULT_ACCESS_MODE:-ReadWriteOnce} - expose: - - "8080" - volumes: - - ${K8S_STORAGE_SP_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z - depends_on: - control-plane: - condition: service_started - nats: - condition: service_started - dcm-ui: image: quay.io/dcm-project/dcm-ui:${DCM_UI_VERSION:-main} pull_policy: always @@ -193,34 +104,41 @@ services: control-plane: condition: service_started - three-tier-demo-service-provider: - profiles: ["providers", "three-tier"] - image: quay.io/dcm-project/three-tier-app-demo-service-provider:${THREE_TIER_DEMO_SERVICE_PROVIDER_VERSION:-main} + environment-agent: + profiles: ["environment-agent"] + image: quay.io/dcm-project/environment-agent:${ENVIRONMENT_AGENT_VERSION:-main} pull_policy: always environment: - <<: *db-common - DB_NAME: three-tier-sp - 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: *dcm-registration-url - SP_NATS_URL: "nats://nats:4222" - SP_K8S_NAMESPACE: ${K8S_CONTAINER_SP_NAMESPACE:-default} - SP_K8S_KUBECONFIG: /kubeconfig - expose: - - "8080" + AGENT_NAME: ${AGENT_NAME:-local-agent} + AGENT_ENVIRONMENT: ${AGENT_ENVIRONMENT:-dev} + AGENT_COST: ${AGENT_COST:-low} + # Base URL only — the agent appends /api/v1alpha1/agents for registration. + DCM_REGISTRATION_URL: http://control-plane:8080 + AGENT_MESSAGING_URL: nats://nats:4222 + AGENT_EMBEDDED_SPS: ${AGENT_EMBEDDED_SPS:-} + SP_DEFAULT_KUBECONFIG: /kubeconfig + SP_CONTAINER_NAMESPACE: ${SP_CONTAINER_NAMESPACE:-default} + SP_K8S_EXTERNAL_SVC_TYPE: ${SP_K8S_EXTERNAL_SVC_TYPE:-NodePort} + SP_VM_NAMESPACE: ${SP_VM_NAMESPACE:-default} + SP_CLUSTER_NAMESPACE: ${SP_CLUSTER_NAMESPACE:-} + SP_PULL_SECRET: ${SP_PULL_SECRET:-} + SP_BASE_DOMAIN: ${SP_BASE_DOMAIN:-} + SP_STORAGE_NAMESPACE: ${SP_STORAGE_NAMESPACE:-default} + SP_K8S_DEFAULT_STORAGE_CLASS: ${SP_K8S_DEFAULT_STORAGE_CLASS:-} + SP_K8S_DEFAULT_ACCESS_MODE: ${SP_K8S_DEFAULT_ACCESS_MODE:-ReadWriteOnce} + AGENT_SP_PERSISTENCE_PATH: /app/data/registrations.json + ports: + - "${AGENT_PORT:-8081}:8080" volumes: - - ${K8S_CONTAINER_SP_KUBECONFIG:-~/.kube/config}:/kubeconfig:ro,z + - ${AGENT_KUBECONFIG_HOST:-~/.kube/config}:/kubeconfig:ro,z + - environment_agent_data:/app/data depends_on: control-plane: condition: service_started nats: condition: service_started - k8s-container-service-provider: - condition: service_started - postgres: - condition: service_healthy volumes: postgres_data: {} nats_data: {} + environment_agent_data: {} diff --git a/deploy/docs/environment-agent-kind.md b/deploy/docs/environment-agent-kind.md new file mode 100644 index 0000000..40f5501 --- /dev/null +++ b/deploy/docs/environment-agent-kind.md @@ -0,0 +1,177 @@ +# Environment Agent with Kind + +Run the [environment-agent](https://github.com/dcm-project/environment-agent) alongside the +control-plane compose stack. The agent shares platform NATS, registers with the control-plane on +the compose network, and runs embedded Service Providers (container, vm, cluster, storage) in-process using +a kubeconfig to reach workloads on Kind. + +## Prerequisites + +- [Podman](https://podman.io/) or [Docker](https://www.docker.com/) (Makefile auto-detects) +- [Kind](https://kind.sigs.k8s.io/) and `kubectl` +- [utilities](https://github.com/dcm-project/utilities) repo as a sibling directory + (`../utilities`) for Kind helper scripts +- Kind and compose on the **same** container runtime + +## Quick start + +This guide uses **two compose steps**: + +1. **`make compose-up`** — platform only (creates `control-plane_default` for `kind-connect`). +2. **`make compose-up-with-agent`** — after Kind is wired. + +Most Kind prep (`install-kubevirt`, `kubeconfig-for-compose`) can run **before** `compose-up`. +Only **`make kind-connect`** must run after the platform stack is up (compose network must exist). + +### 1. Create a Kind cluster + +Use the same container runtime as compose (Podman example): + +```bash +KIND_EXPERIMENTAL_PROVIDER=podman kind create cluster --name dcm-local +kubectl config use-context kind-dcm-local +``` + +For in-cluster agent deploy on the same cluster, see the +[environment-agent in-cluster guide](https://github.com/dcm-project/environment-agent/blob/main/deploy/docs/in-cluster.md) +and create the cluster with `../utilities/scripts/kind/kind-local.yaml` (host NodePort mappings for +`k8s-verify`). + +### 2. Prepare the cluster + +From the control-plane repo root: + +```bash +make install-kubevirt # when vm is in AGENT_EMBEDDED_SPS +make kubeconfig-for-compose +``` + +Writes `deploy/.kube/config` (API URL `https://kubernetes:6443`). No compose stack required yet. + +### 3. Start the platform stack (without the agent) + +```bash +cp deploy/.env.example deploy/.env +make compose-up +``` + +Control-plane API: `http://localhost:8080`. DCM UI: `http://localhost:7007`. + +Set agent variables in `deploy/.env` before starting the agent (step 6). + +### 4. Connect Kind to compose + +Run after `make compose-up` so `control-plane_default` exists: + +```bash +make kind-connect +``` + +Override the utilities path when needed: + +```bash +make kind-connect KIND_SCRIPTS_DIR=/path/to/utilities/scripts/kind +``` + +### 5. Configure the agent + +**Required:** set `AGENT_EMBEDDED_SPS` in `deploy/.env`. Compose does not default this — an unset +value means no embedded service providers. + +```bash +AGENT_EMBEDDED_SPS=container +AGENT_KUBECONFIG_HOST=.kube/config +``` + +Add `vm`, `cluster`, or `storage` as needed (e.g. `container,vm`). Run `make install-kubevirt` +when `vm` is included. + +### 6. Start the agent (`compose-up-with-agent`) + +```bash +make compose-up-with-agent +``` + +Brings up the environment-agent profile on the running platform stack (or starts platform + agent +together if the stack was stopped). + +Agent API: `http://localhost:8081`. + +### 7. Verify + +```bash +curl http://localhost:8080/api/v1alpha1/health +curl http://localhost:8080/api/v1alpha1/agents +curl http://localhost:8081/api/v1alpha1/health +curl http://localhost:8081/api/v1alpha1/providers +``` + +### 8. Teardown + +```bash +make compose-down # kind-disconnect, detach externals, compose down, remove networks +kind delete cluster --name dcm-local +``` + +## Makefile targets + +| Target | Purpose | +|--------|---------| +| `compose-up` | Platform stack (postgres, nats, keycloak, control-plane, dcm-ui) | +| `install-kubevirt` | Install KubeVirt on the current Kind cluster | +| `kubeconfig-for-compose` | Write `deploy/.kube/config` for the agent container | +| `kind-connect` | Join Kind to `control-plane_default` | +| `compose-up-with-agent` | Add/start environment-agent on the platform stack | +| `compose-down` | Kind disconnect, detach network members, compose down, remove networks | + +Scripts are under `UTILITIES_DIR` (default `../utilities`): + +- `scripts/kind/` — Kind networking +- `scripts/compose/` — compose network teardown +- `scripts/kubevirt/` — KubeVirt install + +See [utilities scripts/kind/README.md](https://github.com/dcm-project/utilities/blob/main/scripts/kind/README.md) +and [scripts/compose/README.md](https://github.com/dcm-project/utilities/blob/main/scripts/compose/README.md). + +## Configuration + +Copy `deploy/.env.example` to `deploy/.env` in this repo and set agent profile variables before +`compose-up-with-agent`. See [deploy/.env.example](../.env.example) for the variables wired into +`deploy/compose.yaml`. + +For the full catalog of agent and SP environment variables, see +[environment-agent deploy/.env.example](https://github.com/dcm-project/environment-agent/blob/main/deploy/.env.example). + +| Variable | Default | Description | +| --- | --- | --- | +| `AGENT_NAME` | `local-agent` | Agent name sent to DCM | +| `AGENT_ENVIRONMENT` | `dev` | Environment classification | +| `AGENT_COST` | `low` | Cost classification | +| `AGENT_PORT` | `8081` | Host port for agent HTTP API | +| `AGENT_EMBEDDED_SPS` | _empty_ (set in `deploy/.env`) | **Required** for the agent profile. Comma-separated: `container`, `vm`, `cluster`, `storage` | +| `AGENT_KUBECONFIG_HOST` | `~/.kube/config` | Host kubeconfig bind mount; use `.kube/config` with Kind (`make kubeconfig-for-compose`) | +| `SP_DEFAULT_KUBECONFIG` | `/kubeconfig` | In-container path (set in `compose.yaml`; do not set in `.env`) | +| `SP_CONTAINER_NAMESPACE` | `default` | Container SP workload namespace | +| `SP_K8S_EXTERNAL_SVC_TYPE` | `NodePort` | Container SP external service type | +| `SP_VM_NAMESPACE` | `default` | VM SP workload namespace | +| `SP_CLUSTER_NAMESPACE` | _(required for cluster SP)_ | ACM cluster namespace | +| `SP_PULL_SECRET` | _(required for cluster SP)_ | Base64-encoded dockerconfigjson | +| `SP_BASE_DOMAIN` | _(none)_ | Base domain for hosted clusters | +| `SP_STORAGE_NAMESPACE` | `default` | Storage SP workload namespace | +| `SP_K8S_DEFAULT_STORAGE_CLASS` | _(none)_ | Default storage class for storage SP | +| `SP_K8S_DEFAULT_ACCESS_MODE` | `ReadWriteOnce` | Default PVC access mode for storage SP | +| `ENVIRONMENT_AGENT_VERSION` | `main` | Agent image tag | + +`DCM_REGISTRATION_URL` is set to `http://control-plane:8080` in compose (base URL only and the agent +appends `/api/v1alpha1/agents`). `AGENT_MESSAGING_URL` is `nats://nats:4222`. + +## Why Kind networking is needed + +| Problem | Cause | +|---------|-------| +| Agent container can't reach Kind's IP | Kind and compose run on separate container networks | +| Agent uses `127.0.0.1:` | Default kubeconfig targets the host-side port mapping | +| TLS error with arbitrary hostname | API server cert only includes specific SANs | + +`make kind-connect` joins Kind to the compose network with alias `kubernetes` (a cert SAN). +`make kubeconfig-for-compose` rewrites the API server URL to `https://kubernetes:6443`. diff --git a/deploy/docs/k8s-container-sp-kind.md b/deploy/docs/k8s-container-sp-kind.md deleted file mode 100644 index 09636af..0000000 --- a/deploy/docs/k8s-container-sp-kind.md +++ /dev/null @@ -1,97 +0,0 @@ -# K8s Container Service Provider with Kind - -When Kind runs with Podman, its API server lives on a separate -Podman network. The compose services can't reach it by default. - -## Setup (one-time, until containers are recreated) - -### 1. Create the Kind cluster - -```bash -KIND_EXPERIMENTAL_PROVIDER=podman kind create cluster -``` - -This creates a container named `kind-control-plane`. -If you use `--name `, the container will be `-control-plane`. - -### 2. Start the compose services - -This must be done **before** step 3 so the compose network exists. - -```bash -podman compose -f deploy/compose.yaml --profile k8s-container up -d -``` - -### 3. Connect Kind to the compose network - -Connect the Kind control-plane container to the compose network with -an alias that matches a SAN in Kind's API server certificate. - -To list valid SANs: - -```bash -podman exec kind-control-plane \ - openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text \ - | grep -A1 "Subject Alternative Name" -``` - -Use `kubernetes` as the alias (short, always present in the SAN list): - -```bash -podman network connect \ - --alias kubernetes \ - deploy_default \ - kind-control-plane -``` - -> **Note:** `make compose-up` sets `COMPOSE_PROJECT_NAME=control-plane`, so the -> network is `deploy_default`. Verify with `podman network ls`. -> `make compose-down` disconnects Kind and removes the network automatically. - -### 4. Generate a kubeconfig that uses the alias - -```bash -kubectl config view --minify --flatten --context kind-kind \ - | sed -E 's|https://[^:]+:[0-9]+|https://kubernetes:6443|' \ - > kubeconfig.yaml -``` - -Kind maps the API server to a random host port (e.g. `44615`), but -container-to-container traffic uses port `6443` directly. - -### 5. Point the SP to the generated kubeconfig - -The compose file mounts `${K8S_CONTAINER_SP_KUBECONFIG:-~/.kube/config}` into the -SP container. Set the variable to the generated file and restart: - -```bash -export K8S_CONTAINER_SP_KUBECONFIG="$(pwd)/kubeconfig.yaml" -podman compose -f deploy/compose.yaml --profile k8s-container up -d -``` - -### External service type - -The SP requires `SP_K8S_EXTERNAL_SVC_TYPE` to determine the Kubernetes Service -type for ports with `external` visibility. Valid values: - -| Value | Use case | -|---|---| -| `NodePort` | Default. Works out of the box with Kind and bare-metal clusters. | -| `LoadBalancer` | Cloud environments with a load-balancer controller (e.g., AWS, GCP) or clusters running MetalLB. | - -The compose file defaults to `NodePort`. Override it with: - -```bash -export K8S_CONTAINER_SP_EXTERNAL_SVC_TYPE=LoadBalancer -``` - -## Why this is needed - -| Problem | Cause | -|---|---| -| SP container can't reach Kind's IP | That IP belongs to Kind's network; compose services are on `deploy_default` | -| SP connects to `127.0.0.1:` | Kind's default kubeconfig uses the host-side port mapping, unreachable from other containers | -| TLS error using arbitrary hostname | The API server certificate only includes specific SANs | - -Connecting Kind to the compose network with a certificate-valid alias -and generating a kubeconfig that targets it solves all three. diff --git a/deploy/docs/three-tier-app-kind.md b/deploy/docs/three-tier-app-kind.md deleted file mode 100644 index 11a2f6b..0000000 --- a/deploy/docs/three-tier-app-kind.md +++ /dev/null @@ -1,272 +0,0 @@ -# Three-Tier Demo App Service Provider with Kind - -The Three-Tier Demo App Service Provider (SP) is a DCM plugin that provisions a Pet Clinic application -into a Kubernetes cluster. It requires the k8s-container-service-provider to be configured and running. - -## Setup (one-time, until containers are recreated) - -### CLI or curl - -In the Compose setup from this repo, the control-plane API is **http://localhost:8080** by default. The -sections below give **`dcm`** ([CLI repo](https://github.com/dcm-project/cli)) and **`curl`** for the -same operations. They are equivalent, so use whichever you prefer. -Build or install **`dcm`** per the [CLI README](https://github.com/dcm-project/cli/blob/main/README.md). -Set the control-plane API URL in **`~/.dcm/config.yaml`** (`control-plane-url`) or via -**`DCM_CONTROL_PLANE_URL`** if the API is not on localhost:8080 (default). - -### Prerequisites - -Before starting the three-tier SP, you must complete the k8s-container-service-provider setup: - -1. Follow steps 1–5 in [k8s-container-sp-kind.md](k8s-container-sp-kind.md) to set up: - - A Kind cluster connected to the Compose network - - A kubeconfig configured to use the `kubernetes` alias - - The k8s-container-service-provider running and healthy - -Verify the setup: - -```bash -dcm sp provider list -``` - -Or: - -```bash -curl -s http://localhost:8080/api/v1alpha1/providers | jq '.providers[] | {name, health_status}' -``` - -> **Note:** The response should include `k8s-container-provider` in the list of available providers. - -### 1. Start the three-tier SP - -Using the Makefile (recommended): - -```bash -export K8S_CONTAINER_SP_KUBECONFIG="$(pwd)/kubeconfig.yaml" -make compose-up-with-providers PROFILES=three-tier -``` - -Or the same compose invocation without Make: - -```bash -export K8S_CONTAINER_SP_KUBECONFIG="$(pwd)/kubeconfig.yaml" -podman compose --profile three-tier up -d -``` - -> **Note:** The three-tier profile automatically includes the k8s-container-service-provider -> as a dependency. Both services will start together. - -Verify it is running: - -```bash -podman ps --format "table {{.Names}}\t{{.Status}}" | grep -E 'three-tier|k8s-container' -``` - -Check the SP is registered with DCM: - -```bash -dcm sp provider list -``` - -Or: - -```bash -curl -s http://localhost:8080/api/v1alpha1/providers | jq '.providers[] | select(.name | contains("three-tier"))' -``` - -Note the provider’s `name` (by default `three-tier-provider` unless you changed `THREE_TIER_SP_NAME` in Compose). - -### 2. Provision the Pet Clinic application - -Register a placement policy once, then create a catalog item instance via the -control-plane API (**`dcm`** or **`curl`**). The **First time only (policy)** note sits next to the commands in each subsection. - -#### With DCM CLI - -Create a folder and both YAML files there (copy and paste the whole block): - -```bash -mkdir -p /tmp/dcm-petclinic - -cat > /tmp/dcm-petclinic/three-tier-placement.yaml <<'EOF' -display_name: Three-tier placement -policy_type: GLOBAL -enabled: true -priority: 100 -rego_code: | - package policies.three_tier_default - - main := { - "rejected": false, - "selected_provider": "three-tier-provider" - } if { - input.spec.service_type == "three-tier-app-demo" - } -EOF - -cat > /tmp/dcm-petclinic/my-petclinic.yaml <<'EOF' -api_version: v1alpha1 -display_name: my-petclinic -spec: - catalog_item_id: pet-clinic - user_values: - - path: database.engine - value: postgres - - path: database.version - value: "18" -EOF -``` - -**`three-tier-placement.yaml`** is a placement policy so Policy Manager routes **`three-tier-app-demo`** to -**`three-tier-provider`**. **`my-petclinic.yaml`** is the catalog instance (Pet Clinic item and DB -settings). - -**Control-plane URL:** This repo does not ship a DCM config file. The CLI defaults to -`http://localhost:8080`, which matches this compose stack; **no config file is required** for -that case. Otherwise set `export DCM_CONTROL_PLANE_URL=…` for the session, or create -`~/.dcm/config.yaml`: - -```yaml -control-plane-url: http://localhost:8080 -``` - -If Policy Manager has never been configured, add the rule below so the -platform selects your three-tier provider (same `name` as in step **1**, usually `three-tier-provider`). -Without it, the instance create call can fail with `policy response missing selected provider`. - -Rego sees the instance **`spec`** as **`input.spec`**. This example matches -`service_type == "three-tier-app-demo"`; other service types skip this policy. - -```bash -dcm policy create --from-file /tmp/dcm-petclinic/three-tier-placement.yaml --id three-tier-placement -``` - -Then provision the three tier demo app: - -```bash -dcm catalog item list -dcm catalog instance create --from-file /tmp/dcm-petclinic/my-petclinic.yaml -``` - -#### With `curl` - -**First time only (policy):** Same placement rule as above. POST the policy below only if it is not -already present (duplicate returns **409**). - -```bash -curl -s -X POST 'http://localhost:8080/api/v1alpha1/policies?id=three-tier-placement' \ - -H 'Content-Type: application/json' \ - -d @- <<'JSON' -{ - "display_name": "Three-tier placement", - "policy_type": "GLOBAL", - "enabled": true, - "priority": 100, - "rego_code": "package policies.three_tier_default\n\nmain := {\n \"rejected\": false,\n \"selected_provider\": \"three-tier-provider\"\n} if {\n input.spec.service_type == \"three-tier-app-demo\"\n}\n" -} -JSON -``` - -List catalog items to find the Pet Clinic offering: - -```bash -curl -s http://localhost:8080/api/v1alpha1/catalog-items | jq . -``` - -> **Note:** Look for a catalog item with a `display_name` that indicates a Pet Clinic service. Note its `uid` value. - -Create a catalog item instance: - -```bash -curl -sS -X POST http://localhost:8080/api/v1alpha1/catalog-item-instances \ - -H "Content-Type: application/json" \ - -d '{ - "api_version": "v1alpha1", - "display_name": "my-petclinic", - "spec": { - "catalog_item_id": "pet-clinic", - "user_values": [ - { "path": "database.engine", "value": "postgres" }, - { "path": "database.version", "value": "18" } - ] - } -}' -``` - -> **Note:** Catalog Manager only applies `user_values` whose `path` matches an **editable** field on -> that catalog item. For the seeded Pet Clinic offering (`pet-clinic`), only **`database.engine`** -> and **`database.version`** are editable; app and web images use the catalog defaults. - -### 3. Verify the Pet Clinic application is running - -Monitor the Pet Clinic deployment in Kubernetes: - -```bash -kubectl get pods -n default -``` - -Wait for the Pet Clinic pod(s) to reach `Running` status. - -Find the services: - -```bash -kubectl get svc -n default -``` - -The **web** tier is a Service whose name ends with `-web` (HTTP). On Kind, that Service is usually **ClusterIP** only, so your browser cannot reach it directly from the host. Forward a local port to it (adjust the service name and ports to match `kubectl get svc`; web often uses port **80**): - -```bash -kubectl port-forward -n default svc/-web 8080:80 -``` - -Then open **http://localhost:8080** in a browser. -Use **Ctrl+C** in the terminal to stop forwarding. - -## Troubleshooting - -### The three-tier SP fails to start - -If **`compose up`** errors or a container exits, inspect: - -```bash -podman compose --profile three-tier ps -podman ps -a --format "{{.Names}}\t{{.Status}}" | grep -i three-tier -podman compose --profile three-tier logs --tail=80 three-tier-demo-service-provider -podman compose --profile three-tier logs --tail=80 k8s-container-service-provider -``` - -Then check logs for a specific container name if needed: - -```bash -podman logs -``` - -Common issues: - -- **Kubeconfig not mounted correctly:** Verify `K8S_CONTAINER_SP_KUBECONFIG` is set and the file exists. -- **k8s-container-service-provider not running:** Ensure the k8s-container-service-provider is healthy. -- **NATS or Postgres not ready:** Check that `nats` and `postgres` services are running. - -### Pet Clinic pod fails to start - -Check the pod events: - -```bash -kubectl describe pod -n default -``` - -Check logs: - -```bash -kubectl logs -n default -``` - -### Typical errors and causes - -| Problem | Cause | -|---|---| -| `policy response missing selected provider` | No enabled Policy Manager policy sets `selected_provider`, or the value does not match a registered provider `name`. Add the policy in step **2**. | -| `provider '…' is not in ready state (not_ready)` | Service Provider Manager periodically GETs `{endpoint}/health`. If that fails repeatedly, the provider becomes `not_ready` and SPRM rejects provisioning. Ensure the three-tier process responds with 2xx on that path (the demo SP redirects `/health` to `/api/v1alpha1/health`), then wait for the next health check cycle or restart the stack. Verify with `GET /api/v1alpha1/providers` and `health_status: "ready"`. | -| Three-tier SP cannot provision apps without k8s-container-service-provider | The three-tier SP is a high-level orchestration layer that delegates resource provisioning to a k8s-container-service-provider | -| App is unreachable from the host | Kind often exposes the web Service as ClusterIP only. Use `kubectl port-forward` to the **`-web`** Service (step **3**). | -| Deployment hangs or fails | Missing environment variables or unhealthy dependencies (NATS, Postgres, k8s-container-service-provider) |