diff --git a/how-to-guides/bootstrap-provider.md b/how-to-guides/bootstrap-provider.md index 0a68c6e8b..08b76fc3f 100644 --- a/how-to-guides/bootstrap-provider.md +++ b/how-to-guides/bootstrap-provider.md @@ -71,7 +71,121 @@ export PROVIDER_KUBECONFIG=provider-kubeconfig.yaml kubectl --kubeconfig $PROVIDER_KUBECONFIG apply -f ``` -## Step 5: Wire the kubeconfig into your service controllers +## Step 5: Register your provider in the Marketplace + +For your provider to appear in the Platform Mesh Marketplace and project its navigation into consumer workspaces, apply three resources to the provider workspace. The `ui.platform-mesh.io/content-for` label is the join key, but its **value differs by resource**: + +| Resource | `content-for` value | Purpose | +| --- | --- | --- | +| `ProviderMetadata` | `` — for example `my-service` | Marketplace card — name, description, icon, contacts, documentation, support links. | +| `APIExport` | `` — same as `ProviderMetadata.name` | The Marketplace filter joins `APIExport` to `ProviderMetadata` by this value. The Marketplace skips exports whose `status.identityHash` is empty (not yet established by kcp). UI-only providers that expose no CRD can omit `spec.latestResourceSchemas`. | +| `ContentConfiguration` | `` — for example `my-service.example.com` | The portal's nav projection reads ContentConfigurations by `content-for: ` from the provider workspace once the APIBinding is installed. | + +::: tip Two different values for the same label key +`ProviderMetadata` and `APIExport` share `content-for: ` so the Marketplace can join them. +`ContentConfiguration` uses `content-for: ` (the full API group name) so the portal can project nav nodes into the consumer workspace after install. +::: + +A minimal example for a UI-only provider: + +```yaml +# providermetadata.yaml +apiVersion: ui.platform-mesh.io/v1alpha1 +kind: ProviderMetadata +metadata: + name: my-service + labels: + ui.platform-mesh.io/content-for: my-service +spec: + displayName: My Service + description: Short description shown in the Marketplace card. + tags: [example] + contacts: + - displayName: My Team + email: my-team@example.com + role: [Owner] + documentation: + - displayName: Documentation + url: https://docs.example.com + icon: + light: + url: https://example.com/icon-light.svg + dark: + url: https://example.com/icon-dark.svg +``` + +```yaml +# apiexport.yaml (UI-only: no latestResourceSchemas needed) +apiVersion: apis.kcp.io/v1alpha1 +kind: APIExport +metadata: + name: my-service.example.com + labels: + ui.platform-mesh.io/content-for: my-service +spec: {} +``` + +```yaml +# contentconfiguration.yaml +apiVersion: ui.platform-mesh.io/v1alpha1 +kind: ContentConfiguration +metadata: + name: my-service-ui + labels: + ui.platform-mesh.io/content-for: my-service.example.com + ui.platform-mesh.io/entity: core_platform-mesh_io_account +spec: + remoteConfiguration: + url: https://example.com/portal-config.json + contentType: json +``` + +Apply them using the provider kubeconfig: + +```bash +export PROVIDER_KUBECONFIG=provider-kubeconfig.yaml +kubectl --kubeconfig $PROVIDER_KUBECONFIG apply -f apiexport.yaml +kubectl --kubeconfig $PROVIDER_KUBECONFIG apply -f providermetadata.yaml +kubectl --kubeconfig $PROVIDER_KUBECONFIG apply -f contentconfiguration.yaml +``` + +## Step 6: Grant bind permission + +The Marketplace install flow creates an `APIBinding` in the consumer workspace by calling `bind` on the provider's `APIExport`. Without an explicit grant, the call fails with "no permission to bind to export". Apply a `ClusterRole` and `ClusterRoleBinding` to the provider workspace to allow it: + +```yaml +# rbac-bind.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: my-service-bind +rules: + - apiGroups: ["apis.kcp.io"] + resources: ["apiexports"] + resourceNames: ["my-service.example.com"] + verbs: ["bind"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: my-service-bind +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: my-service-bind +subjects: + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: system:authenticated +``` + +```bash +kubectl --kubeconfig $PROVIDER_KUBECONFIG apply -f rbac-bind.yaml +``` + +After both steps, open the Marketplace in a consumer workspace — the provider card appears and the **Enable** button works. + +## Step 7: Wire the kubeconfig into your service controllers Configure your service controllers to use the provider kubeconfig to watch the `APIExport` virtual workspace and reconcile service consumers. See [Integration paths](/concepts/integration-paths.md) to choose the right mechanism and find the corresponding tutorial. @@ -81,3 +195,5 @@ Configure your service controllers to use the provider kubeconfig to watch the ` - [Provider bootstrap](/concepts/provider-bootstrap.md) - [Integration paths](/concepts/integration-paths.md) - [Service provider persona](/concepts/personas/service-provider.md) +- [Metadata catalog](/reference/resources/metadata-catalog.md) — `ui.platform-mesh.io/content-for` label reference +- [ContentConfiguration](/reference/resources/content-configuration.md) diff --git a/reference/resources/metadata-catalog.md b/reference/resources/metadata-catalog.md index ffb67cfc9..f90197387 100644 --- a/reference/resources/metadata-catalog.md +++ b/reference/resources/metadata-catalog.md @@ -28,11 +28,10 @@ Platform Mesh attaches labels to its own resources and, in some cases, to upstre | Label | Used on | Purpose | | --- | --- | --- | | `core.platform-mesh.io/org` | WorkspaceTypes managed by the account-operator | Scopes a workspace type to a specific organization so child accounts inherit the right RBAC. | +| `ui.platform-mesh.io/content-for` | `ProviderMetadata`, `APIExport`, `ContentConfiguration` | **Required by provider authors.** Links the three resources that make up a Marketplace entry and a portal nav projection, but the value differs by resource: `ProviderMetadata` and `APIExport` use the `ProviderMetadata` name (for example `my-service`) so the Marketplace can join them; `ContentConfiguration` uses the `APIExport` name (for example `my-service.example.com`) so the portal can project nav nodes into a consumer workspace after install. See [Register your provider in the Marketplace](/how-to-guides/bootstrap-provider.md#register-your-provider-in-the-marketplace). | | `ui.platform-mesh.io/entity` | ContentConfiguration | Attaches the configuration to a portal navigation entity (for example, `core_platform-mesh_io_account` to extend Account pages). | | `extensions.openmfp.io` | ContentConfiguration (legacy) | Maps to an `ExtensionClass` in older deployments. New deployments prefer `ui.platform-mesh.io/entity`. | -Provider authors generally do **not** need to set Platform Mesh labels on APIExports or APIBindings — onboarding scripts and operators handle that. - ## Finalizers Finalizers ensure that Platform Mesh resources are torn down in the right order — for example, an Account's IAM Store must be cleaned up before the workspace itself is deleted, otherwise stranded permissions remain in OpenFGA. @@ -71,7 +70,7 @@ This catalog is updated as Platform Mesh component owners contribute the support ## Related - [Account resource](./account-resource.md) — uses the `core.platform-mesh.io` API group and its finalizers -- [ContentConfiguration](./content-configuration.md) — uses the `ui.platform-mesh.io` API group and the `ui.platform-mesh.io/entity` label +- [ContentConfiguration](./content-configuration.md) — uses the `ui.platform-mesh.io` API group and the `ui.platform-mesh.io/entity` and `ui.platform-mesh.io/content-for` labels - [Provider resource](./provider-resource.md) — uses the `providers.platform-mesh.io` API group and its finalizers - [ManagedProvider resource](./managed-provider-resource.md) — uses the `providers.platform-mesh.io` API group and its finalizers - [Account model](/concepts/account-model.md)