Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,14 @@ func (r *ClusterBindingReconciler) SetupWithManager(mgr mcmanager.Manager) error
Owns(&rbacv1.RoleBinding{}).
Watches(
&kubebindv1alpha2.APIServiceExport{},
mapCRD,
mapAPIResourceSchema,
).
Named(controllerName).
Complete(r)
}

func mapCRD(clusterName string, cl cluster.Cluster) handler.TypedEventHandler[client.Object, mcreconcile.Request] {
return handler.TypedEnqueueRequestsFromMapFunc[client.Object, mcreconcile.Request](func(ctx context.Context, obj client.Object) []mcreconcile.Request {
func mapAPIResourceSchema(clusterName string, cl cluster.Cluster) handler.TypedEventHandler[client.Object, mcreconcile.Request] {
return handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []mcreconcile.Request {
serviceExport, ok := obj.(*kubebindv1alpha2.APIServiceExport)
if !ok {
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"fmt"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -67,25 +66,23 @@ func NewAPIServiceExportReconciler(
if err != nil {
return nil, err
}
if err := mgr.GetFieldIndexer().IndexField(ctx, &kubebindv1alpha2.APIServiceExport{}, indexers.ServiceExportByCustomResourceDefinition,
indexers.IndexServiceExportByCustomResourceDefinitionControllerRuntime); err != nil {
return nil, fmt.Errorf("failed to setup ServiceExportByCustomResourceDefinition indexer: %w", err)

if err := mgr.GetFieldIndexer().IndexField(ctx, &kubebindv1alpha2.APIServiceExport{}, indexers.ServiceExportByAPIResourceSchema,
indexers.IndexServiceExportByAPIResourceSchema); err != nil {
return nil, fmt.Errorf("failed to setup ServiceExportByAPIResourceSchema indexer: %w", err)
}

r := &APIServiceExportReconciler{
manager: mgr,
bindClient: bindClient,
reconciler: reconciler{
getCRD: func(ctx context.Context, cache cache.Cache, name string) (*apiextensionsv1.CustomResourceDefinition, error) {
var crd apiextensionsv1.CustomResourceDefinition
getAPIResourceSchema: func(ctx context.Context, cache cache.Cache, name string) (*kubebindv1alpha2.APIResourceSchema, error) {
var schema kubebindv1alpha2.APIResourceSchema
key := types.NamespacedName{Name: name}
if err := cache.Get(ctx, key, &crd); err != nil {
if err := cache.Get(ctx, key, &schema); err != nil {
return nil, err
}
return &crd, nil
},
getAPIResourceSchema: func(ctx context.Context, name string) (*kubebindv1alpha2.APIResourceSchema, error) {
return bindClient.KubeBindV1alpha2().APIResourceSchemas().Get(ctx, name, metav1.GetOptions{})
return &schema, nil
},
deleteServiceExport: func(ctx context.Context, ns, name string) error {
return bindClient.KubeBindV1alpha2().APIServiceExports(ns).Delete(ctx, name, metav1.DeleteOptions{})
Expand All @@ -99,7 +96,6 @@ func NewAPIServiceExportReconciler(
//+kubebuilder:rbac:groups=kubebind.k8s.io,resources=apiserviceexports,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=kubebind.k8s.io,resources=apiserviceexports/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=kubebind.k8s.io,resources=apiserviceexports/finalizers,verbs=update
//+kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
Expand Down Expand Up @@ -148,15 +144,15 @@ func (r *APIServiceExportReconciler) Reconcile(ctx context.Context, req mcreconc
return ctrl.Result{}, nil
}

// getCRDMapper returns a mapper function that uses the manager to find related APIServiceExports.
func getCRDMapper(clusterName string, cl cluster.Cluster) handler.TypedEventHandler[client.Object, mcreconcile.Request] {
return handler.TypedEnqueueRequestsFromMapFunc[client.Object, mcreconcile.Request](func(ctx context.Context, obj client.Object) []mcreconcile.Request {
crd := obj.(*apiextensionsv1.CustomResourceDefinition)
crdKey := crd.Name
// getAPIResourceSchemaMapper returns a mapper function that uses the manager to find related APIServiceExports.
func getAPIResourceSchemaMapper(clusterName string, cl cluster.Cluster) handler.TypedEventHandler[client.Object, mcreconcile.Request] {
return handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []mcreconcile.Request {
apiResourceSchema := obj.(*kubebindv1alpha2.APIResourceSchema)
apiResourceSchemaKey := apiResourceSchema.Name
c := cl.GetClient()

var exports kubebindv1alpha2.APIServiceExportList
if err := c.List(ctx, &exports, client.MatchingFields{indexers.ServiceExportByCustomResourceDefinition: crdKey}); err != nil {
if err := c.List(ctx, &exports, client.MatchingFields{indexers.ServiceExportByAPIResourceSchema: apiResourceSchemaKey}); err != nil {
return []mcreconcile.Request{}
}

Expand All @@ -179,8 +175,8 @@ func (r *APIServiceExportReconciler) SetupWithManager(mgr mcmanager.Manager) err
return mcbuilder.ControllerManagedBy(mgr).
For(&kubebindv1alpha2.APIServiceExport{}).
Watches(
&apiextensionsv1.CustomResourceDefinition{},
getCRDMapper,
&kubebindv1alpha2.APIResourceSchema{},
getAPIResourceSchemaMapper,
).
Named(controllerName).
Complete(r)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"slices"
"sort"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/klog/v2"
Expand All @@ -35,15 +34,14 @@ import (
)

type reconciler struct {
getCRD func(ctx context.Context, cache cache.Cache, name string) (*apiextensionsv1.CustomResourceDefinition, error)
getAPIResourceSchema func(ctx context.Context, name string) (*kubebindv1alpha2.APIResourceSchema, error)
getAPIResourceSchema func(ctx context.Context, cache cache.Cache, name string) (*kubebindv1alpha2.APIResourceSchema, error)
deleteServiceExport func(ctx context.Context, namespace, name string) error
}

func (r *reconciler) reconcile(ctx context.Context, cache cache.Cache, export *kubebindv1alpha2.APIServiceExport) error {
var errs []error

if specChanged, err := r.ensureSchema(ctx, export); err != nil {
if specChanged, err := r.ensureSchema(ctx, cache, export); err != nil {
errs = append(errs, err)
} else if specChanged {
// TODO: This should be separate controller for apiresourceschemas.
Expand All @@ -55,7 +53,7 @@ func (r *reconciler) reconcile(ctx context.Context, cache cache.Cache, export *k
return utilerrors.NewAggregate(errs)
}

func (r *reconciler) ensureSchema(ctx context.Context, export *kubebindv1alpha2.APIServiceExport) (specChanged bool, err error) {
func (r *reconciler) ensureSchema(ctx context.Context, cache cache.Cache, export *kubebindv1alpha2.APIServiceExport) (specChanged bool, err error) {
logger := klog.FromContext(ctx)
leafHashes := make([]string, 0, len(export.Spec.Resources))
for _, resourceRef := range export.Spec.Resources {
Expand All @@ -64,7 +62,7 @@ func (r *reconciler) ensureSchema(ctx context.Context, export *kubebindv1alpha2.
continue
}

schema, err := r.getAPIResourceSchema(ctx, resourceRef.Name)
schema, err := r.getAPIResourceSchema(ctx, cache, resourceRef.Name)
if err != nil {
if errors.IsNotFound(err) {
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"fmt"
"reflect"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -80,12 +79,12 @@ func NewAPIServiceExportRequestReconciler(

// Set up field indexers for APIServiceExportRequests
if err := mgr.GetFieldIndexer().IndexField(ctx, &kubebindv1alpha2.APIServiceExportRequest{}, indexers.ServiceExportRequestByServiceExport,
indexers.IndexServiceExportRequestByServiceExportControllerRuntime); err != nil {
indexers.IndexServiceExportRequestByServiceExport); err != nil {
return nil, fmt.Errorf("failed to setup ServiceExportRequestByServiceExport indexer: %w", err)
}

if err := mgr.GetFieldIndexer().IndexField(ctx, &kubebindv1alpha2.APIServiceExportRequest{}, indexers.ServiceExportRequestByGroupResource,
indexers.IndexServiceExportRequestByGroupResourceControllerRuntime); err != nil {
indexers.IndexServiceExportRequestByGroupResource); err != nil {
return nil, fmt.Errorf("failed to setup ServiceExportRequestByGroupResource indexer: %w", err)
}

Expand All @@ -98,14 +97,6 @@ func NewAPIServiceExportRequestReconciler(
reconciler: reconciler{
informerScope: scope,
clusterScopedIsolation: isolation,
getCRD: func(ctx context.Context, cache cache.Cache, name string) (*apiextensionsv1.CustomResourceDefinition, error) {
var crd apiextensionsv1.CustomResourceDefinition
key := types.NamespacedName{Name: name}
if err := cache.Get(ctx, key, &crd); err != nil {
return nil, err
}
return &crd, nil
},
getAPIResourceSchema: func(ctx context.Context, cache cache.Cache, name string) (*kubebindv1alpha2.APIResourceSchema, error) {
var schema kubebindv1alpha2.APIResourceSchema
key := types.NamespacedName{Name: name}
Expand Down Expand Up @@ -138,9 +129,8 @@ func NewAPIServiceExportRequestReconciler(
}

// getServiceExportRequestMapper creates a mapping function for ServiceExport changes.

func getServiceExportRequestMapper(clusterName string, cl cluster.Cluster) handler.TypedEventHandler[client.Object, mcreconcile.Request] {
return handler.TypedEnqueueRequestsFromMapFunc[client.Object, mcreconcile.Request](func(ctx context.Context, obj client.Object) []mcreconcile.Request {
return handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []mcreconcile.Request {
serviceExport := obj.(*kubebindv1alpha2.APIServiceExport)
seKey := serviceExport.Namespace + "/" + serviceExport.Name

Expand Down Expand Up @@ -168,16 +158,15 @@ func getServiceExportRequestMapper(clusterName string, cl cluster.Cluster) handl
})
}

// getCRDMapper creates a mapping function for CRD changes.
func getCRDMapper(clusterName string, cl cluster.Cluster) handler.TypedEventHandler[client.Object, mcreconcile.Request] {
return handler.TypedEnqueueRequestsFromMapFunc[client.Object, mcreconcile.Request](func(ctx context.Context, obj client.Object) []mcreconcile.Request {
crd := obj.(*apiextensionsv1.CustomResourceDefinition)
crdKey := crd.Name // CRDs are cluster-scoped

// getAPIResourceSchemaMapper creates a mapping function for APIResourceSchema changes.
func getAPIResourceSchemaMapper(clusterName string, cl cluster.Cluster) handler.TypedEventHandler[client.Object, mcreconcile.Request] {
return handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []mcreconcile.Request {
apiResourceSchema := obj.(*kubebindv1alpha2.APIResourceSchema)
apiResourceSchemaKey := apiResourceSchema.Name
c := cl.GetClient()

var requests kubebindv1alpha2.APIServiceExportRequestList
if err := c.List(ctx, &requests, client.MatchingFields{indexers.ServiceExportRequestByGroupResource: crdKey}); err != nil {
if err := c.List(ctx, &requests, client.MatchingFields{indexers.ServiceExportRequestByGroupResource: apiResourceSchemaKey}); err != nil {
return []mcreconcile.Request{}
}

Expand All @@ -202,7 +191,6 @@ func getCRDMapper(clusterName string, cl cluster.Cluster) handler.TypedEventHand
//+kubebuilder:rbac:groups=kubebind.k8s.io,resources=apiserviceexportrequests/finalizers,verbs=update
//+kubebuilder:rbac:groups=kubebind.k8s.io,resources=apiserviceexports,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=kubebind.k8s.io,resources=apiresourceschemas,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
Expand Down Expand Up @@ -261,8 +249,8 @@ func (r *APIServiceExportRequestReconciler) SetupWithManager(mgr mcmanager.Manag
getServiceExportRequestMapper,
).
Watches(
&apiextensionsv1.CustomResourceDefinition{},
getCRDMapper,
&kubebindv1alpha2.APIResourceSchema{},
getAPIResourceSchemaMapper,
).
Named(controllerName).
Complete(r)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ type reconciler struct {
informerScope kubebindv1alpha2.InformerScope
clusterScopedIsolation kubebindv1alpha2.Isolation

getCRD func(ctx context.Context, cache cache.Cache, name string) (*apiextensionsv1.CustomResourceDefinition, error)
getAPIResourceSchema func(ctx context.Context, cache cache.Cache, name string) (*kubebindv1alpha2.APIResourceSchema, error)
getServiceExport func(ctx context.Context, cache cache.Cache, ns, name string) (*kubebindv1alpha2.APIServiceExport, error)
createServiceExport func(ctx context.Context, resource *kubebindv1alpha2.APIServiceExport) (*kubebindv1alpha2.APIServiceExport, error)
Expand All @@ -61,42 +60,21 @@ func (r *reconciler) ensureExports(ctx context.Context, cache cache.Cache, req *
logger := klog.FromContext(ctx)

if req.Status.Phase == kubebindv1alpha2.APIServiceExportRequestPhasePending {
failure := false
for _, res := range req.Spec.Resources {
// backend is created using CRD's as backup. But this is not required.
name := res.Resource + "." + res.Group

apiResourceSchema, err := r.getAPIResourceSchema(ctx, cache, name)
switch {
case apierrors.IsNotFound(err):
logger.V(1).Info("APIResourceSchema not found, continuing with fallback to CRD conversion to APIResourceSchema", "name", name)
crd, err := r.getCRD(ctx, cache, name)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
if err != nil {
if apierrors.IsNotFound(err) {
conditions.MarkFalse(
req,
kubebindv1alpha2.APIServiceExportRequestConditionExportsReady,
"CRDNotFound",
"APIResourceSchemaNotFound",
conditionsapi.ConditionSeverityError,
"CustomResourceDefinition %s in the service provider cluster not found",
"APIResourceSchema %s in the service provider cluster not found",
name,
)
failure = true
break
}
schema, err := helpers.CRDToAPIResourceSchema(crd, "")
if err != nil {
return err
}
schema.Namespace = req.Namespace

logger.V(1).Info("Creating APIResourceSchema", "name", schema.Name, "namespace", schema.Namespace)
if apiResourceSchema, err = r.createAPIResourceSchema(ctx, schema); err != nil {
return err
}
case err != nil:
return err
}

Expand Down Expand Up @@ -135,11 +113,8 @@ func (r *reconciler) ensureExports(ctx context.Context, cache cache.Cache, req *
}
}

if !failure {
conditions.MarkTrue(req, kubebindv1alpha2.APIServiceExportRequestConditionExportsReady)
req.Status.Phase = kubebindv1alpha2.APIServiceExportRequestPhaseSucceeded
return nil
}
conditions.MarkTrue(req, kubebindv1alpha2.APIServiceExportRequestConditionExportsReady)
req.Status.Phase = kubebindv1alpha2.APIServiceExportRequestPhaseSucceeded

if time.Since(req.CreationTimestamp.Time) > time.Minute {
req.Status.Phase = kubebindv1alpha2.APIServiceExportRequestPhaseFailed
Expand Down
1 change: 0 additions & 1 deletion deploy/examples/crd-mangodb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@ spec:
- spec
subresources:
status: {}

2 changes: 1 addition & 1 deletion deploy/examples/servicenamespace-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ apiVersion: kube-bind.io/v1alpha1
kind: APIServiceNamespace
metadata:
name: example
spec: {}
spec: {}
6 changes: 3 additions & 3 deletions pkg/indexers/serviceexport.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

const (
ServiceExportByCustomResourceDefinition = "serviceExportByCustomResourceDefinition"
ServiceExportByAPIResourceSchema = "ServiceExportByAPIResourceSchema"
)

func IndexServiceExportByCustomResourceDefinition(obj any) ([]string, error) {
Expand All @@ -35,9 +36,8 @@ func IndexServiceExportByCustomResourceDefinition(obj any) ([]string, error) {
return []string{export.Name}, nil
}

// IndexServiceExportByCustomResourceDefinitionControllerRuntime is a controller-runtime compatible indexer function
// that indexes APIServiceExports by their CustomResourceDefinition name.
func IndexServiceExportByCustomResourceDefinitionControllerRuntime(obj client.Object) []string {
// IndexServiceExportByAPIResourceSchema is a controller-runtime compatible indexer function.
func IndexServiceExportByAPIResourceSchema(obj client.Object) []string {
export, ok := obj.(*v1alpha2.APIServiceExport)
if !ok {
return nil
Expand Down
32 changes: 4 additions & 28 deletions pkg/indexers/serviceexportrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,9 @@ const (
ServiceExportRequestByServiceExport = "ServiceExportRequestByServiceExport"
)

func IndexServiceExportRequestByGroupResource(obj any) ([]string, error) {
apiServiceExportRequest, ok := obj.(*kubebindv1alpha2.APIServiceExportRequest)
if !ok {
return nil, nil
}
keys := []string{}
for _, gr := range apiServiceExportRequest.Spec.Resources {
keys = append(keys, gr.Resource+"."+gr.Group)
}
return keys, nil
}

// IndexServiceExportRequestByGroupResourceControllerRuntime is a controller-runtime compatible indexer function
// IndexServiceExportRequestByGroupResource is a controller-runtime compatible indexer function
// that indexes APIServiceExportRequests by their Group.Resource name.
func IndexServiceExportRequestByGroupResourceControllerRuntime(obj client.Object) []string {
func IndexServiceExportRequestByGroupResource(obj client.Object) []string {
apiServiceExportRequest, ok := obj.(*kubebindv1alpha2.APIServiceExportRequest)
if !ok {
return nil
Expand All @@ -53,21 +41,9 @@ func IndexServiceExportRequestByGroupResourceControllerRuntime(obj client.Object
return keys
}

func IndexServiceExportRequestByServiceExport(obj any) ([]string, error) {
apiServiceExportRequest, ok := obj.(*kubebindv1alpha2.APIServiceExportRequest)
if !ok {
return nil, nil
}
keys := []string{}
for _, gr := range apiServiceExportRequest.Spec.Resources {
keys = append(keys, apiServiceExportRequest.Namespace+"/"+gr.Resource+"."+gr.Group)
}
return keys, nil
}

// IndexServiceExportRequestByServiceExportControllerRuntime is a controller-runtime compatible indexer function
// IndexServiceExportRequestByServiceExport is a controller-runtime compatible indexer function
// that indexes APIServiceExportRequests by their related ServiceExport name.
func IndexServiceExportRequestByServiceExportControllerRuntime(obj client.Object) []string {
func IndexServiceExportRequestByServiceExport(obj client.Object) []string {
apiServiceExportRequest, ok := obj.(*kubebindv1alpha2.APIServiceExportRequest)
if !ok {
return nil
Expand Down
Loading