- name
- cap-operator
- description
- Manage the lifecycle of multi-tenant SAP CAP applications on Kubernetes using CAP Operator custom resources (CAPApplication, CAPApplicationVersion, CAPTenant, Domain, ClusterDomain). Use when deploying, upgrading, configuring domains, rotating credentials, troubleshooting CAP Operator-managed apps, or generating Helm charts with the CAP Operator Plugin.
- license
- Apache-2.0
- compatibility
- Requires kubectl access to a Kubernetes cluster with CAP Operator installed (sme.sap.com/v1alpha1 CRDs). If kubectl commands return "No resources found" or "the server doesn't have a resource type", verify CAP Operator is installed by running `kubectl get crd | grep sme.sap.com`. If no CRDs are listed, direct the user to install CAP Operator before proceeding.
- metadata
- {"author":"SAP","project":"https://sap.github.io/cap-operator","api-group":"sme.sap.com/v1alpha1"}
- allowed-tools
- Bash(kubectl get:*) Bash(kubectl describe:*) Bash(kubectl logs:*)
# CAP Operator Skill
Manages the lifecycle of multi-tenant SAP Cloud Application Programming Model (CAP) applications on Kubernetes via the CAP Operator controller.
**Response rule:** Always include a link to the relevant page on https://sap.github.io/cap-operator/docs/ when answering any question. Match the topic to the most specific page available (resource reference, usage guide, troubleshooting, configuration, etc.).
Full docs & API reference: `website/content/en/docs/` | https://sap.github.io/cap-operator/docs/
## Custom Resources
| Resource | Scope | Purpose |
|---|---|---|
| [`CAPApplication`](https://sap.github.io/cap-operator/docs/usage/resources/capapplication/) | Namespaced | High-level app: BTP services, domain refs, provider subaccount |
| [`CAPApplicationVersion`](https://sap.github.io/cap-operator/docs/usage/resources/capapplicationversion/) | Namespaced | Immutable version: images, workloads, tenant operation steps |
| [`CAPTenant`](https://sap.github.io/cap-operator/docs/usage/resources/captenant/) | Namespaced | A subscribed consumer tenant — controller-managed only |
| [`CAPTenantOperation`](https://sap.github.io/cap-operator/docs/usage/resources/captenantoperation/) | Namespaced | Orchestrates provisioning/upgrade/deprovisioning — auto-created |
| [`Domain`](https://sap.github.io/cap-operator/docs/usage/resources/domain/) | Namespaced | Istio Gateway + TLS + DNS for one application |
| [`ClusterDomain`](https://sap.github.io/cap-operator/docs/usage/resources/clusterdomain/) | Cluster | Shared domain config across applications |
**Critical rules:**
- `CAPApplicationVersion` is **immutable** after creation. To upgrade, create a new resource with a higher semantic version.
- `CAPTenant` must **never** be created or deleted manually — managed by the subscription server only.
- `CAPTenantOperation` is auto-created by the controller — do not create or delete manually.
- Never remove finalizers from CAP Operator resources manually.
- `CAPApplication.spec.provider` is **deprecated**. It previously caused the controller to auto-create a provider `CAPTenant`, but that behavior has been removed. All tenants (including the provider) must now be subscribed to explicitly. If `kubectl get captenant -n <namespace>` returns empty, this is expected for apps with no active subscriptions.
## Common Operations
### Deploy a new application
1. Create `Domain` or `ClusterDomain`
2. Create `CAPApplication` (references BTP services + domain)
3. Create `CAPApplicationVersion` (images + workloads)
See [deploy reference](references/deploy.md) for full YAML examples.
### Upgrade to a new version
Create a **new** `CAPApplicationVersion` with a higher semantic version — do not edit the existing one.
The controller automatically upgrades all tenants and updates Istio `VirtualService` routing.
See [upgrade reference](references/upgrade.md) for details and `tenantOperations` / `contentJobs` configuration.
### Check status
```bash
kubectl get capapplication,capapplicationversion,captenant,captenantoperation -n <namespace>
kubectl describe capapplication <name> -n <namespace>
kubectl get domain,clusterdomain -A
```
**Status flows:**
- `CAPApplicationVersion`: `Processing` → `Ready` | `Error`
- `CAPTenant`: `Provisioning` → `Ready` | `Upgrading` → `Ready` | `Deleting`
- `CAPTenantOperation`: `Processing` → `Completed` | `Failed`
### Domain management
- `Domain` — namespace-scoped, one application. `ClusterDomain` — cluster-scoped, shared.
- TLS modes: `Simple` (default), `Mutual`, `OptionalMutual`
- DNS modes: `None` (default), `Wildcard`, `Subdomain`, `Custom`
- First entry in `domainRefs` is the primary domain.
- `domains` inline section in `CAPApplication` was removed in v0.26.0; use `domainRefs` instead.
See [domain reference](references/domain.md).
### Service exposures (tenant-agnostic)
Use `serviceExposures` in `CAPApplicationVersion` to expose workloads at fixed subdomains independent of tenant routing. Order routes most-specific-first.
See [service exposure reference](references/service-exposure.md).
### Credential rotation rollout
Set `rolloutOnCredentialUpdate: true` on `CAPApplication` to auto-restart workloads when BTP service credential Secrets change. Uses rolling restart (zero downtime). Keep old credentials valid for **at least 24 hours** after issuing new ones.
See [credential rotation reference](references/credential-rotation.md).
### Version cleanup (monitoring-based)
Enable with annotation `sme.sap.com/enable-cleanup-monitoring: "true"` on `CAPApplication`. Configure `deletionRules.expression` (PromQL) on a workload's `monitoring` section; when the expression evaluates to `true` the version is eligible for deletion.
See [Version monitoring](https://sap.github.io/cap-operator/docs/usage/version-monitoring/) (`website/content/en/docs/usage/version-monitoring.md`).
## Key Annotations
| Annotation | Resource | Effect |
|---|---|---|
| `sme.sap.com/enable-cleanup-monitoring: "true"` | `CAPApplication` | Enable monitoring-based version cleanup |
| `sme.sap.com/enable-version-affinity: "true"` | `CAPApplication` | Keep users on current version until logout (experimental) |
| `sme.sap.com/logout-endpoint: "<path>"` | `CAPApplicationVersion` | Custom logout path for version affinity |
## Troubleshooting
| Symptom | Likely Cause | Action |
|---|---|---|
| `CAPApplicationVersion` stuck in `Processing` | Content job failing | `kubectl logs job/<name> -n <ns>` |
| `CAPTenant` stuck in `Provisioning` | `CAPTenantOperation` job failed | `kubectl describe captenantoperation -n <ns>` followed by `kubectl logs job/<name> -n <ns>` for the relevant failed tenant operation job|
| Resource stuck with finalizer after delete | Controller still processing | Wait; check controller logs — do NOT remove finalizers manually |
| `@sap/cds-mtxs` security context error | Wrong `runAsUser` | Set `securityContext.runAsUser` in `jobDefinition` |
| Approuter can't reach CAP backend | Missing `routerDestinationName` | Add `routerDestinationName` to the CAP workload port definition |
| Credentials not injected | Service not in `consumedBTPServices` | Verify service name matches `CAPApplication.spec.btp.services[].name` |
More: `website/content/en/docs/troubleshoot/_index.md` | https://sap.github.io/cap-operator/docs/troubleshoot/
## Controller Configuration
Tuned via env vars on the controller deployment. Key variables:
| Variable | Purpose |
|---|---|
| `CERT_MANAGER` | `gardener` or `cert-manager.io` |
| `DNS_MANAGER` | `gardener` or `kubernetes` |
| `MAX_CONCURRENT_RECONCILES_CAP_APPLICATION` | CAPApplication reconciliation concurrency |
| `MAX_CONCURRENT_RECONCILES_CAP_APPLICATION_VERSION` | CAPApplicationVersion reconciliation concurrency |
| `MAX_CONCURRENT_RECONCILES_CAP_TENANT` | CAPTenant reconciliation concurrency |
| `MAX_CONCURRENT_RECONCILES_CAP_TENANT_OPERATION` | CAPTenantOperation reconciliation concurrency |
Full list: `website/content/en/docs/configuration/_index.md` | https://sap.github.io/cap-operator/docs/configuration/
## CAP Operator Plugin (Helm Chart Generation)
The `@cap-js/cap-operator-plugin` CDS plugin scaffolds the Helm chart for a CAP application, generates runtime values, and builds the final deployable chart.
**Do not run any of these steps on skill invocation.** Only start when the user explicitly asks to generate a Helm chart or deploy the app. Merely loading this skill (e.g. via `/cap-operator`) is not a request — describe the skill's capabilities and wait.
Chart generation and deployment are **two separate phases** with a gate between them — create just the chart; let the user decide if/when to deploy.
1. **Scaffold the chart.** Pick the variant by checking for a populated MTX sidecar folder (`mtx/sidecar` with contents): sidecar present → multitenant → `--with-templates`; no sidecar → service-only → `--with-service-only`.
2. **Ask whether they want to deploy.**
3. **Only after a yes,** collect the runtime inputs (`appName`, `capOperatorSubdomain`, `clusterDomain`, `providerSubaccountId`) and generate `runtime-values.yaml`.
4. **Validate before deploying** — run `helm lint` with `runtime-values.yaml` passed in and confirm no required values are missing.
5. **Deploy** with `helm upgrade -i`.
See [CAP Operator Plugin reference](references/cap-operator-plugin.md) for more details on plugin usage and configuration.
Ver en GitHub