Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish-website.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: "0.165.0"
hugo-version: "0.167.0"
extended: true
- name: Setup Node
uses: actions/setup-node@v7
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ require (
golang.org/x/sys v0.49.0 // indirect
golang.org/x/term v0.47.0 // indirect
golang.org/x/text v0.43.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20261012220534-4341edf3f941 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/apiextensions-apiserver v0.37.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI=
golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA=
google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8 h1:cFbkNigDInw+wVTXc+CyhUsoHGJCw0BR6mlNoRVfre0=
google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8/go.mod h1:QK6p7IgD7IZEZJBhhSUCib5X2vMOzlrGCGUw2XyVDh0=
google.golang.org/genproto/googleapis/api v0.0.0-20261012220534-4341edf3f941 h1:DS/w3N8rsfrwxwgGSj6WhMsPo7aNQGmDx74J81zd0NQ=
google.golang.org/genproto/googleapis/api v0.0.0-20261012220534-4341edf3f941/go.mod h1:AuGAdi9KnXss7xbLxIKhCvk+Ro/SxpET/iS45DNYN+M=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ The Subscription Server handles HTTP requests from the [SAP Software-as-a-Servic

During the creation of a `saas-registry` service instance (in the provider subaccount), [callback URLs are configured](../../../usage/prerequisites/#sap-software-as-a-service-provisioning-service) to point to the subscription server routes. Additionally, the `getDependencies` URLs can also be configured to point to the subscription server routes.

When a consumer tenant subscribes to an application managed by the operator, the subscription server receives the callback, validates the request, and creates a `CAPTenant` custom resource object for the identified `CAPApplication`.
When a consumer tenant subscribes to an application managed by the operator, the subscription server receives the callback, validates the request using credentials from the corresponding [`SubscriptionProvider`](../../../usage/resources/subscriptionprovider) resource, and creates a [`Subscription`](../../../usage/resources/subscription) resource in the namespace of the identified `CAPApplication`. The controller then creates the `CAPTenant` and drives it through the provisioning flow.

The subscription server returns an `Accepted` (202) response and starts a background routine that polls for tenant status while the `CAPTenant` changes are independently reconciled by the controller.
The subscription server returns an `Accepted` (202) response and starts a background routine that polls `Subscription.status` while the `Subscription` and `CAPTenant` resources are independently reconciled by the controller.

Once the tenant provisioning process completes (or fails), the tracking routine returns the appropriate status to the SaaS Registry via an asynchronous callback.

Expand Down
64 changes: 64 additions & 0 deletions website/content/en/docs/usage/resources/subscription.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: "Subscription"
linkTitle: "Subscription"
weight: 26
type: "docs"
description: >
Represents an active consumer subscription to a CAP application
---

{{% alert color="warning" title="Warning" %}}
`Subscription` resources are fully managed by CAP Operator. They are created and deleted exclusively by the subscription server in response to callbacks from the SAP Software-as-a-Service Provisioning service or SMS. Do not create, modify, or delete them manually.
{{% /alert %}}

A `Subscription` represents an active subscription of a consumer subaccount to a CAP application. When a consumer subscribes via the SAP BTP cockpit or the SaaS Provisioning service API, the [subscription server](../../../concepts/operator-components/subscription-server) creates a `Subscription` resource in the namespace of the corresponding `CAPApplication`. The controller then creates a [`CAPTenant`](../captenant) as a child of the `Subscription` and drives it through the provisioning flow.

```yaml
apiVersion: sme.sap.com/v1alpha2
kind: Subscription
metadata:
name: cap-app-01-consumer-sgz8b
namespace: cap-app-01
spec:
appName: cap-app-01
providerSubaccountId: aa2bae55d7b5-1279-456564-a7b0-aa2bae55d7b5
tenantId: bb3cbf66e8c6-2380-567675-b8c1-bb3cbf66e8c6
subdomain: consumer-subdomain
subscriptionGuid: cc4dcg77f9d7-3491-678786-c9d2-cc4dcg77f9d7
subscriptionDomain: app.example.com
subscriptionRequestPayload: '{"subscriptionParams":{}}'
status:
state: Ready
url: https://consumer-subdomain.app.example.com
```

## Spec

| Field | Description |
|---|---|
| `appName` | Name of the CAP application being subscribed to. Used together with `providerSubaccountId` to identify the owning `CAPApplication`. |
| `providerSubaccountId` | Provider subaccount ID. Used together with `appName` to identify the owning `CAPApplication`. |
| `tenantId` | Tenant ID of the consumer subaccount. |
| `subdomain` | Subdomain of the consumer subaccount. |
| `subscriptionGuid` | Unique GUID of the subscription, as assigned by the SaaS Provisioning service. |
| `subscriptionDomain` | Domain used to construct the tenant-specific subscription URL. If not provided, falls back to the annotation `sme.sap.com/subscription-domain` on the `CAPApplication`, or the first `DomainRef` in the `CAPApplication` spec. |
| `subscriptionRequestPayload` | Full JSON payload of the subscription request. Injected into tenant operation jobs as the `CAPOP_SUBSCRIPTION_PAYLOAD` environment variable. |

## Status

The subscription server polls `Subscription.status` after creating the resource and sends an asynchronous callback to the SaaS Provisioning service once the subscription reaches a terminal state (`Ready` or `Error`).

| Field | Description |
|---|---|
| `state` | Current state of the subscription. See [States](#states) below. |
| `url` | Tenant-specific application URL, populated by the controller once the owned `CAPTenant` is `Ready`. Returned to the SaaS Provisioning service as the subscription URL. |

### States

| State | Description |
|---|---|
| `Processing` | The controller is creating the `CAPTenant` and waiting for provisioning to complete. |
| `ProcessingError` | A retryable error occurred during processing; the controller will retry. |
| `Error` | A terminal error occurred (for example, the owned `CAPTenant` reached `ProvisioningError`). The SaaS Provisioning service is notified of the failure via the async callback. |
| `Deleting` | Deletion has been triggered; the controller is waiting for the owned `CAPTenant` to complete deprovisioning. |
| `Ready` | Provisioning is complete. The `url` field contains the tenant-specific application URL. |
46 changes: 46 additions & 0 deletions website/content/en/docs/usage/resources/subscriptionprovider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "SubscriptionProvider"
linkTitle: "SubscriptionProvider"
weight: 25
type: "docs"
description: >
Caches subscription credentials and dependency information for a CAP application
---

{{% alert color="warning" title="Warning" %}}
`SubscriptionProvider` resources are fully managed by CAP Operator. They are automatically created and kept in sync by the controller whenever a `CAPApplication` is reconciled. Do not create, modify, or delete them manually.
{{% /alert %}}

A `SubscriptionProvider` is created by the controller in the same namespace as its owning `CAPApplication`, with the same name. It stores pre-computed subscription credentials and dependency information derived from the `CAPApplication` spec. The [subscription server](../../../concepts/operator-components/subscription-server) uses it to:

- Authorize incoming subscription requests (using the referenced BTP service secrets)
- Return the pre-computed dependency list from the `getDependencies` endpoint of the SaaS Provisioning service

Because all subscription-related lookups happen at `CAPApplication` reconciliation time rather than at HTTP request time, the subscription server can serve requests efficiently without directly parsing the `CAPApplication` spec.

```yaml
apiVersion: sme.sap.com/v1alpha2
kind: SubscriptionProvider
metadata:
name: cap-app-01
namespace: cap-app-01
spec:
appName: cap-app-01
providerSubaccountId: aa2bae55d7b5-1279-456564-a7b0-aa2bae55d7b5
subscriptionInfo:
type: saas-registry
subscriptionSecret: my-saas-registry-secret
authSecret: my-xsuaa-secret
dependencies: '[{"xsappname":"my-destination-service!b42"},{"xsappname":"my-auditlog-service!b7"}]'
```

## Spec

| Field | Description |
|---|---|
| `appName` | Name of the CAP application. Derived from `CAPApplication.spec.btpAppName`. |
| `providerSubaccountId` | Provider subaccount ID. Derived from `CAPApplication.spec.providerSubaccountId`. |
| `subscriptionInfo.type` | Type of subscription service: `saas-registry` or `subscription-manager`, depending on which BTP service is configured in the `CAPApplication`. |
| `subscriptionInfo.subscriptionSecret` | Name of the Kubernetes Secret containing the BTP subscription service credentials (used for request authorization). |
| `subscriptionInfo.authSecret` | (Optional) Name of the Kubernetes Secret containing the XSUAA credentials. Only set for `saas-registry` type. |
| `dependencies` | Pre-computed JSON array of subscription dependencies (for example, `[{"xsappname":"..."}]`). Computed from BTP service secrets at `CAPApplication` reconciliation time and returned verbatim by the subscription server's `getDependencies` endpoint. See [Service Qualification](../../tenant-provisioning#service-qualification) for how dependencies are determined. |
14 changes: 7 additions & 7 deletions website/content/en/docs/usage/tenant-provisioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ spec:

### Tenant Provisioning

Tenant provisioning begins when a consumer subaccount subscribes to the application, either via the SAP BTP cockpit or using the SaaS Provisioning service APIs. This triggers an asynchronous callback from the SaaS Provisioning service into the cluster, which is handled by the [subscription server](../../concepts/operator-components/subscription-server). The subscription server validates the request and creates a `CAPTenant` instance for the identified `CAPApplication`.
Tenant provisioning begins when a consumer subaccount subscribes to the application, either via the SAP BTP cockpit or using the SaaS Provisioning service APIs. This triggers an asynchronous callback from the SaaS Provisioning service into the cluster, which is handled by the [subscription server](../../concepts/operator-components/subscription-server). The subscription server validates the request using credentials from the [`SubscriptionProvider`](../resources/subscriptionprovider) resource and creates a [`Subscription`](../resources/subscription) resource in the namespace of the identified `CAPApplication`. The controller, observing the new `Subscription`, then creates a `CAPTenant` instance to represent the consumer tenant.

{{% alert color="warning" title="Warning" %}}
`CAPTenant` instances must not be created or deleted manually. They are managed exclusively by the subscription server in response to provisioning calls from the SaaS Provisioning service.
`Subscription` and `CAPTenant` resources must not be created or deleted manually. `Subscription` resources are managed by the subscription server in response to provisioning callbacks, while `CAPTenant` resources are created and managed by the controller as children of `Subscription` resources.
{{% /alert %}}

The controller, observing the new `CAPTenant`, initiates the provisioning process by creating a `CAPTenantOperation` resource representing the provisioning operation.
The controller, observing the new `CAPTenant` (created as a child of the `Subscription`), initiates the provisioning process by creating a `CAPTenantOperation` resource representing the provisioning operation.

```yaml
apiVersion: sme.sap.com/v1alpha2
Expand Down Expand Up @@ -60,7 +60,7 @@ The `CAPTenant` reaches a `Ready` state only after:

### Get Dependencies

During provisioning, the SaaS Provisioning service calls a `getDependencies` endpoint to retrieve the list of reuse services that the multitenant application requires. The CAP Operator subscription server exposes this endpoint and resolves dependencies by inspecting the BTP services defined in the `CAPApplication`.
During provisioning, the SaaS Provisioning service calls a `getDependencies` endpoint to retrieve the list of reuse services that the multitenant application requires. The CAP Operator subscription server exposes this endpoint. The dependency list is pre-computed by the controller at `CAPApplication` reconciliation time and cached in the [`SubscriptionProvider`](../resources/subscriptionprovider) resource. The subscription server reads and returns the cached list directly, without re-inspecting `CAPApplication` service credentials at request time.

The subscription server exposes the following endpoint:

Expand All @@ -85,7 +85,7 @@ A successful response returns a JSON array of objects. Each object contains the

#### Service Qualification

The subscription server iterates over all BTP services listed in `CAPApplication.spec.btp.services`. For each service, it reads the associated Kubernetes secret, parses the credentials, and determines whether to include the service in the response.
The controller iterates over all BTP services listed in `CAPApplication.spec.btp.services`. For each service, it reads the associated Kubernetes secret, parses the credentials, and determines whether to include the service in the pre-computed dependency list.

The `xsappname` is read from either the top-level `xsappname` field or the nested `uaa.xsappname` field in the service credentials. If not found, the service is excluded from the response.

Expand Down Expand Up @@ -130,9 +130,9 @@ This behavior aligns with the implementation in the [`@sap/approuter`](https://w

### Tenant Deprovisioning

When a tenant unsubscribes from the application, the subscription server receives the request, validates the existence and status of the `CAPTenant`, and submits a deletion request to the Kubernetes API server.
When a tenant unsubscribes from the application, the subscription server receives the request, locates the corresponding `Subscription` resource, and submits a deletion request to the Kubernetes API server.

The controller identifies the pending deletion but defers it until a `CAPTenantOperation` of type `deprovisioning` is created and completes successfully. The `CAPTenantOperation` creates the corresponding Jobs (steps) that execute the tenant deprovisioning.
The controller processes the deletion by initiating deprovisioning of the owned `CAPTenant` via a `CAPTenantOperation` of type `deprovisioning`. The `CAPTenantOperation` creates the corresponding Jobs (steps) that execute the tenant deprovisioning. Once deprovisioning completes successfully, the `Subscription` finalizer is removed, allowing Kubernetes to complete the deletion.

### CAPApplication Deletion

Expand Down
18 changes: 15 additions & 3 deletions website/content/en/docs/whats-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ description: >

### Recent updates

{{% cardpane %}}
{{% card header="v1alpha2 API Version" %}}
CAP Operator now uses `v1alpha2` as the default API version (from release `0.37.0`). The new version removes the deprecated `globalAccountId` and `domains` fields from `CAPApplication`. See the [CAPApplication resource](../usage/resources/capapplication) reference for the updated spec.
{{% /card %}}
{{% card header="Subscription Resource" %}}
The new `Subscription` resource represents an active consumer subscription to a CAP application. It is created and managed by the subscription server in response to SaaS Provisioning service callbacks. Learn more about the [Subscription resource](../usage/resources/subscription).
{{% /card %}}
{{% card header="SubscriptionProvider Resource" %}}
The new `SubscriptionProvider` resource is automatically maintained by the controller to cache subscription credentials and pre-computed dependency information for efficient handling of subscription requests. Learn more about the [SubscriptionProvider resource](../usage/resources/subscriptionprovider).
{{% /card %}}
{{% /cardpane %}}


### Previous updates

{{% cardpane %}}
{{% card header="Rollout on Credential Update" %}}
CAP Operator now triggers a rollout of relevant workloads when credentials (service bindings) are updated. Learn more about [Rollout on Credential Update](../usage/rollout-on-credential-update).
Expand All @@ -20,9 +35,6 @@ description: >
{{% /card %}}
{{% /cardpane %}}


### Previous updates

{{% cardpane %}}
{{% card header="Service Exposure" %}}
CAP Operator now allows any deployment workload to be exposed as a service. Learn more about [Service Exposure](../usage/service-exposure).
Expand Down
8 changes: 4 additions & 4 deletions website/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion website/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "website",
"devDependencies": {
"@docsy/theme": ">=0.17.0"
"@docsy/theme": ">=0.18.0"
}
}
Loading