diff --git a/docs/developer-guide/workflows/ci/overview.md b/docs/developer-guide/workflows/ci/overview.md index 951e27b6..c62a535a 100644 --- a/docs/developer-guide/workflows/ci/overview.md +++ b/docs/developer-guide/workflows/ci/overview.md @@ -187,7 +187,7 @@ If your organization uses an external CI platform (e.g., Jenkins, GitHub Actions 1. Navigate to **Create** in Backstage 2. Select your component type and fill in **Component Metadata** 3. In the **Build & Deploy** step, under **Deployment Source**, select **"External CI"** -4. Optionally select your **CI Platform** (e.g., Jenkins) to enable build visibility in Backstage, and provide the **Jenkins Job Path** (e.g., `/job/my-org/job/my-service`) +4. Optionally select your **CI Platform** (e.g., Jenkins) to enable build visibility in Backstage, and provide the **Jenkins job full name** (e.g., `my-org/my-service`, or just `my-service` for a job outside a folder — not the `/job/...` URL path) 5. Complete the remaining steps and review The component is created without a workload. Your CI pipeline will create workloads when builds complete by calling the OpenChoreo Workload API: diff --git a/docs/platform-engineer-guide/workflows/external-ci.mdx b/docs/platform-engineer-guide/workflows/external-ci.mdx index be14c417..a3069331 100644 --- a/docs/platform-engineer-guide/workflows/external-ci.mdx +++ b/docs/platform-engineer-guide/workflows/external-ci.mdx @@ -43,6 +43,20 @@ Steps 1–3 cover wiring an external CI pipeline to the Workload API, with worke - Access to your identity provider (ThunderID IDP or configured OIDC provider) - Your CI system (Jenkins, GitHub Actions, or any other) configured and accessible - Container registry accessible to both CI and OpenChoreo cluster +- `curl` and `jq` available on the machine running the pipeline + +:::note +The examples below use `jq` to read the access token out of the token response. GitHub Actions runners include it, but the official `jenkins/jenkins:lts` image does **not** — a pipeline on a stock Jenkins agent fails with `jq: not found`. Install it on the agent, or bake it into your image: + +```dockerfile +FROM jenkins/jenkins:lts +USER root +RUN apt-get update && apt-get install -y --no-install-recommends jq \ + && rm -rf /var/lib/apt/lists/* +USER jenkins +``` + +::: ## Step 1: Create a Service Account @@ -69,10 +83,17 @@ You can test the credentials by exchanging them for an access token: ```bash curl -X POST "/oauth2/token" \ - -u ":" \ - -d "grant_type=client_credentials" + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "client_id=" \ + --data-urlencode "client_secret=" ``` +:::note +Backend Service applications are registered with `token_endpoint_auth_method: client_secret_post`, so the client credentials must be sent as form parameters in the request body. Passing them as HTTP Basic credentials (`curl -u`) is rejected with `unauthorized_client`. + +Use `--data-urlencode` rather than `-d`: a client secret containing `&`, `+`, or `=` would otherwise be split into extra form fields and the request would fail. +::: + :::tip For long-running CI pipelines, configure a longer token validity period in the application settings within the ThunderID console. ::: @@ -139,19 +160,51 @@ pipeline { string(credentialsId: 'openchoreo-api-url', variable: 'OPENCHOREO_API_URL') ]) { sh ''' - # Get access token + set -eu + + # Jenkins runs sh with -x. Jenkins masks the credentials it + # injected, but not the token derived from them -- without + # this, the bearer token is echoed into the build log. + set +x + + # 1. Get an access token (client_credentials grant) TOKEN=$(curl -sf -X POST "${THUNDER_URL}/oauth2/token" \ - -d "grant_type=client_credentials" \ - -d "client_id=${CLIENT_ID}" \ - -d "client_secret=${CLIENT_SECRET}" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "client_id=${CLIENT_ID}" \ + --data-urlencode "client_secret=${CLIENT_SECRET}" \ | jq -r '.access_token') - # Create/update workload - curl -sf -X POST \ + # 2. Build the Workload CR payload + WORKLOAD_NAME="${COMPONENT}-workload" + cat > workload-cr.json </oauth2/token" \ - -u ":" \ - -d "grant_type=client_credentials" + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "client_id=" \ + --data-urlencode "client_secret=" ``` +:::note +Backend Service applications are registered with `token_endpoint_auth_method: client_secret_post`, so the client credentials must be sent as form parameters in the request body. Passing them as HTTP Basic credentials (`curl -u`) is rejected with `unauthorized_client`. + +Use `--data-urlencode` rather than `-d`: a client secret containing `&`, `+`, or `=` would otherwise be split into extra form fields and the request would fail. +::: + :::tip For long-running CI pipelines, configure a longer token validity period in the application settings within the ThunderID console. ::: @@ -139,19 +160,51 @@ pipeline { string(credentialsId: 'openchoreo-api-url', variable: 'OPENCHOREO_API_URL') ]) { sh ''' - # Get access token + set -eu + + # Jenkins runs sh with -x. Jenkins masks the credentials it + # injected, but not the token derived from them -- without + # this, the bearer token is echoed into the build log. + set +x + + # 1. Get an access token (client_credentials grant) TOKEN=$(curl -sf -X POST "${THUNDER_URL}/oauth2/token" \ - -d "grant_type=client_credentials" \ - -d "client_id=${CLIENT_ID}" \ - -d "client_secret=${CLIENT_SECRET}" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "client_id=${CLIENT_ID}" \ + --data-urlencode "client_secret=${CLIENT_SECRET}" \ | jq -r '.access_token') - # Create/update workload - curl -sf -X POST \ + # 2. Build the Workload CR payload + WORKLOAD_NAME="${COMPONENT}-workload" + cat > workload-cr.json <