Skip to content

Commit f44925e

Browse files
authored
Providers API v2 (#660)
Provider bootstrapping API updates The changes mainly revolve around tightening the bootstrap workflow & RBACs. The Provider resource provisions a workspace + admin kubeconfig against that workspace. This is what the service provider receives, and it is the SERVICE PROVIDER's responsibility to populate this workspace with whatever is necessary to expose that service (e.g. APIExport, ContentConfiguration, ...); this can be for example an initContainer in the service operator or some other mechanism. The ManagedProvider is platform-owned "convenience" layer to onboard a platform-owned service. This triggers creating the Provider obj on kcp side, and passing the workspace admin kubeconfig to the specified deployments. The same principle about applies here as well: the deployment is expected to bring its own APIExport, ContentConfiguration, ... On-behalf-of: @SAP robert.vasek@sap.com Signed-off-by: Robert Vasek <rvasek01@gmail.com>
1 parent 859b760 commit f44925e

30 files changed

Lines changed: 1657 additions & 728 deletions

README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,108 @@ The Resource subroutine (in `pkg/subroutines/resource/`) manages OCM Resource ob
638638
- For ArgoCD: updates Application objects with resolved OCI repository URLs from OCM Resources
639639
- Manages image version extraction and stores versions in the ImageVersionStore
640640

641+
## Provider Bootstrap
642+
643+
Provider bootstrapping spans two controllers and two CRDs:
644+
645+
- **`Provider`** (reconciled by the Provider controller) — provisions a dedicated kcp workspace and issues a scoped kubeconfig for it. Owned by the service provider. Kcp-level only.
646+
- **`ManagedProvider`** (reconciled by the platform-mesh controller) — a convenience API for the platform-mesh admin to onboard a platform-owned service end-to-end: creates the `Provider`, waits for it to be Ready, copies the kubeconfig into Platform Mesh's runtime cluster, and deploys the service operator there.
647+
648+
### Provider Resource
649+
650+
`Provider` is a kcp-level resource, watched by the Provider controller. Creating one provisions a dedicated workspace and a scoped kubeconfig. Once `status.phase` is `Ready`, it is the provider's responsibility to bootstrap their workspace resources (APIExports, schemas, etc.) using that kubeconfig, and to wire the kubeconfig into their service controllers. Afterwards, the provider can then watch their APIExport virtual workspace to reconcile consumers of the service.
651+
652+
```yaml
653+
apiVersion: providers.platform-mesh.io/v1alpha1
654+
kind: Provider
655+
metadata:
656+
name: my-service
657+
spec:
658+
# Optional — overrides the name, namespace, and key of the kubeconfig Secret.
659+
# Defaults to name: <Provider.Name>-provider-kubeconfig, namespace: default, key: kubeconfig.
660+
providerKubeconfigSecret:
661+
name: my-service-provider-kubeconfig
662+
namespace: default
663+
key: kubeconfig
664+
# Optional — override the kcp front-proxy host written into the kubeconfig.
665+
hostOverride: "https://frontproxy.example.com:8443"
666+
status:
667+
phase: Ready
668+
providerKubeconfigSecretRef:
669+
name: my-service-provider-kubeconfig
670+
namespace: default
671+
```
672+
673+
#### Provider Controller Subroutine Chain
674+
675+
| # | Subroutine | Action |
676+
|---|-----------|--------|
677+
| 1 | `ProviderWorkspaceSubroutine` | Creates a dedicated workspace under `root:providers` (e.g. `root:providers:wildwest-2cyb4oxml4sv8o3r`) |
678+
| 2 | `ScopedKubeconfigSubroutine` | Inside the provider workspace, creates a ServiceAccount, cluster-admin ClusterRoleBinding, and a static token Secret; generates a kubeconfig from those credentials and writes a kubeconfig Secret into the workspace where the Provider lives |
679+
680+
### ManagedProvider Resource
681+
682+
`ManagedProvider` is a resource created by the platform-mesh admin. It is a convenience API for onboarding a platform-owned service. On the kcp side, it creates a `Provider` object in `root:providers:system` (overridable via `spec.provider`) and waits for it to reach `Ready`. On the runtime side, it copies the kubeconfig into Platform Mesh's runtime cluster so that service operators can reach their provider workspace, and finally deploys those operators.
683+
684+
```yaml
685+
apiVersion: providers.platform-mesh.io/v1alpha1
686+
kind: ManagedProvider
687+
metadata:
688+
name: wildwest
689+
namespace: platform-mesh-system
690+
spec:
691+
# Which PlatformMesh instance this ManagedProvider belongs to.
692+
platformMeshRef:
693+
name: platform-mesh
694+
695+
# Service operators to deploy in Platform Mesh's runtime cluster.
696+
# Each OCM component is resolved and deployed as a Helm chart via FluxCD.
697+
runtimeDeployments:
698+
- ocm:
699+
componentName: wildwest-controller
700+
registry: ghcr.io/platform-mesh/helm-charts
701+
version: "0.1.0"
702+
values:
703+
kubeconfig:
704+
secretName: wildwest-provider-kubeconfig # scoped kubeconfig copied by KubeconfigCopySubroutine
705+
- ocm:
706+
componentName: wildwest-portal
707+
registry: ghcr.io/platform-mesh/helm-charts
708+
version: "0.1.0"
709+
710+
# If true, also deletes the Provider on the kcp side when ManagedProvider is deleted,
711+
# which cascades to delete the provider workspace and generated kubeconfig.
712+
cleanupOnDelete: false
713+
714+
# Optional — where to store the provider kubeconfig Secret in Platform Mesh's runtime cluster.
715+
# Defaults to name: <ManagedProvider.Name>-provider-kubeconfig, key: kubeconfig.
716+
# providerKubeconfigSecret:
717+
# name: wildwest-provider-kubeconfig
718+
# key: kubeconfig
719+
720+
# Optional — target a specific Platform Mesh runtime cluster. Defaults to the hosting cluster.
721+
# runtimeKubeconfigSecretName: platform-runtime-wildwest-kubeconfig
722+
723+
# Optional — override the kcp front-proxy host in the generated kubeconfig.
724+
# providerHostOverride: https://root.kcp.localhost:31000
725+
726+
# Optional — create the Provider in a different workspace (default: root:providers:system),
727+
# or adopt an already-existing Provider at the given path and name.
728+
# provider:
729+
# path: root:orgs:org-a:demo-acc
730+
# name: wildwest-platform
731+
```
732+
733+
#### ManagedProvider Controller Subroutine Chain
734+
735+
| # | Subroutine | Action |
736+
|---|-----------|--------|
737+
| 1 | `WaitPlatformMeshSubroutine` | Waits for the referenced `PlatformMesh` to have its `Ready` condition `True` |
738+
| 2 | `ProviderResourceSubroutine` | Creates (or adopts) the `Provider` resource at the specified kcp path (default: `root:providers:system`) |
739+
| 3 | `WaitProviderSubroutine` | Polls the `Provider` until `status.phase == "Ready"` |
740+
| 4 | `KubeconfigCopySubroutine` | Copies the kubeconfig Secret from the Provider's kcp workspace into Platform Mesh's runtime cluster; sets `status.providerKubeconfigSecretRef` |
741+
| 5 | `DeploySubroutine` | Deploys each `spec.runtimeDeployments` entry into the target cluster (OCM components resolved and installed via FluxCD) |
742+
641743
## Releasing
642744

643745
The release is performed automatically through a GitHub Actions Workflow.

api/providers/v1alpha1/managedprovider_types.go

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,60 @@ import (
2222
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2323
)
2424

25+
const (
26+
ManagedProviderPhasePending = "Pending"
27+
ManagedProviderPhaseWaitingForPlatformMesh = "WaitingForPlatformMesh"
28+
ManagedProviderPhaseWaitingForProvider = "WaitingForProvider"
29+
ManagedProviderPhaseCopyingKubeconfig = "CopyingKubeconfig"
30+
ManagedProviderPhaseCopyingKubeconfigFailed = "CopyingKubeconfigFailed"
31+
ManagedProviderPhaseDeploying = "Deploying"
32+
ManagedProviderPhaseReady = "Ready"
33+
ManagedProviderPhaseDeleting = "Deleting"
34+
)
35+
2536
// ManagedProviderSpec defines the desired state of ManagedProvider.
2637
// ManagedProvider is a runtime-cluster resource that orchestrates the full
2738
// provider lifecycle: workspace creation, Provider bootstrap, secret copy,
2839
// and workload deployment.
2940
type ManagedProviderSpec struct {
30-
// workspacePath is the full kcp logical path for the provider workspace.
31-
// Defaults to root:providers:<name> when omitted.
32-
// When set, must be of the form root:providers:<name>
41+
// provider references the Provider resource.
42+
// If not provided, a corresponding Provider is created in root:providers:system by default.
43+
// If corresponding Provider doesn't exist, it is created.
44+
// If specified, the workspace ProviderReference.Path must exist.
45+
//
46+
// +optional
47+
ProviderReference *ProviderReferenceSpec `json:"provider,omitempty"`
48+
49+
// providerHostOverride overrides the kcp front-proxy host written into the
50+
// generated kubeconfig Secret. Optional; defaults to the operator-configured
51+
// front-proxy URL.
52+
// +optional
53+
ProviderHostOverride string `json:"providerHostOverride,omitempty"`
54+
55+
// runtimeKubeconfigSecretName is the name of the Secret that contains
56+
// data.kubeconfig key, with kubeconfig referencing the runtime cluster
57+
// where the provider components are to be deployed.
58+
// If not provided, the hosting platform-mesh cluster is used.
3359
//
3460
// +optional
35-
// +kubebuilder:validation:XValidation:rule="self == '' || self.matches('^root:providers:[a-z][a-z0-9]*$')",message="workspacePath must be of the form root:providers:<Workspace name>"
36-
WorkspacePath string `json:"workspacePath,omitempty"`
61+
RuntimeKubeconfigSecretName string `json:"runtimeKubeconfigSecretName,omitempty"`
62+
63+
// providerKubeconfigSecret is a Secret specification used to store
64+
// the provider's kubeconfig in the runtime cluster. This is the admin kubeconfig
65+
// that provider controllers can use to access the workspace in :root:providers:<WS name>.
66+
// If not provided, a default of Name:<ManagedProvider.Name>-provider-kubeconfig,
67+
// Key:kubeconfig is used.
68+
//
69+
// +optional
70+
ProviderKubeconfigSecret *LocalKubeconfigSecretSpec `json:"providerKubeconfigSecret,omitempty"`
3771

3872
// platformMeshRef is a reference to the PlatformMesh object.
3973
// It must refer to the PlatformMesh instance this ManagedProvider
4074
// is associated with.
4175
PlatformMeshReference PlatformMeshReferenceSpec `json:"platformMeshRef"`
4276

43-
// controller defines the OCM component to deploy as the provider controller.
44-
// +required
45-
Controller ProviderComponentSpec `json:"controller"`
46-
47-
// portal defines the OCM component to deploy as the provider portal.
48-
// +optional
49-
Portal *ProviderComponentSpec `json:"portal,omitempty"`
77+
// runtimeDeployments is a list of components to be deployed in the runtime cluster.
78+
RuntimeDeployments []ProviderComponentSpec `json:"runtimeDeployments"`
5079

5180
// cleanupOnDelete removes the kcp workspace when the ManagedProvider is deleted.
5281
// +optional
@@ -60,11 +89,36 @@ type PlatformMeshReferenceSpec struct {
6089
Name string `json:"name"`
6190
}
6291

92+
// ProviderReferenceSpec is a reference to a Provider object.
93+
type ProviderReferenceSpec struct {
94+
// path is a logical cluster path where the Provider is defined.
95+
//
96+
// +required
97+
// +kubebuilder:validation:Pattern:="^[a-z0-9]([-a-z0-9]*[a-z0-9])?(:[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
98+
Path string `json:"path"`
99+
100+
// name is the name of the referenced Provider.
101+
//
102+
// +required
103+
// +kubebuilder:validation:Required
104+
// +kube:validation:MinLength=1
105+
Name string `json:"name"`
106+
}
107+
108+
// LocalKubeconfigSecretSpec describes a Secret and the key with kubeconfig YAML content.
109+
type LocalKubeconfigSecretSpec struct {
110+
// name is the name of the Secret.
111+
Name string `json:"name"`
112+
113+
// key is the key in data map where the kubeconfig YAML content is stored.
114+
Key string `json:"key"`
115+
}
116+
63117
// ProviderComponentSpec references an OCM component to deploy.
64118
type ProviderComponentSpec struct {
65119
// ocm identifies the component in an OCM registry.
66-
// +required
67-
OCM OCMComponentSpec `json:"ocm"`
120+
// +optional
121+
OCM *OCMComponentSpec `json:"ocm,omitempty"`
68122
}
69123

70124
// OCMComponentSpec identifies a component in an OCM registry.
@@ -84,6 +138,11 @@ type OCMComponentSpec struct {
84138
// values are Helm values passed to the deployed chart.
85139
// +optional
86140
Values apiextensionsv1.JSON `json:"values,omitempty"`
141+
142+
// insecure allows HTTP (insecure) connection to the OCI repository.
143+
// Disabled by default.
144+
// +optional
145+
Insecure bool `json:"insecure,omitempty"`
87146
}
88147

89148
// ManagedProviderStatus defines the observed state of ManagedProvider.
@@ -92,10 +151,10 @@ type ManagedProviderStatus struct {
92151
// +optional
93152
Phase string `json:"phase,omitempty"`
94153

95-
// kubeconfigSecretRef points to the Secret in the runtime namespace that
154+
// providerKubeconfigSecretRef points to the Secret in the runtime namespace that
96155
// contains the scoped kubeconfig copied from the provider kcp workspace.
97156
// +optional
98-
KubeconfigSecretRef *corev1.SecretReference `json:"kubeconfigSecretRef,omitempty"`
157+
ProviderKubeconfigSecretRef *corev1.SecretReference `json:"providerKubeconfigSecretRef,omitempty"`
99158

100159
// conditions represent the current state of the ManagedProvider resource.
101160
// +listType=map

api/providers/v1alpha1/provider_types.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,56 @@ import (
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2222
)
2323

24+
const (
25+
ProviderPhasePending = "Pending"
26+
ProviderPhaseProvisioningWorkspace = "ProvisioningWorkspace"
27+
ProviderPhaseProvisioningKubeconfig = "ProvisioningKubeconfig"
28+
ProviderPhaseReady = "Ready"
29+
ProviderPhaseDeleting = "Deleting"
30+
)
31+
2432
// ProviderSpec defines the desired state of Provider.
2533
// Provider is a kcp-level resource that handles kcp-side bootstrap only
26-
// (ServiceAccount, RBAC, kubeconfig Secret). It has no runtime-side effects.
34+
// (Workspace, ServiceAccount, RBAC, kubeconfig Secret). It has no runtime-side effects.
2735
type ProviderSpec struct {
36+
// providerKubeconfigSecret is a Secret specification used to store
37+
// the provider's kubeconfig. This is the admin kubeconfig that provider controllers
38+
// can use to access the workspace in :root:providers:<WS name>.
39+
// If not provided, a default of Namespace:default, Name:<Provider.Name>-provider-kubeconfig,
40+
// Key:kubeconfig is used.
41+
//
42+
// +optional
43+
ProviderKubeconfigSecret *KubeconfigSecretSpec `json:"providerKubeconfigSecret,omitempty"`
44+
2845
// hostOverride overrides the kcp front-proxy host written into the
2946
// generated kubeconfig Secret. Optional; defaults to the operator-configured
3047
// front-proxy URL.
3148
// +optional
3249
HostOverride string `json:"hostOverride,omitempty"`
3350
}
3451

52+
// KubeconfigSecretSpec describes a Secret and the key with kubeconfig YAML content.
53+
type KubeconfigSecretSpec struct {
54+
// name is the name of the Secret.
55+
Name string `json:"name"`
56+
57+
// namespace is the namespace of the Secret.
58+
Namespace string `json:"namespace"`
59+
60+
// key is the key in data map where the kubeconfig YAML content is stored.
61+
Key string `json:"key"`
62+
}
63+
3564
// ProviderStatus defines the observed state of Provider.
3665
type ProviderStatus struct {
3766
// phase summarises the bootstrap state of the Provider (e.g. "Pending", "Ready").
3867
// +optional
3968
Phase string `json:"phase,omitempty"`
4069

41-
// kubeconfigSecretRef points to the Secret created in the provider workspace
42-
// that contains the scoped kubeconfig.
70+
// providerKubeconfigSecretRef points to the Secret that contains
71+
// the scoped kubeconfig for the provider workspace.
4372
// +optional
44-
KubeconfigSecretRef *corev1.SecretReference `json:"kubeconfigSecretRef,omitempty"`
73+
ProviderKubeconfigSecretRef *corev1.SecretReference `json:"providerKubeconfigSecretRef,omitempty"`
4574

4675
// conditions represent the current state of the Provider resource.
4776
// +listType=map

0 commit comments

Comments
 (0)