Skip to content

Commit 584b545

Browse files
committed
Add provider side namespaces claims
Signed-off-by: Mangirdas Judeikis <mangirdas@judeikis.lt> On-behalf-of: @SAP mangirdas.judeikis@sap.com
1 parent 4848dc9 commit 584b545

17 files changed

Lines changed: 376 additions & 30 deletions

backend/controllers/serviceexport/serviceexport_controller.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type APIServiceExportReconciler struct {
5555
func NewAPIServiceExportReconciler(
5656
ctx context.Context,
5757
mgr mcmanager.Manager,
58+
scope kubebindv1alpha2.InformerScope,
5859
opts controller.TypedOptions[mcreconcile.Request],
5960
) (*APIServiceExportReconciler, error) {
6061
if err := mgr.GetFieldIndexer().IndexField(ctx, &kubebindv1alpha2.APIServiceExport{}, indexers.ServiceExportByBoundSchema,
@@ -66,6 +67,7 @@ func NewAPIServiceExportReconciler(
6667
manager: mgr,
6768
opts: opts,
6869
reconciler: reconciler{
70+
scope: scope,
6971
getBoundSchema: func(ctx context.Context, cache cache.Cache, namespace, name string) (*kubebindv1alpha2.BoundSchema, error) {
7072
var schema kubebindv1alpha2.BoundSchema
7173
key := types.NamespacedName{Namespace: namespace, Name: name}

backend/controllers/serviceexport/serviceexport_reconcile.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
)
3232

3333
type reconciler struct {
34+
scope kubebindv1alpha2.InformerScope
3435
getBoundSchema func(ctx context.Context, cache cache.Cache, namespace, name string) (*kubebindv1alpha2.BoundSchema, error)
3536
}
3637

@@ -41,6 +42,12 @@ func (r *reconciler) reconcile(ctx context.Context, cache cache.Cache, export *k
4142
errs = append(errs, err)
4243
}
4344

45+
if r.scope == kubebindv1alpha2.ClusterScope {
46+
if err := r.ensureClusterPermissions(ctx, cache, export); err != nil {
47+
errs = append(errs, err)
48+
}
49+
}
50+
4451
return utilerrors.NewAggregate(errs)
4552
}
4653

@@ -86,3 +93,12 @@ func (r *reconciler) ensureSchema(ctx context.Context, cache cache.Cache, export
8693

8794
return nil
8895
}
96+
97+
// ensureClusterPermissions ensures that the necessary cluster-wide permissions are in place for the exported APIs.
98+
// This runs only when backend is operating in ClusterScope. In this scenario, there might not yet be a namespace so
99+
// APIServiceNamespace reconciliation cannot be used.
100+
func (r *reconciler) ensureClusterPermissions(ctx context.Context, cache cache.Cache, export *kubebindv1alpha2.APIServiceExport) error {
101+
// TODO(mjudeikis): implement this for cluster scoped resources.
102+
// https://github.com/kube-bind/kube-bind/issues/344
103+
return nil
104+
}

backend/controllers/serviceexportrequest/serviceexportrequest_reconcile.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ func (r *reconciler) reconcile(ctx context.Context, cl client.Client, cache cach
7070
return fmt.Errorf("failed to ensure exports: %w", err)
7171
}
7272

73+
if err := r.ensureAPIServiceNamespaces(ctx, cl, cache, req); err != nil {
74+
conditions.SetSummary(req)
75+
return fmt.Errorf("failed to ensure APIServiceNamespaces: %w", err)
76+
}
77+
7378
// TODO(mjudeikis): we could potentially add finallizer to APIServiceExport above or "adopt" boundschemas
7479
// with owner references once export is created.
7580
// https://github.com/kube-bind/kube-bind/issues/297
@@ -367,3 +372,34 @@ func isClaimableAPI(claim kubebindv1alpha2.PermissionClaim) bool {
367372
}
368373
return false
369374
}
375+
376+
func (r *reconciler) ensureAPIServiceNamespaces(ctx context.Context, cl client.Client, cache cache.Cache, req *kubebindv1alpha2.APIServiceExportRequest) error {
377+
logger := klog.FromContext(ctx)
378+
379+
// TODO(mjudeikis): We have this object above already, pass it down to avoid extra get.
380+
export := &kubebindv1alpha2.APIServiceExport{}
381+
if err := cl.Get(ctx, client.ObjectKey{Namespace: req.Namespace, Name: req.Name}, export); err != nil {
382+
return fmt.Errorf("failed to get APIServiceExport %s/%s: %w", req.Namespace, req.Name, err)
383+
}
384+
385+
for _, ns := range req.Spec.Namespaces {
386+
apiServiceNamespace := helpers.APIServiceNamespaceFromExport(export, ns.Name)
387+
currentAPIServiceNamespace := &kubebindv1alpha2.APIServiceNamespace{}
388+
err := cache.Get(ctx, client.ObjectKey{Namespace: apiServiceNamespace.Namespace, Name: apiServiceNamespace.Name}, currentAPIServiceNamespace)
389+
if err != nil {
390+
if apierrors.IsNotFound(err) {
391+
logger.V(1).Info("Creating APIServiceNamespace", "name", apiServiceNamespace.Name, "namespace", apiServiceNamespace.Namespace)
392+
if err := cl.Create(ctx, apiServiceNamespace); err != nil {
393+
if apierrors.IsAlreadyExists(err) {
394+
continue
395+
}
396+
return err
397+
}
398+
} else {
399+
return err
400+
}
401+
}
402+
}
403+
404+
return nil
405+
}

backend/controllers/servicenamespace/servicenamespace_reconcile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (c *reconciler) reconcile(ctx context.Context, client client.Client, cache
106106
Rules: permissions,
107107
}
108108
// Create new ClusterRole
109-
if err := client.Create(ctx, role); err != nil {
109+
if err := client.Create(ctx, role); !errors.IsAlreadyExists(err) {
110110
return fmt.Errorf("failed to create ClusterRole %s: %w", name, err)
111111
}
112112
} else {

backend/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ func NewServer(ctx context.Context, c *Config) (*Server, error) {
147147
s.ServiceExport, err = serviceexport.NewAPIServiceExportReconciler(
148148
ctx,
149149
s.Config.Manager,
150+
kubebindv1alpha2.InformerScope(c.Options.ConsumerScope),
150151
opts,
151152
)
152153
if err != nil {

contrib/kcp/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ kubectl apply -f contrib/kcp/deploy/examples/sheriff.yaml
170170
```
171171

172172

173+
174+
173175
## Debug
174176

175177
```bash

contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ spec:
5454
crd: {}
5555
- group: kube-bind.io
5656
name: apiserviceexportrequests
57-
schema: v250929-62fc678.apiserviceexportrequests.kube-bind.io
57+
schema: v251020-7892cc6.apiserviceexportrequests.kube-bind.io
5858
storage:
5959
crd: {}
6060
- group: kube-bind.io

contrib/kcp/deploy/resources/apiresourceschema-apiserviceexportrequests.kube-bind.io.yaml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1
22
kind: APIResourceSchema
33
metadata:
44
creationTimestamp: null
5-
name: v250929-62fc678.apiserviceexportrequests.kube-bind.io
5+
name: v251020-7892cc6.apiserviceexportrequests.kube-bind.io
66
spec:
77
conversion:
88
strategy: None
@@ -208,6 +208,25 @@ spec:
208208
spec specifies how an API service from a service provider should be bound in the
209209
local consumer cluster.
210210
properties:
211+
namespaces:
212+
description: |-
213+
namespaces specifies the namespaces to bootstrap as part of this request.
214+
When objects originate from provider side, the consumer does not always know the necessary details.
215+
This field allows provider to pre-heat the necessary namespaces on provider side by creating
216+
APIServiceNamespace objects attached to the APIServiceExport. More namespaces can be created later by the consumer.
217+
items:
218+
properties:
219+
name:
220+
description: name is the name of the namespace to create on provider
221+
side.
222+
type: string
223+
required:
224+
- name
225+
type: object
226+
type: array
227+
x-kubernetes-validations:
228+
- message: namespaces are immutable
229+
rule: self == oldSelf
211230
parameters:
212231
description: |-
213232
parameters holds service provider specific parameters for this binding

deploy/crd/kube-bind.io_apiserviceexportrequests.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,25 @@ spec:
212212
spec specifies how an API service from a service provider should be bound in the
213213
local consumer cluster.
214214
properties:
215+
namespaces:
216+
description: |-
217+
namespaces specifies the namespaces to bootstrap as part of this request.
218+
When objects originate from provider side, the consumer does not always know the necessary details.
219+
This field allows provider to pre-heat the necessary namespaces on provider side by creating
220+
APIServiceNamespace objects attached to the APIServiceExport. More namespaces can be created later by the consumer.
221+
items:
222+
properties:
223+
name:
224+
description: name is the name of the namespace to create on
225+
provider side.
226+
type: string
227+
required:
228+
- name
229+
type: object
230+
type: array
231+
x-kubernetes-validations:
232+
- message: namespaces are immutable
233+
rule: self == oldSelf
215234
parameters:
216235
description: |-
217236
parameters holds service provider specific parameters for this binding

pkg/konnector/controllers/cluster/claimedresources/claimedresources_reconciler.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ import (
3131
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
3232
)
3333

34-
const label = "kube-bind.io/owner"
35-
3634
type readReconciler struct {
3735
getServiceNamespace func(upstreamNamespace string) (*kubebindv1alpha2.APIServiceNamespace, error)
3836
getProviderObject func(ns, name string) (*unstructured.Unstructured, error)
@@ -185,7 +183,7 @@ func (r readReconciler) makeConsumerOwner(obj *unstructured.Unstructured) {
185183
if a == nil {
186184
a = map[string]string{}
187185
}
188-
a[label] = string(kubebindv1alpha2.OwnerConsumer)
186+
a[kubebindv1alpha2.ObjectOwnerLabel] = string(kubebindv1alpha2.OwnerConsumer)
189187
obj.SetLabels(a)
190188
}
191189

@@ -194,7 +192,7 @@ func (r readReconciler) makeProviderOwner(obj *unstructured.Unstructured) {
194192
if a == nil {
195193
a = map[string]string{}
196194
}
197-
a[label] = string(kubebindv1alpha2.OwnerProvider)
195+
a[kubebindv1alpha2.ObjectOwnerLabel] = string(kubebindv1alpha2.OwnerProvider)
198196
obj.SetLabels(a)
199197
}
200198

@@ -237,7 +235,7 @@ func candidateFromOwnerObj(downstreamNS string, obj *unstructured.Unstructured)
237235
// consumer or provider side.
238236
func determineOwner(providerObj, consumerObj *unstructured.Unstructured) (kubebindv1alpha2.Owner, error) {
239237
if providerObj != nil {
240-
ownerLabel := providerObj.GetLabels()[label]
238+
ownerLabel := providerObj.GetLabels()[kubebindv1alpha2.ObjectOwnerLabel]
241239
switch ownerLabel {
242240
case kubebindv1alpha2.OwnerProvider.String():
243241
return kubebindv1alpha2.OwnerProvider, nil
@@ -250,7 +248,7 @@ func determineOwner(providerObj, consumerObj *unstructured.Unstructured) (kubebi
250248
}
251249

252250
if consumerObj != nil {
253-
ownerLabel := consumerObj.GetLabels()[label]
251+
ownerLabel := consumerObj.GetLabels()[kubebindv1alpha2.ObjectOwnerLabel]
254252
switch ownerLabel {
255253
case kubebindv1alpha2.OwnerProvider.String():
256254
return kubebindv1alpha2.OwnerProvider, nil

0 commit comments

Comments
 (0)