Fix provider MSP compose mount identity

This commit is contained in:
rcourtman 2026-06-02 15:25:05 +01:00
parent f68d0ae079
commit 25febfbf82
5 changed files with 49 additions and 10 deletions

View file

@ -15,6 +15,9 @@ CP_ENV=production
CP_ADMIN_KEY=
PULSE_PROVIDER_MSP_DATA_DIR=/data
PULSE_PROVIDER_MSP_DOCKER_NETWORK=pulse-provider-msp
PULSE_PROVIDER_MSP_DOCKER_SOCKET=/var/run/docker.sock
PULSE_PROVIDER_MSP_HOST_ROOT=/
PULSE_PROVIDER_MSP_DOCKER_DATA_DIR=/var/lib/docker
CP_PROVIDER_MSP_LICENSE_FILE=./provider-msp-license.jwt
CP_TRIAL_ACTIVATION_PRIVATE_KEY=
CP_TENANT_MEMORY_LIMIT=536870912

View file

@ -5,7 +5,7 @@ services:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${PULSE_PROVIDER_MSP_DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./traefik-dynamic.yml:/etc/traefik/traefik-dynamic.yml:ro
- acme-data:/etc/traefik/acme
@ -22,16 +22,16 @@ services:
control-plane:
image: ${CONTROL_PLANE_IMAGE:?CONTROL_PLANE_IMAGE must be set to a digest-pinned image}
volumes:
- ${PULSE_PROVIDER_MSP_DATA_DIR:-/data}:/data
- /:/host-root:ro
- /var/lib/docker:/host-var-lib-docker:ro
- /var/run/docker.sock:/var/run/docker.sock
- ${PULSE_PROVIDER_MSP_DATA_DIR:-/data}:${PULSE_PROVIDER_MSP_DATA_DIR:-/data}
- ${PULSE_PROVIDER_MSP_HOST_ROOT:-/}:/host-root:ro
- ${PULSE_PROVIDER_MSP_DOCKER_DATA_DIR:-/var/lib/docker}:/host-var-lib-docker:ro
- ${PULSE_PROVIDER_MSP_DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock
secrets:
- provider_msp_license
env_file:
- .env
environment:
- CP_DATA_DIR=/data
- CP_DATA_DIR=${PULSE_PROVIDER_MSP_DATA_DIR:-/data}
- CP_ENV=${CP_ENV}
- CP_CONTROL_PLANE_MODE=provider_hosted_msp
- CP_BIND_ADDRESS=0.0.0.0
@ -45,7 +45,7 @@ services:
- CP_ALLOW_DOCKERLESS_PROVISIONING=${CP_ALLOW_DOCKERLESS_PROVISIONING:-false}
- CP_STORAGE_GUARDRAILS_ENABLED=${CP_STORAGE_GUARDRAILS_ENABLED:-true}
- CP_STORAGE_ROOT_PATH=/host-root
- CP_STORAGE_DATA_PATH=/data
- CP_STORAGE_DATA_PATH=${PULSE_PROVIDER_MSP_DATA_DIR:-/data}
- CP_STORAGE_DOCKER_PATH=/host-var-lib-docker
- CP_STORAGE_MIN_ROOT_AVAILABLE=${CP_STORAGE_MIN_ROOT_AVAILABLE:-10GiB}
- CP_STORAGE_MIN_DATA_AVAILABLE=${CP_STORAGE_MIN_DATA_AVAILABLE:-5GiB}

View file

@ -8,6 +8,9 @@ IFS=$'\n\t'
PULSE_PROVIDER_MSP_INSTALL_DIR="${PULSE_PROVIDER_MSP_INSTALL_DIR:-/opt/pulse-provider-msp}"
PULSE_PROVIDER_MSP_DATA_DIR="${PULSE_PROVIDER_MSP_DATA_DIR:-/data}"
PULSE_PROVIDER_MSP_DOCKER_NETWORK="${PULSE_PROVIDER_MSP_DOCKER_NETWORK:-pulse-provider-msp}"
PULSE_PROVIDER_MSP_DOCKER_SOCKET="${PULSE_PROVIDER_MSP_DOCKER_SOCKET:-/var/run/docker.sock}"
PULSE_PROVIDER_MSP_HOST_ROOT="${PULSE_PROVIDER_MSP_HOST_ROOT:-/}"
PULSE_PROVIDER_MSP_DOCKER_DATA_DIR="${PULSE_PROVIDER_MSP_DOCKER_DATA_DIR:-/var/lib/docker}"
PULSE_PROVIDER_MSP_BUNDLE_URL="${PULSE_PROVIDER_MSP_BUNDLE_URL:-}"
PULSE_PROVIDER_MSP_EXPECT_ENV="${PULSE_PROVIDER_MSP_EXPECT_ENV:-production}"
PULSE_PROVIDER_MSP_SKIP_PULL="${PULSE_PROVIDER_MSP_SKIP_PULL:-0}"
@ -244,6 +247,7 @@ Edit it now and set required values:
- CP_ADMIN_KEY
- PULSE_PROVIDER_MSP_DATA_DIR
- PULSE_PROVIDER_MSP_DOCKER_NETWORK
- PULSE_PROVIDER_MSP_DOCKER_SOCKET
- CP_PROVIDER_MSP_LICENSE_FILE
- CP_TRIAL_ACTIVATION_PRIVATE_KEY
@ -269,7 +273,7 @@ validate_env_file() {
local missing=()
local k v
for k in DOMAIN ACME_EMAIL CF_DNS_API_TOKEN CP_ENV TRAEFIK_IMAGE CONTROL_PLANE_IMAGE CP_ADMIN_KEY CP_PULSE_IMAGE PULSE_PROVIDER_MSP_DATA_DIR PULSE_PROVIDER_MSP_DOCKER_NETWORK CP_PROVIDER_MSP_LICENSE_FILE CP_TRIAL_ACTIVATION_PRIVATE_KEY CP_TENANT_MEMORY_LIMIT CP_ALLOW_DOCKERLESS_PROVISIONING CP_STORAGE_GUARDRAILS_ENABLED CP_STORAGE_MIN_ROOT_AVAILABLE CP_STORAGE_MIN_DATA_AVAILABLE CP_STORAGE_MIN_DOCKER_AVAILABLE CP_STORAGE_MAX_DOCKER_BUILD_CACHE CP_PROOF_TENANT_MAX_AGE CP_PROOF_TENANT_MATCHERS CP_REQUIRE_EMAIL_PROVIDER PULSE_EMAIL_FROM PULSE_EMAIL_REPLY_TO; do
for k in DOMAIN ACME_EMAIL CF_DNS_API_TOKEN CP_ENV TRAEFIK_IMAGE CONTROL_PLANE_IMAGE CP_ADMIN_KEY CP_PULSE_IMAGE PULSE_PROVIDER_MSP_DATA_DIR PULSE_PROVIDER_MSP_DOCKER_NETWORK PULSE_PROVIDER_MSP_DOCKER_SOCKET PULSE_PROVIDER_MSP_HOST_ROOT PULSE_PROVIDER_MSP_DOCKER_DATA_DIR CP_PROVIDER_MSP_LICENSE_FILE CP_TRIAL_ACTIVATION_PRIVATE_KEY CP_TENANT_MEMORY_LIMIT CP_ALLOW_DOCKERLESS_PROVISIONING CP_STORAGE_GUARDRAILS_ENABLED CP_STORAGE_MIN_ROOT_AVAILABLE CP_STORAGE_MIN_DATA_AVAILABLE CP_STORAGE_MIN_DOCKER_AVAILABLE CP_STORAGE_MAX_DOCKER_BUILD_CACHE CP_PROOF_TENANT_MAX_AGE CP_PROOF_TENANT_MATCHERS CP_REQUIRE_EMAIL_PROVIDER PULSE_EMAIL_FROM PULSE_EMAIL_REPLY_TO; do
v="$(env_value "${k}" "${env_path}")"
if [[ -z "${v}" ]]; then
missing+=("${k}")
@ -284,6 +288,20 @@ validate_env_file() {
die "CP_ENV must be '${expected_env}' for this setup run (got '${cp_env}')"
fi
local path_var path_value
for path_var in PULSE_PROVIDER_MSP_DATA_DIR PULSE_PROVIDER_MSP_DOCKER_SOCKET PULSE_PROVIDER_MSP_HOST_ROOT PULSE_PROVIDER_MSP_DOCKER_DATA_DIR; do
path_value="$(env_value "${path_var}" "${env_path}")"
if [[ "${path_value}" != /* ]]; then
die "${path_var} must be an absolute path"
fi
done
if [[ ! -S "$(env_value PULSE_PROVIDER_MSP_DOCKER_SOCKET "${env_path}")" ]]; then
die "PULSE_PROVIDER_MSP_DOCKER_SOCKET must point to a reachable Docker socket"
fi
if [[ ! -d "$(env_value PULSE_PROVIDER_MSP_HOST_ROOT "${env_path}")" ]]; then
die "PULSE_PROVIDER_MSP_HOST_ROOT must point to an existing directory"
fi
local image_ref
for k in TRAEFIK_IMAGE CONTROL_PLANE_IMAGE CP_PULSE_IMAGE; do
image_ref="$(env_value "${k}" "${env_path}")"
@ -306,6 +324,9 @@ validate_env_file() {
if ! truthy "$(env_value CP_STORAGE_GUARDRAILS_ENABLED "${env_path}")"; then
die "CP_STORAGE_GUARDRAILS_ENABLED must be true for provider-hosted MSP deploys"
fi
if [[ ! -d "$(env_value PULSE_PROVIDER_MSP_DOCKER_DATA_DIR "${env_path}")" ]]; then
die "PULSE_PROVIDER_MSP_DOCKER_DATA_DIR must point to the host Docker data directory"
fi
local proof_matchers required_matcher
proof_matchers="$(env_value CP_PROOF_TENANT_MATCHERS "${env_path}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"

View file

@ -158,7 +158,10 @@ surfaces.
missing signed MSP license files, Dockerless production provisioning, disabled
storage guardrails, or Stripe/cloud-signup variables, validate compose, and
optionally hand off to `run-install-proof.sh` when the provider account name
and owner email are supplied.
and owner email are supplied. Because provider-hosted MSP provisions tenant
containers through the host Docker socket, the provider data directory must
be mounted at the same absolute path inside the control-plane container that
the host Docker daemon will later use for tenant runtime bind mounts.
`pulse-control-plane provider-msp status` is the non-mutating operational
companion to that proof: it must report registry readiness, tenant
state/health counts, stuck provisioning workspaces, Docker runtime

View file

@ -22,9 +22,12 @@ func TestProviderMSPDeployComposeIsProviderModeAndStripeFree(t *testing.T) {
text := string(composeBytes)
assertContainsAll(t, text,
"CP_CONTROL_PLANE_MODE=provider_hosted_msp",
"CP_DATA_DIR=${PULSE_PROVIDER_MSP_DATA_DIR:-/data}",
"CP_PROVIDER_MSP_LICENSE_FILE=/run/secrets/provider_msp_license",
"CP_DOCKER_NETWORK=${PULSE_PROVIDER_MSP_DOCKER_NETWORK:-pulse-provider-msp}",
"${PULSE_PROVIDER_MSP_DATA_DIR:-/data}:/data",
"CP_STORAGE_DATA_PATH=${PULSE_PROVIDER_MSP_DATA_DIR:-/data}",
"${PULSE_PROVIDER_MSP_DATA_DIR:-/data}:${PULSE_PROVIDER_MSP_DATA_DIR:-/data}",
"${PULSE_PROVIDER_MSP_DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock",
"provider_msp_license:",
"name: ${PULSE_PROVIDER_MSP_DOCKER_NETWORK:-pulse-provider-msp}",
)
@ -48,6 +51,9 @@ func TestProviderMSPDeployEnvExampleMatchesBootstrapPath(t *testing.T) {
"CP_ENV=production",
"PULSE_PROVIDER_MSP_DATA_DIR=/data",
"PULSE_PROVIDER_MSP_DOCKER_NETWORK=pulse-provider-msp",
"PULSE_PROVIDER_MSP_DOCKER_SOCKET=/var/run/docker.sock",
"PULSE_PROVIDER_MSP_HOST_ROOT=/",
"PULSE_PROVIDER_MSP_DOCKER_DATA_DIR=/var/lib/docker",
"CP_PROVIDER_MSP_LICENSE_FILE=./provider-msp-license.jwt",
"CP_TRIAL_ACTIVATION_PRIVATE_KEY=",
"sudo -E ./setup.sh",
@ -104,6 +110,12 @@ func TestProviderMSPSetupScriptMatchesProviderContract(t *testing.T) {
"CP_TRIAL_ACTIVATION_PRIVATE_KEY",
"PULSE_PROVIDER_MSP_DATA_DIR",
"PULSE_PROVIDER_MSP_DOCKER_NETWORK",
"PULSE_PROVIDER_MSP_DOCKER_SOCKET",
"PULSE_PROVIDER_MSP_HOST_ROOT",
"PULSE_PROVIDER_MSP_DOCKER_DATA_DIR",
"must be an absolute path",
"must point to a reachable Docker socket",
"must point to the host Docker data directory",
"must not be configured in provider-hosted MSP mode",
"CP_ALLOW_DOCKERLESS_PROVISIONING must be false",
"CP_STORAGE_GUARDRAILS_ENABLED must be true",