Skip to content

Commit d76d14e

Browse files
committed
Add provider side namespaces
1 parent d84f782 commit d76d14e

27 files changed

Lines changed: 392 additions & 39 deletions

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ All the actions shown between the clusters are done by the konnector, except: th
6262

6363
To get familiar with setting up the environment, please check out docs at [kube-bind.io](https://docs.kube-bind.io/main/setup).
6464

65+
## API Changes in v0.6.0 release
66+
67+
Addition of `PermissionClaims` to APIServiceExportSpec. It allows service provider to specify what additional resources are needed by the service consumer to effectively use the exported API. In example of a database service, the service consumer might need to create Secrets for database credentials, or ConfigMaps for configuration settings.
68+
69+
Because objects are namespaced on provider and consumer side, to establish correct RBAC `APIServiceNamespace` controller now creates Roles and RoleBindings.
70+
There is caviate that if backend operates in `ClusterScope` mode, the necessary cluster-wide permissions are created.
71+
Importnat: If provider wants to iniciate object, like `ConfigMap` or `Secret`, the provider needs to create `APIServiceNamespace` first, so that the necessary Roles and RoleBindings are created. This will
72+
6573
## API Changes in v0.5.0 release
6674

6775
Version v0.5.0 includes significant architectural improvements to the API structure:

apiserviceexport.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apiVersion: kube-bind.io/v1alpha2
2+
kind: APIServiceExportRequest
3+
metadata:
4+
name: sheriffs
5+
spec:
6+
namespaces:
7+
- name: sheriff-operations
8+
- name: sheriff-intelligence
9+
permissionClaims:
10+
- resource: secrets
11+
selector:
12+
labelSelector:
13+
matchLabels:
14+
app: sheriff
15+
security: high
16+
namedResources:
17+
- name: sheriff-badge-credentials
18+
- name: sheriff-jurisdiction-config
19+
- resource: configmaps
20+
selector:
21+
labelSelector:
22+
matchLabels:
23+
app: sheriff
24+
component: enforcement
25+
namedResources:
26+
- name: sheriff-jurisdiction-map
27+
- name: sheriff-enforcement-rules
28+
resources:
29+
- group: wildwest.dev
30+
resource: sheriffs
31+
versions:
32+
- v1alpha1
33+
status: {}

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/http/handler.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ type UISchema struct {
350350

351351
Resources []kubebindv1alpha2.APIServiceExportResource
352352
PermissionClaims []kubebindv1alpha2.PermissionClaim
353+
Namespaces []kubebindv1alpha2.Namespaces
353354

354355
// SessionID
355356
SessionID string
@@ -392,6 +393,7 @@ func (h *handler) handleResources(w http.ResponseWriter, r *http.Request) {
392393
Scope: string(item.Spec.Scope),
393394
PermissionClaims: item.Spec.PermissionClaims,
394395
Resources: item.Spec.Resources,
396+
Namespaces: item.Spec.Namespaces,
395397
SessionID: sessionID,
396398
})
397399
}
@@ -462,6 +464,7 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) {
462464
Spec: kubebindv1alpha2.APIServiceExportRequestSpec{
463465
Resources: module.Spec.Resources,
464466
PermissionClaims: module.Spec.PermissionClaims,
467+
Namespaces: module.Spec.Namespaces,
465468
},
466469
}
467470

@@ -524,11 +527,10 @@ func mustRead(f func(name string) ([]byte, error), name string) string {
524527
// listCollectionModules fetches the list of Collections from the backend cluster.
525528
// Flow is:
526529
// 1. List Collection and check what modules we are targeting
527-
// 2. Get modules from the backend cluster and consutruct shallow-bound schemas (no crd content)
530+
// 2. Get modules from the backend cluster and consutruct shallow-bound schemas (no crd content).
528531
func (h *handler) listCollectionModules(ctx context.Context, cluster string) (*catalogv1alpha1.ModuleList, error) {
529532
collections, err := h.kubeManager.ListCollections(ctx, cluster)
530533
if err != nil {
531-
532534
return nil, fmt.Errorf("failed to list collections: %w", err)
533535
}
534536

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 {

backend/template/resources.gohtml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,16 @@
204204
{{end}}
205205
</li>
206206
{{end}}
207+
{{if $schema.Namespaces}}
208+
<li class="list-group-item">
209+
<strong>Namespaces ({{len $schema.Namespaces}}):</strong>
210+
<div class="mt-1">
211+
{{range $schema.Namespaces}}
212+
<span class="item-tag">{{.Name}}</span>
213+
{{end}}
214+
</div>
215+
</li>
216+
{{end}}
207217
</ul>
208218
<div class="card-body">
209219
<a href="{{if $.Cluster}}/clusters/{{$.Cluster}}{{end}}/bind?s={{$schema.SessionID}}&module={{$schema.Name}}" class="btn bind-btn {{$schema.Name}}">

cli/pkg/kubectl/bind-apiservice/plugin/servicebindings.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (b *BindAPIServiceOptions) createAPIServiceBindings(ctx context.Context, co
6767
}
6868
}
6969
}
70-
70+
7171
return []*kubebindv1alpha2.APIServiceBinding{existing}, nil
7272
}
7373

0 commit comments

Comments
 (0)