Kubesphere kubesphere-devops-pipeline
Use when creating, running, or managing CI/CD pipelines in KubeSphere DevOps, including pipeline API operations and run monitoring
git clone https://github.com/kubesphere/kubesphere
T=$(mktemp -d) && git clone --depth=1 https://github.com/kubesphere/kubesphere "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/kubesphere-devops-pipeline" ~/.claude/skills/kubesphere-kubesphere-kubesphere-devops-pipeline && rm -rf "$T"
skills/kubesphere-devops-pipeline/SKILL.mdKubeSphere DevOps Pipeline Management
Overview
Pipelines in KubeSphere DevOps are Kubernetes custom resources that integrate with Jenkins. KubeSphere uses a cloud-native, object-reconcile approach where Kubernetes resources are the source of truth.
When to Use
- Creating or updating CI/CD pipelines
- Triggering pipeline runs
- Monitoring pipeline execution
- Retrieving pipeline logs and artifacts
- Troubleshooting failed pipeline runs
Architecture Mapping
KubeSphere DevOps maps Kubernetes resources to Jenkins objects:
| KubeSphere Resource | K8s Resource | Jenkins Resource |
|---|---|---|
| DevOpsProject | DevOpsProject CR + Namespace | Folder |
| Pipeline | Pipeline CR | WorkflowJob |
| PipelineRun | PipelineRun CR | Build Run |
| Workspace | Workspace CR | (authorization context) |
KubeSphere Kubernetes Jenkins ───────────────────────────────────────────────────────────── Workspace demo └── DevOpsProject → demo-project NS → Folder demo-project └── Pipeline → Pipeline CR → WorkflowJob └── Run → PipelineRun CR → Build #1
Triggering Pipeline Runs (Recommended: Object-Reconcile)
CRITICAL: ALWAYS Check for Parameters First!
Before triggering ANY pipeline (regular or multi-branch), you MUST check if the pipeline has parameters defined. Triggering a pipeline without required parameters will cause the build to fail or use incorrect defaults.
For Multi-Branch Pipelines: Query
endpoint to get/branches/{branch}array For Regular Pipelines: Query the Pipeline CR and check.parametersfor.spec.pipeline.jenkinsfiledirectiveparameters {}
Preferred Approach: Create a
PipelineRun custom resource. KubeSphere watches for these resources and triggers the corresponding Jenkins build.
Create a PipelineRun
apiVersion: devops.kubesphere.io/v1alpha3 kind: PipelineRun metadata: name: my-pipeline-run-001 namespace: demo-project spec: pipelineRef: name: my-pipeline parameters: - name: BRANCH value: "main"
Apply with kubectl:
kubectl apply -f pipelinerun.yaml
Check PipelineRun Status
# List all runs kubectl get pipelineruns -n demo-project # Get specific run status kubectl get pipelinerun my-pipeline-run-001 -n demo-project -o yaml # Watch run progress kubectl get pipelineruns -n demo-project -w
PipelineRun Status Fields
| Field | Description |
|---|---|
| Current state (Pending, Running, Succeeded, Failed, Unknown) |
| Detailed conditions (Succeeded, Ready) |
| When run finished |
| When run started |
Delete a PipelineRun
kubectl delete pipelinerun my-pipeline-run-001 -n demo-project
Working Pipeline Example
Here's a complete, working pipeline that builds a Go application:
apiVersion: devops.kubesphere.io/v1alpha3 kind: Pipeline metadata: name: go-demo-pipeline namespace: demo-project spec: type: pipeline pipeline: name: go-demo-pipeline description: "Build and test Go application" jenkinsfile: | pipeline { agent any stages { stage('Build, Test and Archive') { agent { kubernetes { yaml ''' apiVersion: v1 kind: Pod spec: containers: - name: golang image: golang:1.21 command: ["sleep"] args: ["99d"] ''' } } steps { container('golang') { sh ''' export GO111MODULE=on git clone https://github.com/kubesphere-sigs/demo-go-http.git . go mod download go test ./... -v go build -o service main.go ''' } archiveArtifacts artifacts: 'service', followSymlinks: false } } } }
Key Points:
- Uses
to define a custom pod with Go containeragent { kubernetes { yaml ... } } - The
step must be in the same stage as the build (same workspace)archiveArtifacts - Container name in
must match the container name in the YAMLcontainer('golang')
Pipeline Resource
apiVersion: devops.kubesphere.io/v1alpha3 kind: Pipeline metadata: name: my-pipeline namespace: demo-project spec: type: pipeline pipeline: name: my-pipeline description: "Build and deploy app" jenkinsfile: |- pipeline { agent any stages { stage('Build') { steps { sh 'make build' } } } }
Tenant-Created Resources: Creator Annotation
When creating pipelines as a tenant (not cluster-admin), you MUST include the
kubesphere.io/creator annotation to properly track ownership:
apiVersion: devops.kubesphere.io/v1alpha3 kind: Pipeline metadata: name: my-pipeline namespace: demo-project annotations: kubesphere.io/creator: "stone-ns-admin" # Required for tenant-created resources spec: type: pipeline pipeline: name: my-pipeline description: "Pipeline created by tenant" jenkinsfile: |- pipeline { agent any stages { stage('Build') { steps { sh 'echo Building...' } } } }
Why this matters:
- KubeSphere uses this annotation for ownership tracking
- UI displays creator information
- Required for proper RBAC enforcement
- CRITICAL: Always set this annotation when creating ANY pipeline via API (both regular and multi-branch)
Example API call with creator annotation for regular pipeline:
curl -s -X POST "${KUBESPHERE_API}/kapis/devops.kubesphere.io/v1alpha3/namespaces/demo-project/pipelines" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "devops.kubesphere.io/v1alpha3", "kind": "Pipeline", "metadata": { "name": "my-pipeline", "namespace": "demo-project", "annotations": { "kubesphere.io/creator": "'${USERNAME}'" } }, "spec": { "type": "pipeline", "pipeline": { "name": "my-pipeline", "description": "Tenant-created pipeline", "jenkinsfile": "pipeline { agent any; stages { stage(\"Build\") { steps { sh \"echo hello\" } } } }" } } }'
Multi-Branch Pipeline
Multi-branch pipelines automatically discover branches from SCM and create jobs for each branch. The Jenkinsfile is loaded from the repository.
⚠️ CRITICAL: Always Check Repository Type First
Before creating a multi-branch pipeline, you MUST ask the user:
"Is this a private repository?"
If YES (Private Repo):
- Ask if they want to use an existing credential or create a new one
- Create a DevOps credential (
type with GitHub PAT) if neededbasic-auth- Reference the credential in
git_source.credential_id- (Optional) Create a GitRepository CR for additional metadata
If NO (Public Repo):
- Set
(empty string)credential_id: ""Never assume repository type - always confirm with the user first.
Create Multi-Branch Pipeline
Step 1: Check Repository Type
- Ask user: "Is the repository private?"
- If yes, proceed with credential creation
Step 2: Create Credential (For Private Repos Only)
# Create GitHub credential export GITHUB_PAT="ghp_xxxxxxxxxxxxxxxxxxxx" curl -s -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/credentials" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "v1", "kind": "Secret", "metadata": { "name": "github-token", "namespace": "'${DEVOPS_PROJECT}'", "annotations": { "kubesphere.io/creator": "'${USERNAME}'", "credential.devops.kubesphere.io/type": "basic-auth" } }, "stringData": { "username": "git", "password": "'${GITHUB_PAT}'" }, "type": "credential.devops.kubesphere.io/basic-auth" }'
Step 3a: For Public Repository:
apiVersion: devops.kubesphere.io/v1alpha3 kind: Pipeline metadata: name: demo-jenkinsfiles-go namespace: demo-project annotations: kubesphere.io/creator: "stone-ns-admin" # Required for tenant-created resources spec: type: multi-branch-pipeline multi_branch_pipeline: name: demo-jenkinsfiles-go description: "Multi-branch Go pipeline" source_type: git git_source: url: https://github.com/kubesphere/demo-jenkinsfiles credential_id: "" # Empty for public repos discover_branches: true discover_tags: false script_path: go/Jenkinsfile # Path to Jenkinsfile in repo
**Step 3b: For Private Repository (with credential):
apiVersion: devops.kubesphere.io/v1alpha3 kind: Pipeline metadata: name: private-repo-pipeline namespace: demo-project annotations: kubesphere.io/creator: "stone-ns-admin" spec: type: multi-branch-pipeline multi_branch_pipeline: name: private-repo-pipeline description: "Pipeline for private repository" source_type: git git_source: url: https://github.com/org/private-repo.git credential_id: "github-token" # Reference to DevOps credential discover_branches: true discover_tags: false script_path: Jenkinsfile
Complete Flow for Private Repo:
- Ask user if repository is private (ALWAYS do this first)
- Create credential (
type with GitHub PAT)basic-auth - (Optional) Create GitRepository CR (with
andprovider
fields)secret - Create multi-branch pipeline referencing the credential in
git_source.credential_id
Important: Never use
GITHUB_ env vars directly in the pipeline spec. Always create proper DevOps credentials.
SCM Source Types:
| Type | Field | Use Case |
|---|---|---|
| Git | | Generic Git repositories |
| GitHub | | GitHub.com or GitHub Enterprise |
| GitLab | | GitLab.com or self-hosted GitLab |
| SVN | | Subversion repositories |
Trigger Multi-Branch Pipeline Run
apiVersion: devops.kubesphere.io/v1alpha3 kind: PipelineRun metadata: name: demo-jenkinsfiles-go-main-run namespace: demo-project spec: pipelineRef: name: demo-jenkinsfiles-go scm: refName: main # Branch name refType: branch # or 'tag'
Check Discovered Branches
Via v1alpha3 API (preferred):
curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches?filter=origin&page=1&limit=10" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" | jq -r '.items[] | "- Branch: \(.name) | Latest Run: \(.latestRun.id // "N/A") | Status: \(.latestRun.result // "N/A")"'
Via Jenkins (admin only):
kubectl run curl-jenkins --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/job/demo-project/job/demo-jenkinsfiles-go/api/json"
Trigger Repository Scanning
Note: Repository scanning uses v1alpha2 API (not v1alpha3). This is an exception to the general rule of preferring v1alpha3.
Step 1: Trigger Scan
curl -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha2/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/scan" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{}'
Step 2: Fetch Scanning Log
curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha2/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/consolelog" \ -H "Authorization: Bearer ${API_TOKEN}"
When to use:
- Force immediate repository re-scan
- Discover new branches that aren't showing up
- Troubleshoot branch detection issues
Example scanning log output:
Started by user admin Starting branch indexing... > git ls-remote --symref -- https://github.com/org/repo.git Checking branches: Checking branch main ✓ 'Jenkinsfile' found Met criteria Checking branch feature-x ✓ 'Jenkinsfile' found Met criteria Checking branch old-branch 'Jenkinsfile' not found Does not meet criteria Processed 3 branches Finished branch indexing. Indexing took 3 sec Finished: SUCCESS
Complete Private Repo Setup Example
Scenario: Create a multi-branch pipeline for a private GitHub repository with proper authentication.
Step 1: Ask User About Repository Type
Question: "Is https://github.com/stoneshi-yunify/jenkinsfiles a private repository?" User Answer: "Yes"
Step 2: Create GitHub Credential
export KUBESPHERE_API="http://kubesphere-apiserver:80" export API_TOKEN="<tenant-oauth-token>" export DEVOPS_PROJECT="devopstestc2nj7" export USERNAME="stone-ns-admin" export GITHUB_PAT="ghp_xxxxxxxxxxxxxxxxxxxx" curl -s -X POST "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/credentials" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "v1", "kind": "Secret", "metadata": { "name": "github-token", "namespace": "'${DEVOPS_PROJECT}'", "annotations": { "kubesphere.io/creator": "'${USERNAME}'", "credential.devops.kubesphere.io/type": "basic-auth" } }, "stringData": { "username": "git", "password": "'${GITHUB_PAT}'" }, "type": "credential.devops.kubesphere.io/basic-auth" }'
Step 3: Create Multi-Branch Pipeline with Credential
curl -s -X POST "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "devops.kubesphere.io/v1alpha3", "kind": "Pipeline", "metadata": { "name": "echo-jenkinsfile-pipeline", "namespace": "'${DEVOPS_PROJECT}'", "annotations": { "kubesphere.io/creator": "'${USERNAME}'" } }, "spec": { "type": "multi-branch-pipeline", "multi_branch_pipeline": { "name": "echo-jenkinsfile-pipeline", "description": "Multi-branch pipeline with GitHub auth", "source_type": "git", "git_source": { "url": "https://github.com/stoneshi-yunify/jenkinsfiles.git", "credential_id": "github-token", "discover_branches": true, "discover_tags": false }, "script_path": "echo/Jenkinsfile" } } }'
Note: The
kubesphere.io/creator annotation is required for all pipelines (both regular and multi-branch). Without it, the pipeline may not appear correctly in the KubeSphere UI.
Step 4: Verify Creation
curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/echo-jenkinsfile-pipeline" \ -H "Authorization: Bearer ${API_TOKEN}" | jq -r '{ name: .metadata.name, syncStatus: .metadata.annotations."pipeline.devops.kubesphere.io/syncstatus", credential: .spec.multi_branch_pipeline.git_source.credential_id }'
Expected output:
{ "name": "echo-jenkinsfile-pipeline", "syncStatus": "successful", "credential": "github-token" }
Complete Multi-Branch Workflow Example
Scenario: Trigger build, monitor, retrieve logs, and download artifacts.
Step 1: Check Pipeline Exists
kubectl get pipelines -n demo-project # NAME TYPE AGE # demo-jenkinsfiles-go multi-branch-pipeline 12d
Step 2: Trigger Build via Jenkins API
# Get token TOKEN=$(kubectl -n kubesphere-devops-system get secret devops-jenkins -o jsonpath='{.data.jenkins-admin-token}' | base64 -d) # Trigger main branch build kubectl run curl-trigger --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/job/demo-project/job/demo-jenkinsfiles-go/job/main/build" \ -X POST -w "\nHTTP Status: %{http_code}\n" # Expected: HTTP Status: 201
Step 3: Monitor Build Status
# Check PipelineRun created kubectl get pipelineruns -n demo-project --sort-by=.metadata.creationTimestamp | tail -3 # Example output: # demo-jenkinsfiles-go-vf8p5 3 Succeeded 2m # Get detailed status # Or check via Jenkins API kubectl run curl-status --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/job/demo-project/job/demo-jenkinsfiles-go/job/main/3/api/json" \ | grep -E '"result"|"building"|"duration"'
Step 4: Retrieve Console Log
# Get full console log from build #3 kubectl run curl-log --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/job/demo-project/job/demo-jenkinsfiles-go/job/main/3/consoleText" # Look for: # [Pipeline] Start of Pipeline # [Pipeline] { (Clone Repository) # [Pipeline] { (Run Tests) # === RUN TestHello # --- PASS: TestHello (0.00s) # [Pipeline] End of Pipeline # Finished: SUCCESS
Step 5: Download Build Artifacts
For binary artifacts, use pod-based download:
# Create download pod kubectl run artifact-downloader --image=curlimages/curl -- sleep 300 kubectl wait --for=condition=Ready pod/artifact-downloader --timeout=60s # Download artifact inside pod TOKEN=$(kubectl -n kubesphere-devops-system get secret devops-jenkins -o jsonpath='{.data.jenkins-admin-token}' | base64 -d) kubectl exec artifact-downloader -- sh -c \ "curl -s -o /tmp/service 'http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/job/demo-project/job/demo-jenkinsfiles-go/job/main/3/artifact/service'" # Copy to local kubectl cp artifact-downloader:/tmp/service /tmp/service # Clean up kubectl delete pod artifact-downloader --force
Step 6: Verify Downloaded Artifact
# Check file details ls -lh /tmp/service file /tmp/service # Expected output: # /tmp/service: ELF 64-bit LSB executable, x86-64... # Make executable and test chmod +x /tmp/service /tmp/service --help
Build Summary Example:
Build #3 Summary: - Status: SUCCESS - Duration: ~54 seconds - Test Results: 1/1 passed - Artifact: service (8.0 MB Go binary) - Stages: Checkout → Clone → Dependencies → Test → Build → Archive
Multi-Branch vs Regular Pipeline
| Feature | Regular Pipeline | Multi-Branch Pipeline |
|---|---|---|
| Type | | |
| Jenkinsfile | Inline in CRD | From SCM () |
| Branches | Single | Auto-discovered |
| SCM Config | Manual checkout | Automatic |
| Trigger | PipelineRun/API | Branch indexing + webhooks |
| Get Parameters | From | From endpoint |
Triggering Regular Pipeline Runs with Parameters (v1alpha3 API)
For regular pipelines, use this two-step procedure to handle parameters:
Step 1: Get Pipeline Definition and Extract Parameters
Unlike multi-branch pipelines, regular pipelines don't have a dedicated
/parameters endpoint. Instead, retrieve the Pipeline CR and extract parameters from the Jenkinsfile:
# Get the pipeline definition curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" | jq -r '.spec.pipeline.jenkinsfile'
Look for the
directive in the Jenkinsfile:parameters {}
pipeline { agent any stages { stage('Example') { steps { echo "Hello ${params.PERSON}" } } } parameters { string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?') booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value') choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something') } }
Parameter Types in Jenkinsfile:
| Directive | Type | Example |
|---|---|---|
| Single-line text | |
| Multi-line text | |
| True/false | |
| Dropdown | |
| Hidden value | |
Step 2: Trigger Pipeline with Parameters
Use the
/pipelineruns endpoint to create a new run with parameters:
# Trigger with parameters curl -s -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/pipelineruns" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "parameters": [ {"name": "PERSON", "value": "John Doe"}, {"name": "TOGGLE", "value": "true"}, {"name": "CHOICE", "value": "Two"} ] }' | jq -r '.metadata.name'
Key Points:
- No branch query parameter needed (regular pipelines have no branches)
- Parameters are passed in the request body as a JSON array
- Boolean values must be strings:
or"true""false" - The response includes the created PipelineRun resource with your parameters
Complete Example for Regular Pipelines
#!/bin/bash export KUBESPHERE_API="http://kubesphere-apiserver:80" export API_TOKEN="<tenant-oauth-token>" export DEVOPS_PROJECT="devopstestc2nj7" export PIPELINE_NAME="my-regular-pipeline" # Step 1: Get pipeline definition and check for parameters echo "Checking for parameters in pipeline..." JENKINSFILE=$(curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}" \ -H "Authorization: Bearer ${API_TOKEN}" | jq -r '.spec.pipeline.jenkinsfile') # Check if parameters exist if echo "$JENKINSFILE" | grep -q "parameters {"; then echo "Pipeline has parameters defined. Extracting..." # In practice, parse the Jenkinsfile to extract parameter names and defaults echo "$JENKINSFILE" | grep -E "(string|booleanParam|choice|password|text)\(name:" else echo "No parameters found. Triggering without parameters." fi # Step 2: Trigger with parameters echo "Triggering pipeline with parameters..." curl -s -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/pipelineruns" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "parameters": [ {"name": "PERSON", "value": "Test User"}, {"name": "TOGGLE", "value": "true"}, {"name": "CHOICE", "value": "Two"}, {"name": "BIOGRAPHY", "value": "Testing API"}, {"name": "PASSWORD", "value": "secret123"} ] }' | jq -r '.metadata.name'
Triggering Multi-Branch Pipeline Runs (v1alpha3 API)
⚠️ API Version Notice: The
APIs are deprecated. Always prefer/kapis/devops.kubesphere.io/v1alpha2/APIs when available.v1alpha3
For multi-branch pipelines, use this three-step procedure with v1alpha3 APIs:
Step 1: List Available Branches
curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches?filter=origin&page=1&limit=10" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" | jq -r '.items[] | "- Branch: \(.name) | Latest Run: \(.latestRun.id // "N/A") | Status: \(.latestRun.result // "N/A")"'
Example output:
- Branch: main | Latest Run: 2 | Status: SUCCESS - Branch: stone | Latest Run: 1 | Status: SUCCESS
Step 2: Ask User Which Branch to Build
Present the available branches to the user and ask:
"Which branch would you like to build?"
Step 3: Trigger Build on Selected Branch
export BRANCH="main" # User's selection curl -s -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/pipelineruns?branch=${BRANCH}" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"parameters":[]}' | jq -r '.metadata.name'
Key Points:
- Use
endpoint (not v1alpha2)v1alpha3 - Pass branch as query parameter
?branch=${BRANCH} - Returns a PipelineRun resource (Kubernetes CRD format)
- The PipelineRun name is auto-generated with the pipeline name as prefix
Triggering with Parameters
When a pipeline has parameters defined in the Jenkinsfile, you can provide values when triggering a build. Follow this workflow:
Step 1: Get Available Parameters
Query the branch to retrieve parameter definitions:
curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches/${BRANCH}" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" | jq '.parameters'
Example Response:
[ { "description": "Who should I say hello to?", "name": "PERSON", "type": "StringParameterDefinition", "value": "", "defaultParameterValue": { "name": "PERSON", "value": "Mr Jenkins" } }, { "description": "Toggle this value", "name": "TOGGLE", "type": "BooleanParameterDefinition", "value": "", "defaultParameterValue": { "name": "TOGGLE", "value": true } }, { "description": "Pick something", "name": "CHOICE", "type": "ChoiceParameterDefinition", "value": "", "defaultParameterValue": { "name": "CHOICE", "value": "One" }, "choices": ["One", "Two", "Three"] } ]
Parameter Types:
| Type | Description | Value Format |
|---|---|---|
| Single-line text | |
| Multi-line text | |
| True/false toggle | or |
| Predefined options | (must match one of ) |
| Hidden value | |
Step 2: Build Parameters Array
Create the parameters JSON array with user-provided values:
# Example parameters cat > params.json << 'EOF' { "parameters": [ {"name": "PERSON", "value": "stone"}, {"name": "BIOGRAPHY", "value": "i'm a software engineer"}, {"name": "TOGGLE", "value": "true"}, {"name": "CHOICE", "value": "Two"}, {"name": "PASSWORD", "value": "secret123"} ] } EOF
Important:
- Boolean values must be strings:
or"true""false" - Use the
field, not the descriptionname - Omit parameters to use their default values
Step 3: Trigger Build with Parameters
export BRANCH="main" # Trigger with parameters curl -s -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/pipelineruns?branch=${BRANCH}" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d @params.json | jq -r '.metadata.name'
Complete Parameterized Build Example
#!/bin/bash export KUBESPHERE_API="http://kubesphere-apiserver:80" export API_TOKEN="<tenant-oauth-token>" export DEVOPS_PROJECT="devopstestc2nj7" export PIPELINE_NAME="echo-jenkinsfile-pipeline" export BRANCH="main" # Step 1: Get parameters echo "Fetching parameter definitions..." PARAMS=$(curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches/${BRANCH}" \ -H "Authorization: Bearer ${API_TOKEN}") echo "Available parameters:" echo "$PARAMS" | jq -r '.parameters[] | "- \(.name) (\(.type)): \(.description // "No description") [Default: \(.defaultParameterValue.value // "(none)")]"' # Step 2: Ask user for values (or use defaults) # In practice, prompt user or read from input # Step 3: Build and trigger with parameters echo "Triggering build with parameters..." curl -s -X POST "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/pipelineruns?branch=${BRANCH}" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "parameters": [ {"name": "PERSON", "value": "stone"}, {"name": "BIOGRAPHY", "value": "software engineer"}, {"name": "TOGGLE", "value": "true"}, {"name": "CHOICE", "value": "Two"}, {"name": "PASSWORD", "value": "secret123"} ] }' | jq -r '.metadata.name'
Complete Working Example
#!/bin/bash export KUBESPHERE_API="http://kubesphere-apiserver:80" export API_TOKEN="<oauth-token>" export DEVOPS_PROJECT="devopstestc2nj7" export PIPELINE_NAME="echo-jenkinsfile-pipeline" # Step 1: List branches echo "Available branches:" curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches?filter=origin&page=1&limit=10" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" | jq -r '.items[] | " - \(.name)"' # Step 2: User selects branch (example: main) BRANCH="main" # Step 3: Trigger build echo "Triggering build on branch: ${BRANCH}" PIPELINE_RUN=$(curl -s -X POST "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/pipelineruns?branch=${BRANCH}" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"parameters":[]}') RUN_NAME=$(echo "$PIPELINE_RUN" | jq -r '.metadata.name') echo "PipelineRun created: ${RUN_NAME}"
Alternative: API Endpoints (Deprecated v1alpha2)
⚠️ Deprecated: The
APIs are deprecated. Use/kapis/devops.kubesphere.io/v1alpha2/APIs shown above.v1alpha3
Use APIs when you need programmatic access from external systems:
Pipeline CRUD Operations
| Operation | Method | Endpoint | Status |
|---|---|---|---|
| List Pipelines | GET | | Deprecated |
| Get Pipeline | GET | | |
| List Runs | GET | | |
| Run Pipeline (API) | POST | | |
| Get Run | GET | | |
| Stop Run | POST | | |
| Get Log | GET | | Use for tenant access |
| Get Artifacts | GET | |
API Run Example
curl -X POST "https://kubesphere-api/kapis/devops.kubesphere.io/v1alpha2/namespaces/{devops}/pipelines/{pipeline}/runs" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "parameters": [ {"name": "BRANCH", "value": "main"} ] }'
Pipeline Run Flow
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Queue │───▶│ Running │───▶│ Complete │───▶│ Success │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ ▼ ▼ ┌──────────┐ ┌──────────┐ │ Paused │ │ Failed │ │ (Input) │ │ │ └──────────┘ └──────────┘
Pipeline Status Values
| Status | Meaning |
|---|---|
| QUEUED | Waiting for available agent |
| RUNNING | Currently executing |
| PAUSED | Waiting for user input |
| SUCCESS | Completed successfully |
| FAILED | Failed during execution |
| ABORTED | Manually stopped |
Handling Paused Pipeline Steps with Input
When a pipeline reaches an
input step (e.g., approval gates), it enters the PAUSED state and waits for user interaction. Tenants can approve or reject these steps via the KubeSphere API.
Step 1: Get Node Details to Find Input Step
Use the
nodedetails endpoint to identify the paused step with input:
# Get node details for the pipeline run curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelineruns/${PIPELINE_RUN_NAME}/nodedetails" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" | jq '.[-1]'
Look for steps with
:approvable: true
{ "displayName": "Build Binary", "id": "38", "state": "PAUSED", "steps": [ { "id": "41", "displayName": "Wait for interactive input", "state": "PAUSED", "input": { "id": "Build-binary-confirm", "message": "Build binary now?", "ok": "Proceed" }, "approvable": true } ] }
Key Fields:
| Field | Description |
|---|---|
| Step ID (e.g., "41") |
| Input action ID (e.g., "Build-binary-confirm") |
| if this step can be approved/rejected |
Step 2: Approve or Reject the Input Step
Use the v1alpha2 API to submit your decision:
Approve (Proceed):
curl -s -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha2/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches/${BRANCH}/runs/${JENKINS_RUN_ID}/nodes/${NODE_ID}/steps/${STEP_ID}/" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"id":"${INPUT_ID}","abort":false}'
Reject (Abort):
curl -s -X POST "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha2/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches/${BRANCH}/runs/${JENKINS_RUN_ID}/nodes/${NODE_ID}/steps/${STEP_ID}/" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"id":"${INPUT_ID}","abort":true}'
Parameters:
| Parameter | Description | Example |
|---|---|---|
| Jenkins build number | |
| Stage/node ID from nodedetails | |
| Step ID from nodedetails | |
| Input ID from the object | |
| to proceed, to abort | |
Complete Example
export KUBESPHERE_API="http://kubesphere-apiserver:80" export API_TOKEN="<tenant-oauth-token>" export DEVOPS_PROJECT="devopstestc2nj7" export PIPELINE_NAME="echo-jenkinsfile-pipeline" export BRANCH="main" export PIPELINE_RUN_NAME="echo-jenkinsfile-pipeline-ndrkn" # Step 1: Get nodedetails and extract input information NODES=$(curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelineruns/${PIPELINE_RUN_NAME}/nodedetails" \ -H "Authorization: Bearer ${API_TOKEN}") # Extract the last node (current stage) and find approvable step NODE_ID=$(echo "$NODES" | jq -r '.[-1].id') STEP_ID=$(echo "$NODES" | jq -r '.[-1].steps[] | select(.approvable == true) | .id') INPUT_ID=$(echo "$NODES" | jq -r '.[-1].steps[] | select(.approvable == true) | .input.id') JENKINS_RUN_ID=$(echo "$NODES" | jq -r '.[-1].steps[] | select(.approvable == true) | .jenkinsRunId // "9"') echo "Approving step ${STEP_ID} in node ${NODE_ID}" echo "Input ID: ${INPUT_ID}" # Step 2: Approve the step curl -s -X POST "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha2/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches/${BRANCH}/runs/${JENKINS_RUN_ID}/nodes/${NODE_ID}/steps/${STEP_ID}/" \ -H "Authorization: Bearer ${API_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"id\":\"${INPUT_ID}\",\"abort\":false}" # Step 3: Verify the pipeline continues echo "Pipeline resumed. Check status..."
Important Notes:
- This approach works for tenants (no cluster-admin access required)
- Uses v1alpha2 API for the approval action (not available in v1alpha3)
- The trailing slash
in the URL is required/ - Empty response usually indicates success (HTTP 200)
Retrieving Build Logs via KubeSphere API
To retrieve build logs through the KubeSphere API (recommended for tenants):
Step 1: Get Jenkins PipelineRun ID
First, get the Jenkins PipelineRun ID from the PipelineRun CR annotation:
# Get fresh token TOKEN_RESPONSE=$(curl -s -X POST "${KUBESPHERE_API}/oauth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode 'grant_type=password' \ --data-urlencode "username=${USERNAME}" \ --data-urlencode "password=${PASSWORD}" \ --data-urlencode 'client_id=kubesphere' \ --data-urlencode 'client_secret=kubesphere') export API_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token') # Get Jenkins PipelineRun ID from annotation JENKINS_ID=$(curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelineruns/${PIPELINE_RUN_NAME}" \ -H "Authorization: Bearer ${API_TOKEN}" \ | jq -r '.metadata.annotations."devops.kubesphere.io/jenkins-pipelinerun-id"') echo "Jenkins Run ID: $JENKINS_ID"
Step 2: Fetch Logs
Use the v1alpha2 API to fetch logs. Note: The
?start=0 parameter is required for tenant access:
# For multi-branch pipelines curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha2/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches/${BRANCH}/runs/${JENKINS_ID}/log/?start=0" \ -H "Authorization: Bearer ${API_TOKEN}" # Example: curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha2/namespaces/devopstestc2nj7/pipelines/echo-jenkinsfile-pipeline/branches/main/runs/5/log/?start=0" \ -H "Authorization: Bearer ${API_TOKEN}"
Key Points:
- Use the
annotation (not the Kubernetes PipelineRun name)jenkins-pipelinerun-id - For multi-branch pipelines: include
in the path/branches/{branch}/ - CRITICAL: Include
query parameter - required for tenant access?start=0 - The trailing slash
at the end of the URL path is required/ - Works with tenant credentials (no cluster-admin access needed)
Retrieving Build Artifacts via KubeSphere API
To query and download build artifacts through the KubeSphere API:
Step 1: List Artifacts
Query artifacts for a specific run:
curl -s "${KUBESPHERE_API}/clusters/${CLUSTER}/kapis/devops.kubesphere.io/v1alpha2/namespaces/${DEVOPS_PROJECT}/pipelines/${PIPELINE_NAME}/branches/${BRANCH}/runs/${JENKINS_ID}/artifacts/?start=0&limit=10" \ -H "Authorization: Bearer ${API_TOKEN}"
Example Response:
[ { "name": "service", "path": "service", "size": 8344228, "downloadable": true, "url": "/job/devopstestc2nj7/job/go-pipeline/job/main/2/artifact/service" } ]
Step 2: Download Artifact
Download a specific artifact by filename:
curl -s -o service "${KUBESPHERE_API}/kapis/clusters/${CLUSTER}/devops.kubesphere.io/v1alpha3/namespaces/${DEVOPS_PROJECT}/pipelineruns/${PIPELINE_RUN_NAME}/artifacts/download?filename=service" \ -H "Authorization: Bearer ${API_TOKEN}"
Key Points:
- Use the Kubernetes PipelineRun name (not Jenkins ID) for the download endpoint
- The filename must match exactly what's listed in the artifacts query
- Supports pagination with
andstart
parameters for listinglimit - Works with tenant credentials (no cluster-admin access needed)
Querying Jenkins Directly
For debugging, you can query Jenkins directly using the admin token:
# Get Jenkins admin token TOKEN=$(kubectl -n kubesphere-devops-system get secret devops-jenkins -o jsonpath='{.data.jenkins-admin-token}' | base64 -d) # List all folders (maps to DevOpsProjects) kubectl run curl-jenkins --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/api/json" \ -H "Content-Type: application/json" # List jobs in a specific folder kubectl run curl-jenkins --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/job/demo-project/api/json" \ -H "Content-Type: application/json"
Common Mistakes
| Mistake | Fix |
|---|---|
| PipelineRun not triggering | Check Pipeline exists and is synced to Jenkins |
| Controller panic (getAgentInfo nil pointer) | Known issue with PipelineRef; use Jenkins API directly as workaround |
| Agent label not found | Check available labels: |
| Go/Tool not found in agent | Use agent with appropriate container image |
| Artifact not found | Run in the same stage where the file was created (same workspace) |
| Permission denied | Check DevOps project membership and RBAC |
| Pipeline shows in KubeSphere but not Jenkins | Check sync status annotation: |
| Run fails immediately | Check controller logs: |
| Escape characters in Jenkinsfile | Don't use escape sequences in inline Jenkinsfile |
Debugging Steps
-
Check PipelineRun status:
kubectl get pipelinerun <name> -n <namespace> -o yaml -
Check Jenkins build directly:
# Get admin token TOKEN=$(kubectl -n kubesphere-devops-system get secret devops-jenkins -o jsonpath='{.data.jenkins-admin-token}' | base64 -d) # Get console output kubectl run curl-jenkins --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/job/<project>/job/<pipeline>/<build>/consoleText" -
List Jenkins agent labels:
kubectl run curl-jenkins --rm -i --restart=Never --image=curlimages/curl \ -- "http://admin:${TOKEN}@devops-jenkins.kubesphere-devops-system:80/labelsdashboard/labelsData"
| Mistake | Fix |
|---|---|
| PipelineRun not triggering | Check Pipeline exists and is synced to Jenkins |
| Agent not found | Verify Jenkins agent labels match pipeline's |
| Permission denied | Check DevOps project membership and RBAC |
| Pipeline shows in KubeSphere but not Jenkins | Check sync status annotation: |
| Run fails immediately | Check controller logs: |
Best Practices
- Use PipelineRun CRDs for triggering - it's the cloud-native approach
- Check status via kubectl rather than polling APIs
- Use
on the PipelineRun controller for debuggingkubectl logs - Verify sync status - pipelines must sync to Jenkins before they can run