diff --git a/docs/content/concepts/workspaces/workspace-initialization.md b/docs/content/concepts/workspaces/workspace-initialization.md index c27ef8fc65e..9eb6c041987 100644 --- a/docs/content/concepts/workspaces/workspace-initialization.md +++ b/docs/content/concepts/workspaces/workspace-initialization.md @@ -8,7 +8,7 @@ Initializers are used to customize workspaces and bootstrap required resources u ### Defining Initializers in WorkspaceTypes -A `WorkspaceType` can specify an initializer using the `initializer` field. Here is an example of a `WorkspaceType` with an initializer. +A `WorkspaceType` can specify having an initializer using the `initializer` field. Here is an example of a `WorkspaceType` with an initializer. ```yaml apiVersion: tenancy.kcp.io/v1alpha1 @@ -22,6 +22,25 @@ spec: path: root ``` +Each initializer has a unique name, which gets automatically generated using `:`. So for example, if you were to apply the aforementioned WorkspaceType on the root workspace, your initializer would be called `root:example`. + +Since `WorkspaceType.spec.initializers` is a boolean field, each WorkspaceType comes with a single initializer by default. However each WorkspaceType inherits the initializers of its parent WorkspaceType. As a result, it is possible to have multiple initializers on a WorkspaceType using [WorkspaceType Extension](../../concepts/workspaces/workspace-types.md#workspace-type-extensions-and-constraints) + +In the following example, `child` inherits the initializers of `parent`. As a result, child workspaces will have the `root:child` and `root:parent` initializers set. + +```yaml +apiVersion: tenancy.kcp.io/v1alpha1 +kind: WorkspaceType +metadata: + name: child +spec: + initializer: true + extend: + with: + - name: parent + path: root +``` + ### Enforcing Permissions for Initializers The non-root user must have the `verb=initialize` on the `WorkspaceType` that the initializer is for. This ensures that only authorized users can perform initialization actions using virtual workspace endpoint. Here is an example of the `ClusterRole`. @@ -37,6 +56,7 @@ rules: resourceNames: ["example"] verbs: ["initialize"] ``` + You can then bind this role to a user or a group. ```yaml @@ -54,46 +74,40 @@ roleRef: apiGroup: rbac.authorization.k8s.io ``` -## initializingworkspaces Virtual Workspace - -As a service provider, you can use the `initializingworkspaces` virtual workspace to manage workspace resources in the initializing phase. This virtual workspace allows you to fetch `LogicalCluster` objects that are in the initializing phase and request initialization by a specific controller. +## Writing Custom Initialization Controllers -This Virtual Workspace can fetch `LogicalCluster` either by specific its name or using wildcard. +### Responsibilities Of Custom Initialization Controllers -### Endpoint URL path +Custom Initialization Controllers are responsible for handling initialization logic for custom WorkspaceTypes. They interact with kcp by: -`initializingworkspaces` Virtual Workspace provide a virtual api-server to access workspaces that are initializing with the specific initializer. These URLs are published in the status of WorkspaceType object. +1. Watching for the creation of new LogicalClusters (the backing object behind Workspaces) with the corresponding initializer on them +2. Running any custom initialization logic +3. Removing the corresponding initializer from the `.status.initializers` list of the LogicalCluster after initialization logic has successfully finished +In order to simplify these processes, kcp provides the `initializingworkspaces` virtual workspace. -```yaml - virtualWorkspaces: - - url: https://:6443/services/initializingworkspaces/ -``` +### The `initializingworkspaces` Virtual Workspace -This is an example URL path for accessing logical cluster apis for a specific initializer in a `initializingworkspaces` virtual workspace. +As a service provider, you can use the `initializingworkspaces` virtual workspace to manage workspace resources in the initializing phase. This virtual workspace allows you to fetch `LogicalCluster` objects that are in the initializing phase and request initialization by a specific controller. -```yaml -/services/initializingworkspaces//clusters/*/apis/core.kcp.io/v1alpha1/logicalclusters -``` +You can retrieve the url of a Virtual Workspace directly from the `.status.virtualWorkspaces` field of the corresponding WorkspaceType. Returning to our previous example using a custom WorkspaceType called "example", you will receive the following output: -You can also use `LogicalCluster` name for the direct view, allowing to manage all resources within that logical cluster. +```sh +$ kubectl get workspacetype example -o yaml -```yaml -/services/initializingworkspaces//clusters//apis/core.kcp.io/v1alpha1/logicalclusters +... +status: + virtualWorkspaces: + - url: https:///services/initializingworkspaces/root:example ``` -### Example workflow - -* Add your custom WorkspaceType to the platform with an initializer. +You can use this url to construct a kubeconfig for your controller. To do so, use the url directly as the `cluster.server` in your kubeconfig and provide the subject with sufficient permissions (see [Enforcing Permissions for Initializers](#enforcing-permissions-for-initializers)) -* Create a workspace with the necessary warrants and scopes. The workspace will stay in the initializing state as the initializer is present. +### Code Sample -* Use a controller to watch your initializing workspaces, you can interact with the workspace through the virtual workspace endpoint: - -```yaml -/services/initializingworkspaces/foo/clusters/*/apis/core.kcp.io/v1alpha1/logicalclusters -``` +When writing a custom initializer, the following needs to be taken into account: -* Once you get the object, you need to initialize the workspace with its related resources, using the same endpoint +* We strongly recommend to use the kcp [initializingworkspace multicluster-provider](https://github.com/kcp-dev/multicluster-provider) to build your custom initializer +* You need to update LogicalClusters using patches; They cannot be updated using the update api -* Once the initialization is complete, use the same endpoint to remove the initializer from the workspace. +Keeping this in mind, you can use the [multicluster-provider initializingworkspaces example](https://github.com/kcp-dev/multicluster-provider/tree/main/examples/initializingworkspaces) as a starting point for your initialization controller diff --git a/pkg/reconciler/committer/committer.go b/pkg/reconciler/committer/committer.go index 8dee1aa0925..4055f9d95e5 100644 --- a/pkg/reconciler/committer/committer.go +++ b/pkg/reconciler/committer/committer.go @@ -51,7 +51,7 @@ type Patcher[R runtime.Object] interface { } // CommitFunc is an alias to clean up type declarations. -type CommitFunc[Sp any, St any] func(context.Context, *Resource[Sp, St], *Resource[Sp, St]) error +type CommitFunc[Sp any, St any] func(_ context.Context, old *Resource[Sp, St], new *Resource[Sp, St]) error // NewCommitter returns a function that can patch instances of R based on meta, // spec or status changes using a cluster-aware patcher.