Skip to content

Commit a998472

Browse files
authored
Provider side namespace management (#345)
* Delete old stuf code * Add provider side namespaces claims * Update test & simplify logic --------- Signed-off-by: Mangirdas Judeikis <mangirdas@judeikis.lt> On-behalf-of: @SAP mangirdas.judeikis@sap.com
1 parent 10c28b8 commit a998472

20 files changed

Lines changed: 481 additions & 126 deletions

backend/controllers/clusterbinding/clusterbinding_controller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,10 @@ func mapAPIResourceSchema(clusterName string, cl cluster.Cluster) handler.TypedE
229229
return []mcreconcile.Request{
230230
{
231231
Request: reconcile.Request{
232-
NamespacedName: client.ObjectKeyFromObject(serviceExport),
232+
NamespacedName: types.NamespacedName{
233+
Namespace: serviceExport.GetNamespace(),
234+
Name: "cluster",
235+
},
233236
},
234237
ClusterName: clusterName,
235238
},

backend/controllers/serviceexport/serviceexport_controller.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222

2323
"k8s.io/apimachinery/pkg/api/equality"
2424
"k8s.io/apimachinery/pkg/api/errors"
25-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2625
"k8s.io/apimachinery/pkg/types"
2726
ctrl "sigs.k8s.io/controller-runtime"
2827
"sigs.k8s.io/controller-runtime/pkg/cache"
@@ -56,6 +55,7 @@ type APIServiceExportReconciler struct {
5655
func NewAPIServiceExportReconciler(
5756
ctx context.Context,
5857
mgr mcmanager.Manager,
58+
scope kubebindv1alpha2.InformerScope,
5959
opts controller.TypedOptions[mcreconcile.Request],
6060
) (*APIServiceExportReconciler, error) {
6161
if err := mgr.GetFieldIndexer().IndexField(ctx, &kubebindv1alpha2.APIServiceExport{}, indexers.ServiceExportByBoundSchema,
@@ -67,6 +67,7 @@ func NewAPIServiceExportReconciler(
6767
manager: mgr,
6868
opts: opts,
6969
reconciler: reconciler{
70+
scope: scope,
7071
getBoundSchema: func(ctx context.Context, cache cache.Cache, namespace, name string) (*kubebindv1alpha2.BoundSchema, error) {
7172
var schema kubebindv1alpha2.BoundSchema
7273
key := types.NamespacedName{Namespace: namespace, Name: name}
@@ -75,14 +76,6 @@ func NewAPIServiceExportReconciler(
7576
}
7677
return &schema, nil
7778
},
78-
deleteServiceExport: func(ctx context.Context, cl client.Client, ns, name string) error {
79-
return cl.Delete(ctx, &kubebindv1alpha2.APIServiceExport{
80-
ObjectMeta: metav1.ObjectMeta{
81-
Namespace: ns,
82-
Name: name,
83-
},
84-
})
85-
},
8679
},
8780
}
8881

backend/controllers/serviceexport/serviceexport_reconcile.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
utilerrors "k8s.io/apimachinery/pkg/util/errors"
2424
"k8s.io/klog/v2"
2525
"sigs.k8s.io/controller-runtime/pkg/cache"
26-
"sigs.k8s.io/controller-runtime/pkg/client"
2726

2827
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
2928
"github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2/helpers"
@@ -32,8 +31,8 @@ import (
3231
)
3332

3433
type reconciler struct {
35-
getBoundSchema func(ctx context.Context, cache cache.Cache, namespace, name string) (*kubebindv1alpha2.BoundSchema, error)
36-
deleteServiceExport func(ctx context.Context, client client.Client, namespace, name string) error
34+
scope kubebindv1alpha2.InformerScope
35+
getBoundSchema func(ctx context.Context, cache cache.Cache, namespace, name string) (*kubebindv1alpha2.BoundSchema, error)
3736
}
3837

3938
func (r *reconciler) reconcile(ctx context.Context, cache cache.Cache, export *kubebindv1alpha2.APIServiceExport) error {
@@ -43,6 +42,12 @@ func (r *reconciler) reconcile(ctx context.Context, cache cache.Cache, export *k
4342
errs = append(errs, err)
4443
}
4544

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

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

8994
return nil
9095
}
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.ObjectKeyFromObject(req), 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.ObjectKeyFromObject(apiServiceNamespace), 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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (c *reconciler) reconcile(ctx context.Context, client client.Client, cache
8383
}
8484

8585
for _, export := range apiServiceExports.Items {
86-
name := fmt.Sprintf("kube-binder-%s-export-%s", sns.Name, export.Name) // per-sns unique name
86+
name := fmt.Sprintf("kube-binder-export-%s-%s", sns.Name, export.Name) // per-sns unique name
8787
permissions := []rbacv1.PolicyRule{}
8888
for _, claim := range export.Spec.PermissionClaims {
8989
permissions = append(permissions, rbacv1.PolicyRule{
@@ -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); err != nil && !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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ kubectl kcp bind apiexport root:provider:cowboys-stable
104104
```bash
105105
kubectl get logicalcluster
106106
# NAME PHASE URL AGE
107-
# cluster Ready https://192.168.2.166:6443/clusters/1d5vpxvdpy0opbj1
107+
# cluster Ready https://192.168.2.166:6443/clusters/1f4roigyt6meiaf8
108108
```
109109

110110
## Consumer
@@ -120,7 +120,7 @@ kubectl ws create consumer --enter
120120
10. Bind the thing:
121121

122122
```bash
123-
./bin/kubectl-bind http://127.0.0.1:8080/clusters/1d5vpxvdpy0opbj1/exports --dry-run -o yaml > apiserviceexport.yaml
123+
./bin/kubectl-bind http://127.0.0.1:8080/clusters/1f4roigyt6meiaf8/exports --dry-run -o yaml > apiserviceexport.yaml
124124

125125
# Extract secret for binding process. Note that secret name is not the same as output from command above. Check secret
126126
# name by running `kubectl get secret -n kube-bind`
@@ -169,7 +169,6 @@ kubectl apply -f contrib/kcp/deploy/examples/cowboy.yaml
169169
kubectl apply -f contrib/kcp/deploy/examples/sheriff.yaml
170170
```
171171

172-
173172
## Debug
174173

175174
```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

0 commit comments

Comments
 (0)