Use when operating KubeSphere DevOps as a namespace-scoped tenant with limited permissions, without cluster-admin access, or when accessing DevOps through KubeSphere APIs only
Install
The skill file stays in the publisher's repository.
Paste the copied command in a terminal. It installs this SKILL.md into that agent.
npx skills add kubesphere/kubesphere --skill kubesphere-devops-tenant -a cursor -y
Download skills/kubesphere-devops-tenant/SKILL.md from the publisher, or copy the path and open the repository. PlugMCP lists the skill and does not host the file.
Where does kubesphere-devops-tenant live?
The publisher keeps it at https://github.com/kubesphere/kubesphere.
Which agents can use kubesphere-devops-tenant?
Claude, Cursor, and Codex can read a SKILL.md you add from the publisher's repository.
Use when operating KubeSphere DevOps as a namespace-scoped tenant with limited permissions, without cluster-admin access, or when accessing DevOps through KubeSphere APIs only
name: kubesphere-devops-tenant
description: Use when operating KubeSphere DevOps as a namespace-scoped tenant with limited permissions, without cluster-admin access, or when accessing DevOps through KubeSphere APIs only
KubeSphere DevOps Tenant Operations
Overview
This guide covers DevOps operations for namespace-scoped tenants who:
Have admin/operator permissions within their DevOpsProject namespace(s)
Started by user stone-ns-admin
Starting branch indexing...
> git ls-remote --symref -- https://github.com/org/repo.git
Fetching & pruning origin...
Checking branches:
Checking branch main ✓
'Jenkinsfile' found
Met criteria
Checking branch feature-branch
'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
Alternative: Via kubectl with exec (if artifact is in workspace):
# Find the agent pod (if still running)
AGENT_POD=$(kubectl get pods -n kubesphere-devops-worker -l jenkins/label-digest -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
# Copy artifact (if pod exists)
if [ -n "$AGENT_POD" ]; then
kubectl cp ${AGENT_POD}:/home/jenkins/agent/workspace/${PIPELINE_NAME}/${ARTIFACT_NAME} /tmp/${ARTIFACT_NAME}
fi
KubeSphere supports managing DevOps resources across multiple clusters. Use the /clusters/{cluster-name}/ prefix to forward API requests to specific member clusters.
DevOps Projects are Namespaces
Important: In KubeSphere DevOps, a "DevOps project" is actually a Kubernetes namespace with the devops.kubesphere.io/managed=true label. The DevOpsProject CR is a wrapper resource, but when listing accessible DevOps projects for a tenant, you query namespaces, not DevOpsProject CRs.
Correct API for listing tenant-accessible DevOps projects:
# List DevOps project namespaces (NOT devopsprojects CRs)
GET /clusters/{cluster}/kapis/devops.kubesphere.io/v1alpha3/workspaces/{workspace}/namespaces
This endpoint returns namespaces that:
Have the devops.kubesphere.io/managed=true label
Have the kubesphere.io/workspace={workspace} label
Are accessible to the authenticated tenant
DevOps Project Naming Convention
When users refer to DevOps projects, they may use either a shortname or fullname:
Name Type
Example
Source
Description
Shortname
devopstest
DevOpsProject CR .metadata.generateName
User-friendly display name
Fullname
devopstestc2nj7
DevOpsProject CR .metadata.name
Actual namespace name
Key Points:
The fullname is the actual Kubernetes namespace name that you use in API calls
The fullname = DevOpsProject CR's .metadata.name = Namespace's .metadata.name
The shortname comes from .metadata.generateName and is used for display purposes
Resolving Ambiguity:
When a user provides a name that could match multiple projects:
# First, get all accessible DevOps project namespaces
NAMESPACES=$(curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/workspaces/stone2/namespaces" \
-H "Authorization: Bearer ${API_TOKEN}")
# Example: User says "devopstest" which could match:
# - Fullname: devopstestc2nj7 (from generateName "devopstest")
# - Fullname: devopstestxyz12 (from generateName "devopstest")
# - Fullname: my-devopstest (different project)
# Check for matches
echo "$NAMESPACES" | jq -r '.items[].metadata.name' | grep "devopstest"
# Output might show:
# devopstestc2nj7
# devopstestxyz12
# If multiple matches found, ask user to confirm:
# "Multiple DevOps projects match 'devopstest':
# 1. devopstestc2nj7
# 2. devopstestxyz12
# Which one do you want to use?"
Best Practice:
When user provides a name, check if it matches any fullname (namespace name) exactly
If exact match found → use that namespace
If no exact match → check if it matches any shortname (generateName prefix)
If multiple matches → ask user to confirm before proceeding
Key Insight: The /kapis/ endpoints enforce workspace-level RBAC and work for tenants. The /apis/ endpoints require cluster-scoped permissions and will return 403 for tenants. When listing DevOps projects a tenant can access, use the /namespaces endpoint, not /devopsprojects.
=== Host Cluster ===
(No output - no DevOpsProjects in workspace 'stone' on host)
=== Member-1 Cluster ===
stonedev154cht (2026-03-18T06:46:04Z)
Why Tenant Can't Use /apis/ Endpoints
# ❌ This will return 403 Forbidden for tenants
curl -s "${KUBESPHERE_API}/clusters/member-1/apis/devops.kubesphere.io/v1alpha3/devopsprojects" \
-H "Authorization: Bearer ${TOKEN}"
# Output:
# {
# "kind": "Status",
# "apiVersion": "v1",
# "status": "Failure",
# "message": "devopsprojects.devops.kubesphere.io is forbidden: User \"stone-ns-admin\" cannot list resource \"devopsprojects\" in API group \"devops.kubesphere.io\" at the cluster scope",
# "reason": "Forbidden",
# "code": 403
# }
# ✅ This works because /kapis/ with workspace scope enforces tenant RBAC
curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/devops.kubesphere.io/v1alpha3/workspaces/stone/namespaces" \
-H "Authorization: Bearer ${TOKEN}"
# Output:
# {"items":[{"kind":"DevOpsProject","apiVersion":"devops.kubesphere.io/v1alpha3",...}]}
Complete Multi-Cluster Query Script
#!/bin/bash
export KUBESPHERE_API="http://kubesphere-apiserver:80"
export USERNAME="stone-ns-admin"
export PASSWORD="P@88w0rd"
export WORKSPACE="stone"
# Get token
TOKEN=$(curl -s -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
"${KUBESPHERE_API}/oauth/token" \
--data-urlencode 'grant_type=password' \
--data-urlencode "username=${USERNAME}" \
--data-urlencode "password=${PASSWORD}" \
--data-urlencode 'client_id=kubesphere' \
--data-urlencode 'client_secret=kubesphere' | jq -r '.access_token')
# Get list of clusters (requires admin token or cluster list permission)
# For tenants, typically hardcode the clusters they have access to
CLUSTERS=("host" "member-1")
echo "=== DevOpsProjects in Workspace '${WORKSPACE}' Across All Clusters ==="
for CLUSTER in "${CLUSTERS[@]}"; do
echo -e "\n## Cluster: ${CLUSTER}"
# Use /kapis/ endpoint with workspace scope
ENDPOINT="${KUBESPHERE_API}"
if [ "${CLUSTER}" != "host" ]; then
ENDPOINT="${ENDPOINT}/clusters/${CLUSTER}"
fi
PROJECTS=$(curl -s "${ENDPOINT}/kapis/devops.kubesphere.io/v1alpha3/workspaces/${WORKSPACE}/namespaces" \
-H "Authorization: Bearer ${TOKEN}")
# Check if response contains items
COUNT=$(echo "$PROJECTS" | jq '.items | length')
if [ "$COUNT" -gt 0 ]; then
echo "$PROJECTS" | jq -r '.items[] | " - \(.metadata.name) (Created: \(.metadata.creationTimestamp), Status: \(.metadata.annotations."devopsproject.devops.kubesphere.io/syncstatus" // "N/A"))"'
else
echo " No DevOpsProjects found"
fi
done
Workspace-Scoped API Operations
Query DevOps Projects (Namespaces) by Workspace
Tenants can query DevOps projects (which are namespaces) within their authorized workspaces:
# List DevOps project namespaces in specific workspace
# Note: Returns namespaces with devops.kubesphere.io/managed=true label
# Returns empty if tenant doesn't have workspace access
curl -s "${KUBESPHERE_API}/clusters/host/kapis/devops.kubesphere.io/v1alpha3/workspaces/stone/namespaces?sortBy=createTime&limit=10" \
-H "Authorization: Bearer ${API_TOKEN}" | jq -r '.items[].metadata.name'
# Example output:
# stone-devops
# Query different workspace (returns 0 items if no access)
curl -s "${KUBESPHERE_API}/clusters/host/kapis/devops.kubesphere.io/v1alpha3/workspaces/demo/namespaces?sortBy=createTime&limit=10" \
-H "Authorization: Bearer ${API_TOKEN}" | jq '.totalItems'
# Example output:
# 0
Important Distinction:
DevOps Project = A namespace with devops.kubesphere.io/managed=true label
DevOpsProject CR = A Kubernetes custom resource that wraps the namespace
To list projects a tenant can access → Use /namespaces endpoint
The /namespaces endpoint filters by namespace label kubesphere.io/workspace. If the namespace label doesn't match the workspace, it won't be returned even if the DevOpsProject CR has the correct label.
When spec.argoApp.spec.destination.server is https://kubernetes.default.svc and destination.name is empty or in-cluster, the Application deploys to the cluster specified in the API path:
Since tenants may not have permissions to directly query the destination namespace (due to RBAC), always verify deployment via the Application status rather than trying to access deployed resources directly:
# ✅ CORRECT: Check Application status (tenant has permissions)
curl -s "${KUBESPHERE_API}/clusters/member-1/kapis/gitops.kubesphere.io/v1alpha1/namespaces/demo-project/applications/guestbook" \
-H "Authorization: Bearer ${API_TOKEN}" | jq -r '.metadata.labels["gitops.kubesphere.io/sync-status"]'
# Output: "Synced"
# ❌ INCORRECT: Direct namespace access may fail for tenants
curl -s "${KUBESPHERE_API}/clusters/member-1/api/v1/namespaces/demo-project/pods" \
-H "Authorization: Bearer ${API_TOKEN}"
# May return 403 Forbidden
Tenant Limitations (Important)
Action
Tenant Can
Notes
Create GitOps App
✅ Yes
Via KubeSphere API
Modify ArgoCD Config
❌ No
Cannot access argocd namespace
Add App Namespace to ArgoCD
❌ No
Requires admin to update application.namespaces
View ArgoCD UI
❌ No
No direct ArgoCD access
View Deployed Resources
✅ Yes
In own namespace
Complete Tenant Workflow
Step-by-Step: Build and Retrieve Artifacts as Tenant
# Cause: Wrong namespace or resource doesn't exist
# Fix: Verify namespace and resource names
kubectl get pipelines -n ${DEVOPS_PROJECT}
kubectl auth can-i get pipelines -n ${DEVOPS_PROJECT}
Error: No logs available
# Cause: Run not complete or logs not persisted
# Fix: Wait for completion, check if run succeeded
kubectl get pipelinerun ${RUN_ID} -n ${DEVOPS_PROJECT} -o jsonpath='{.status.phase}'
Workspace API Returns Empty (Namespace Label Mismatch)
Symptom:
# Query workspace API returns 0 items even though DevOpsProject exists
curl -s "${KUBESPHERE_API}/clusters/host/kapis/devops.kubesphere.io/v1alpha3/workspaces/demo/namespaces" \
-H "Authorization: Bearer ${API_TOKEN}"
# Output: {"items": null, "totalItems": 0}
# But direct query works
curl -s "${KUBESPHERE_API}/kapis/devops.kubesphere.io/v1alpha3/namespaces/demo-project/pipelines" \
-H "Authorization: Bearer ${API_TOKEN}"
# Output: Returns pipelines successfully
Root Cause:
The workspace-scoped API filters namespaces by the label kubesphere.io/workspace. If the namespace label doesn't match the workspace, it won't be returned, even if the DevOpsProject CR has the correct label.
Check Labels:
# Check DevOpsProject label (usually correct)
kubectl get devopsproject demo-project -o jsonpath='{.metadata.labels.kubesphere\.io/workspace}'
# Output: demo
# Check namespace label (may be empty or wrong)
kubectl get ns demo-project -o jsonpath='{.metadata.labels.kubesphere\.io/workspace}'
# Output: "" (EMPTY - this is the problem!)
Fix (Admin Required):
# Update namespace label to match workspace
kubectl label ns demo-project kubesphere.io/workspace=demo --overwrite
# Verify fix
kubectl get ns demo-project -o jsonpath='{.metadata.labels.kubesphere\.io/workspace}'
# Output: demo
# Now workspace API returns the namespace
curl -s "${KUBESPHERE_API}/clusters/host/kapis/devops.kubesphere.io/v1alpha3/workspaces/demo/namespaces" \
-H "Authorization: Bearer ${API_TOKEN}" | jq '.totalItems'
# Output: 1
Why This Happens:
DevOpsProject CR and namespace are separate resources
DevOpsProject controller should sync the workspace label to the namespace
If the controller missed it or the label was removed, the API filtering breaks
Workspace-scoped APIs use namespace labels, not DevOpsProject labels
Download skills/kubesphere-devops-tenant/SKILL.md from the publisher, or copy the path and open the repository. PlugMCP lists the skill and does not host the file.
Where does kubesphere-devops-tenant live?
The publisher keeps it at https://github.com/kubesphere/kubesphere.
Which agents can use kubesphere-devops-tenant?
Claude, Cursor, and Codex can read a SKILL.md you add from the publisher's repository.