Skip to content

Commit 23979b3

Browse files
committed
add configurable claims
1 parent 8ab6385 commit 23979b3

14 files changed

Lines changed: 1326 additions & 89 deletions

backend/controllers/serviceexportrequest/serviceexportrequest_reconcile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ func (r *reconciler) validate(ctx context.Context, cl client.Client, req *kubebi
322322

323323
// isClaimableAPI checks if a permission claim is for a claimable API.
324324
func isClaimableAPI(claim kubebindv1alpha2.PermissionClaim) bool {
325-
for _, api := range kubebindv1alpha2.ClaimableAPIs {
325+
for _, api := range kubebindv1alpha2.ClaimableAPIsData {
326326
if claim.Group == api.GroupVersionResource.Group && claim.Resource == api.Names.Plural {
327327
return true
328328
}

backend/http/handler.go

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ func (h *handler) AddRoutes(mux *mux.Router) {
116116

117117
mux.HandleFunc("/api/callback", h.handleCallback).Methods(http.MethodGet)
118118
mux.HandleFunc("/api/healthz", h.handleHealthz).Methods(http.MethodGet)
119+
mux.HandleFunc("/api/bindable-resources", h.handleBindableResources).Methods(http.MethodGet)
119120

120121
if strings.HasPrefix(h.frontend, "http://") {
121122
spaserver, err := spaserver.NewSPAReverseProxyServer(h.frontend)
@@ -577,38 +578,54 @@ func (h *handler) handleBindPost(w http.ResponseWriter, r *http.Request) {
577578
return
578579
}
579580

580-
// Create API service export requests for each resource
581-
var requests []runtime.RawExtension
582-
for _, bindableResource := range bindRequest.Resources {
583-
exportRequest := kubebindv1alpha2.APIServiceExportRequestResponse{
584-
TypeMeta: metav1.TypeMeta{
585-
APIVersion: kubebindv1alpha2.SchemeGroupVersion.String(),
586-
Kind: "APIServiceExportRequest",
587-
},
588-
ObjectMeta: kubebindv1alpha2.NameObjectMeta{
589-
Name: bindableResource.Resource + "." + bindableResource.Group,
590-
},
591-
Spec: kubebindv1alpha2.APIServiceExportRequestSpec{
592-
Resources: []kubebindv1alpha2.APIServiceExportRequestResource{
593-
{
594-
GroupResource: kubebindv1alpha2.GroupResource{
595-
Group: bindableResource.Group,
596-
Resource: bindableResource.Resource,
597-
},
598-
Versions: []string{bindableResource.APIVersion},
599-
},
600-
},
581+
exportRequest := kubebindv1alpha2.APIServiceExportRequestResponse{
582+
TypeMeta: metav1.TypeMeta{
583+
APIVersion: kubebindv1alpha2.SchemeGroupVersion.String(),
584+
Kind: "APIServiceExportRequest",
585+
},
586+
ObjectMeta: kubebindv1alpha2.NameObjectMeta{
587+
Name: bindRequest.Name,
588+
},
589+
Spec: kubebindv1alpha2.APIServiceExportRequestSpec{
590+
Resources: []kubebindv1alpha2.APIServiceExportRequestResource{},
591+
PermissionClaims: []kubebindv1alpha2.PermissionClaim{},
592+
},
593+
}
594+
595+
for _, resource := range bindRequest.Resources {
596+
exportRequest.Spec.Resources = append(exportRequest.Spec.Resources, kubebindv1alpha2.APIServiceExportRequestResource{
597+
GroupResource: kubebindv1alpha2.GroupResource{
598+
Group: resource.Group,
599+
Resource: resource.Resource,
601600
},
602-
}
601+
Versions: []string{resource.APIVersion},
602+
})
603+
}
603604

604-
requestBytes, err := json.Marshal(&exportRequest)
605+
for _, claim := range bindRequest.PermissionClaims {
606+
_, err := kubebindv1alpha2.ResolveClaimableAPI(claim)
605607
if err != nil {
606-
logger.Error(err, "failed to marshal export request", "resource", bindableResource.Resource)
607-
http.Error(w, "internal error", http.StatusInternalServerError)
608+
logger.Error(err, "invalid permission claim", "claim", claim)
609+
http.Error(w, fmt.Sprintf("invalid permission claim: %v", err), http.StatusBadRequest)
608610
return
609611
}
612+
exportRequest.Spec.PermissionClaims = append(exportRequest.Spec.PermissionClaims, kubebindv1alpha2.PermissionClaim{
613+
GroupResource: kubebindv1alpha2.GroupResource{
614+
Group: claim.Group,
615+
Resource: claim.Resource,
616+
},
617+
Selector: kubebindv1alpha2.Selector{
618+
All: claim.Selector.All,
619+
LabelSelector: claim.Selector.LabelSelector,
620+
},
621+
})
622+
}
610623

611-
requests = append(requests, runtime.RawExtension{Raw: requestBytes})
624+
requestBytes, err := json.Marshal(&exportRequest)
625+
if err != nil {
626+
logger.Error(err, "failed to marshal export request")
627+
http.Error(w, "internal error", http.StatusInternalServerError)
628+
return
612629
}
613630

614631
// Create binding response
@@ -624,7 +641,7 @@ func (h *handler) handleBindPost(w http.ResponseWriter, r *http.Request) {
624641
},
625642
},
626643
Kubeconfig: kfg,
627-
Requests: requests,
644+
Requests: []runtime.RawExtension{{Raw: requestBytes}},
628645
}
629646

630647
payload, err := json.Marshal(&response)
@@ -683,3 +700,18 @@ func (h *handler) getBackendDynamicResource(ctx context.Context, cluster string)
683700
}
684701
return h.kubeManager.ListDynamicResources(ctx, cluster, gvk, labelSelector.AsSelector())
685702
}
703+
704+
// handleBindableResources returns a static list of resources that can be claimed/bound by users.
705+
func (h *handler) handleBindableResources(w http.ResponseWriter, r *http.Request) {
706+
logger := getLogger(r)
707+
708+
bs, err := json.Marshal(&kubebindv1alpha2.ClaimableAPIsData)
709+
if err != nil {
710+
logger.Error(err, "failed to marshal resources")
711+
http.Error(w, "internal error", http.StatusInternalServerError)
712+
return
713+
}
714+
715+
w.Header().Set("Content-Type", "application/json")
716+
w.Write(bs) //nolint:errcheck
717+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.17.3
7+
name: bindableresourcesrequests.kube-bind.io
8+
spec:
9+
group: kube-bind.io
10+
names:
11+
kind: BindableResourcesRequest
12+
listKind: BindableResourcesRequestList
13+
plural: bindableresourcesrequests
14+
singular: bindableresourcesrequest
15+
scope: Namespaced
16+
versions:
17+
- name: v1alpha2
18+
schema:
19+
openAPIV3Schema:
20+
description: |-
21+
BindableResourcesRequest is sent by the consumer to the service provider
22+
to indicate which resources the user wants to bind to. It is sent after
23+
authentication and resource selection on the service provider website.
24+
properties:
25+
apiVersion:
26+
description: |-
27+
APIVersion defines the versioned schema of this representation of an object.
28+
Servers should convert recognized schemas to the latest internal value, and
29+
may reject unrecognized values.
30+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
31+
type: string
32+
kind:
33+
description: |-
34+
Kind is a string value representing the REST resource this object represents.
35+
Servers may infer this from the endpoint the client submits requests to.
36+
Cannot be updated.
37+
In CamelCase.
38+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
39+
type: string
40+
metadata:
41+
type: object
42+
permissionClaims:
43+
description: PermissionClaims are additional permissions that the user
44+
wants to have.
45+
items:
46+
description: |-
47+
permissionClaim selects objects of a GVR that a service provider may
48+
request and that a consumer may accept and allow the service provider access to.
49+
properties:
50+
group:
51+
default: ""
52+
description: |-
53+
group is the name of an API group.
54+
For core groups this is the empty string '""'.
55+
pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$
56+
type: string
57+
resource:
58+
description: |-
59+
resource is the name of the resource.
60+
Note: it is worth noting that you can not ask for permissions for resource provided by a CRD
61+
not provided by an service binding export.
62+
pattern: ^[a-z][-a-z0-9]*[a-z0-9]$
63+
type: string
64+
selector:
65+
description: Selector is a resource selector that selects objects
66+
of a GVR.
67+
properties:
68+
all:
69+
description: |-
70+
all claims all resources for the given group/resource.
71+
This is mutually exclusive with resourceSelector.
72+
type: boolean
73+
labelSelector:
74+
description: LabelSelector is a label selector that selects
75+
objects of a GVR.
76+
properties:
77+
matchExpressions:
78+
description: matchExpressions is a list of label selector
79+
requirements. The requirements are ANDed.
80+
items:
81+
description: |-
82+
A label selector requirement is a selector that contains values, a key, and an operator that
83+
relates the key and values.
84+
properties:
85+
key:
86+
description: key is the label key that the selector
87+
applies to.
88+
type: string
89+
operator:
90+
description: |-
91+
operator represents a key's relationship to a set of values.
92+
Valid operators are In, NotIn, Exists and DoesNotExist.
93+
type: string
94+
values:
95+
description: |-
96+
values is an array of string values. If the operator is In or NotIn,
97+
the values array must be non-empty. If the operator is Exists or DoesNotExist,
98+
the values array must be empty. This array is replaced during a strategic
99+
merge patch.
100+
items:
101+
type: string
102+
type: array
103+
x-kubernetes-list-type: atomic
104+
required:
105+
- key
106+
- operator
107+
type: object
108+
type: array
109+
x-kubernetes-list-type: atomic
110+
matchLabels:
111+
additionalProperties:
112+
type: string
113+
description: |-
114+
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
115+
map is equivalent to an element of matchExpressions, whose key field is "key", the
116+
operator is "In", and the values array contains only "value". The requirements are ANDed.
117+
type: object
118+
type: object
119+
x-kubernetes-map-type: atomic
120+
type: object
121+
required:
122+
- resource
123+
type: object
124+
type: array
125+
resources:
126+
description: resources is a list of resources that the user can select
127+
from.
128+
items:
129+
description: BindableResource describes a resource that the user can
130+
select to bind to.
131+
properties:
132+
apiVersion:
133+
description: apiVersion is the API version of the resource.
134+
type: string
135+
description:
136+
description: description is a human friendly description of the
137+
resource.
138+
type: string
139+
group:
140+
description: group is the API group of the resource.
141+
type: string
142+
kind:
143+
description: kind is the kind of the resource.
144+
type: string
145+
name:
146+
description: name is the name of the resource.
147+
type: string
148+
resource:
149+
description: resource is the plural name of the resource.
150+
type: string
151+
scope:
152+
description: scope is the scope of the resource, either "Cluster"
153+
or "Namespaced".
154+
type: string
155+
sessionID:
156+
description: |-
157+
sessionID is a session ID that the consumer must pass back to the service provider
158+
during the binding step. If multiple backends are aggregated, this can be used to
159+
to authenticate the user to the correct backend.
160+
type: string
161+
required:
162+
- apiVersion
163+
- group
164+
- kind
165+
- name
166+
- resource
167+
- scope
168+
- sessionID
169+
type: object
170+
minItems: 1
171+
type: array
172+
required:
173+
- resources
174+
type: object
175+
served: true
176+
storage: true

deploy/crd/kube-bind.io_boundschemas.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ spec:
2626
name: v1alpha2
2727
schema:
2828
openAPIV3Schema:
29-
description: BoundAPIResourceSchema
29+
description: BoundSchema
3030
properties:
3131
apiVersion:
3232
description: |-

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,14 @@ spec:
6767
schema: v250809-5ed76a1.apiservicenamespaces.kube-bind.io
6868
storage:
6969
crd: {}
70+
- group: kube-bind.io
71+
name: bindableresourcesrequests
72+
schema: v250917-8ab6385.bindableresourcesrequests.kube-bind.io
73+
storage:
74+
crd: {}
7075
- group: kube-bind.io
7176
name: boundschemas
72-
schema: v250905-71082b5.boundschemas.kube-bind.io
77+
schema: v250917-8ab6385.boundschemas.kube-bind.io
7378
storage:
7479
crd: {}
7580
- group: kube-bind.io

0 commit comments

Comments
 (0)