Skip to content
Open
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 @@ -273,7 +273,7 @@ func (r *reconciler) reconcile(ctx context.Context, clusterName string, cl clien
req.Status.Namespace = result.Namespace

// Create or update the BindingResourceResponse secret
if err := r.ensureBindingResponseSecret(ctx, cl, req, result.Kubeconfig, secretName, secretKey); err != nil {
if err := r.ensureBindingResponseSecret(ctx, cl, req, result, secretName, secretKey); err != nil {
meta.SetStatusCondition(&req.Status.Conditions, metav1.Condition{
Type: string(kubebindv1alpha2.BindableResourcesRequestConditionReady),
Status: metav1.ConditionFalse,
Expand Down Expand Up @@ -306,25 +306,26 @@ func (r *reconciler) reconcile(ctx context.Context, clusterName string, cl clien
return ctrl.Result{}, nil
}

// ensureBindingResponseSecret creates or updates a secret containing the BindingResourceResponse
// with only the kubeconfig set (no authentication or requests).
// with only the kubeconfig, providerID, and providerNamespace set (no authentication or requests).
func (r *reconciler) ensureBindingResponseSecret(
ctx context.Context,
cl client.Client,
req *kubebindv1alpha2.BindableResourcesRequest,
kubeconfig []byte,
result *kubernetes.HandleResourcesResult,
secretName string,
secretKey string,
) error {
logger := log.FromContext(ctx)

// Create the BindingResourceResponse with only kubeconfig
// Create the BindingResourceResponse
response := kubebindv1alpha2.BindingResourceResponse{
TypeMeta: metav1.TypeMeta{
APIVersion: kubebindv1alpha2.SchemeGroupVersion.String(),
Kind: "BindingResourceResponse",
},
Kubeconfig: kubeconfig,
Kubeconfig: result.Kubeconfig,
ProviderNamespace: result.Namespace,
ProviderID: result.ProviderID,
}

responseBytes, err := json.Marshal(&response)
Expand Down
1 change: 1 addition & 0 deletions backend/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) {
Kubeconfig: handleResult.Kubeconfig,
Requests: []runtime.RawExtension{{Raw: requestBytes}},
ProviderNamespace: handleResult.Namespace,
ProviderID: handleResult.ProviderID,
BindingName: exportRequestName,
}

Expand Down
8 changes: 8 additions & 0 deletions backend/kubernetes/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ type HandleResourcesResult struct {
Kubeconfig []byte
// Namespace is the namespace assigned to this binding on the service provider cluster.
Namespace string
// ProviderID is the UID of the kube-system namespace of the provider cluster.
ProviderID string
}

func (m *Manager) HandleResources(
Expand Down Expand Up @@ -181,9 +183,15 @@ func (m *Manager) HandleResources(
return nil, err
}

var kubeSystemNS corev1.Namespace
if err := c.Get(ctx, types.NamespacedName{Name: "kube-system"}, &kubeSystemNS); err != nil {
return nil, fmt.Errorf("failed to get kube-system namespace for ProviderID: %w", err)
}

return &HandleResourcesResult{
Kubeconfig: kfgSecret.Data["kubeconfig"],
Namespace: ns,
ProviderID: string(kubeSystemNS.UID),
}, nil
}

Expand Down
8 changes: 4 additions & 4 deletions cli/pkg/kubectl/bind-apiservice/plugin/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (b *Binder) BindFromFile(ctx context.Context) ([]*kubebindv1alpha2.APIServi
return nil, fmt.Errorf("failed to create kubeconfig secret: %w", err)
}

results, err := b.createAPIServiceBindings(ctx, result, secretName)
results, err := b.createAPIServiceBindings(ctx, result, secretName, "", false)
if err != nil {
return nil, fmt.Errorf("failed to create API service bindings: %w", err)
}
Expand Down Expand Up @@ -247,7 +247,7 @@ func (b *Binder) BindFromResponse(ctx context.Context, response *kubebindv1alpha
return nil, fmt.Errorf("failed to create kubeconfig secret: %w", err)
}

results, err := b.createAPIServiceBindings(ctx, result, secretName)
results, err := b.createAPIServiceBindings(ctx, result, secretName, response.ProviderID, true)
if err != nil {
return nil, fmt.Errorf("failed to create API service bindings: %w", err)
}
Expand Down Expand Up @@ -304,11 +304,11 @@ func (b *Binder) createKubeconfigSecret(ctx context.Context, remoteHost, remoteN
return tempOpts.createKubeconfigSecret(ctx, b.config, remoteHost, remoteNamespace, remoteKubeconfig)
}

func (b *Binder) createAPIServiceBindings(ctx context.Context, request *kubebindv1alpha2.APIServiceExportRequest, secretName string) ([]*kubebindv1alpha2.APIServiceBinding, error) {
func (b *Binder) createAPIServiceBindings(ctx context.Context, request *kubebindv1alpha2.APIServiceExportRequest, secretName string, providerID string, isDefault bool) ([]*kubebindv1alpha2.APIServiceBinding, error) {
tempOpts := &BindAPIServiceOptions{
Options: &base.Options{IOStreams: b.opts.IOStreams},
}
return tempOpts.createAPIServiceBindings(ctx, b.config, request, secretName)
return tempOpts.createAPIServiceBindings(ctx, b.config, request, secretName, providerID, isDefault)
}

func (b *Binder) getRequestManifest() ([]byte, error) {
Expand Down
4 changes: 3 additions & 1 deletion cli/pkg/kubectl/bind-apiservice/plugin/servicebindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (
bindclient "github.com/kube-bind/kube-bind/sdk/client/clientset/versioned"
)

func (b *BindAPIServiceOptions) createAPIServiceBindings(ctx context.Context, config *rest.Config, request *kubebindv1alpha2.APIServiceExportRequest, secretName string) ([]*kubebindv1alpha2.APIServiceBinding, error) {
func (b *BindAPIServiceOptions) createAPIServiceBindings(ctx context.Context, config *rest.Config, request *kubebindv1alpha2.APIServiceExportRequest, secretName string, providerID string, isDefault bool) ([]*kubebindv1alpha2.APIServiceBinding, error) {
bindClient, err := bindclient.NewForConfig(config)
if err != nil {
return nil, err
Expand Down Expand Up @@ -86,6 +86,8 @@ func (b *BindAPIServiceOptions) createAPIServiceBindings(ctx context.Context, co
},
Namespace: "kube-bind",
},
ProviderID: providerID,
IsDefault: isDefault,
},
}, metav1.CreateOptions{})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ spec:
crd: {}
- group: kube-bind.io
name: apiservicebindings
schema: v260122-c9f0f376.apiservicebindings.kube-bind.io
schema: v260716-5d02230.apiservicebindings.kube-bind.io
storage:
crd: {}
- group: kube-bind.io
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: apis.kcp.io/v1alpha1
kind: APIResourceSchema
metadata:
name: v260122-c9f0f376.apiservicebindings.kube-bind.io
name: v260716-5d02230.apiservicebindings.kube-bind.io
spec:
conversion:
strategy: None
Expand Down Expand Up @@ -196,6 +196,11 @@ spec:
spec specifies how an API service from a service provider should be bound in the
local consumer cluster.
properties:
isDefault:
description: |-
isDefault indicates whether this binding should be used as the default routing
destination for Custom Resources that do not specify a provider annotation.
type: boolean
kubeconfigSecretRef:
description: kubeconfigSecretName is the secret ref that contains the
kubeconfig of the service cluster.
Expand All @@ -219,6 +224,10 @@ spec:
x-kubernetes-validations:
- message: kubeconfigSecretRef is immutable
rule: self == oldSelf
providerID:
description: providerID is a stable and unique identifier for the provider
cluster.
type: string
required:
- kubeconfigSecretRef
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ spec:
spec specifies how an API service from a service provider should be bound in the
local consumer cluster.
properties:
isDefault:
description: |-
isDefault indicates whether this binding should be used as the default routing
destination for Custom Resources that do not specify a provider annotation.
type: boolean
kubeconfigSecretRef:
description: kubeconfigSecretName is the secret ref that contains
the kubeconfig of the service cluster.
Expand All @@ -224,6 +229,10 @@ spec:
x-kubernetes-validations:
- message: kubeconfigSecretRef is immutable
rule: self == oldSelf
providerID:
description: providerID is a stable and unique identifier for the
provider cluster.
type: string
required:
- kubeconfigSecretRef
type: object
Expand Down
9 changes: 9 additions & 0 deletions deploy/crd/kube-bind.io_apiservicebindings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ spec:
spec specifies how an API service from a service provider should be bound in the
local consumer cluster.
properties:
isDefault:
description: |-
isDefault indicates whether this binding should be used as the default routing
destination for Custom Resources that do not specify a provider annotation.
type: boolean
kubeconfigSecretRef:
description: kubeconfigSecretName is the secret ref that contains
the kubeconfig of the service cluster.
Expand All @@ -225,6 +230,10 @@ spec:
x-kubernetes-validations:
- message: kubeconfigSecretRef is immutable
rule: self == oldSelf
providerID:
description: providerID is a stable and unique identifier for the
provider cluster.
type: string
required:
- kubeconfigSecretRef
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (r *reconciler) ensureControllers(ctx context.Context, namespace, name stri
}

// Start/update controller for this schema
if err := r.ensureControllerForSchema(ctx, export, schema); err != nil {
if err := r.ensureControllerForSchema(ctx, export, binding, schema); err != nil {
errs = append(errs, err)
}

Expand Down Expand Up @@ -170,7 +170,7 @@ func (r *reconciler) ensureControllers(ctx context.Context, namespace, name stri
return utilerrors.NewAggregate(errs)
}

func (r *reconciler) ensureControllerForSchema(ctx context.Context, export *kubebindv1alpha2.APIServiceExport, schema *kubebindv1alpha2.BoundSchema) error {
func (r *reconciler) ensureControllerForSchema(ctx context.Context, export *kubebindv1alpha2.APIServiceExport, binding *kubebindv1alpha2.APIServiceBinding, schema *kubebindv1alpha2.BoundSchema) error {
logger := klog.FromContext(ctx)
key := contextstore.NewKey(export.Namespace, export.Name, schema.Name)

Expand Down Expand Up @@ -296,6 +296,8 @@ func (r *reconciler) ensureControllerForSchema(ctx context.Context, export *kube
consumerInf.ForResource(gvr),
providerInf,
r.serviceNamespaceInformer,
binding.Spec.ProviderID,
binding.Spec.IsDefault,
)
if err != nil {
runtime.HandleError(err)
Expand All @@ -312,6 +314,8 @@ func (r *reconciler) ensureControllerForSchema(ctx context.Context, export *kube
consumerInf.ForResource(gvr),
providerInf,
r.serviceNamespaceInformer,
binding.Spec.ProviderID,
binding.Spec.IsDefault,
)
if err != nil {
runtime.HandleError(err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ func NewController(
consumerDynamicInformer informers.GenericInformer,
providerDynamicInformer multinsinformer.GetterInformer,
serviceNamespaceInformer dynamic.Informer[bindlisters.APIServiceNamespaceLister],
providerID string,
isDefault bool,
) (*controller, error) {
queue := workqueue.NewTypedRateLimitingQueueWithConfig(workqueue.DefaultTypedControllerRateLimiter[string](), workqueue.TypedRateLimitingQueueConfig[string]{Name: controllerName})

Expand Down Expand Up @@ -150,12 +152,21 @@ func NewController(

if _, err := consumerDynamicInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj any) {
if !shouldProcess(obj, providerID, isDefault) {
return
}
c.enqueueConsumer(logger, obj)
},
UpdateFunc: func(_, newObj any) {
if !shouldProcess(newObj, providerID, isDefault) {
return
}
c.enqueueConsumer(logger, newObj)
},
DeleteFunc: func(obj any) {
if !shouldProcess(obj, providerID, isDefault) {
return
}
c.enqueueConsumer(logger, obj)
},
}); err != nil {
Expand Down Expand Up @@ -206,7 +217,31 @@ func (c *controller) enqueueConsumer(logger klog.Logger, obj any) {
}

logger.V(2).Info("queueing Unstructured", "queued", key, "reason", "consumerObject")
c.queue.Add(key)
}

func shouldProcess(obj any, providerID string, isDefault bool) bool {
u, ok := obj.(*unstructured.Unstructured)
if !ok {
// Try DeletedFinalStateUnknown
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
return false
}
u, ok = tombstone.Obj.(*unstructured.Unstructured)
if !ok {
return false
}
}

annotations := u.GetAnnotations()
if annotations == nil {
return isDefault
}
val, ok := annotations["provider.kube-bind.io/provider-id"]
if !ok || val == "" {
return isDefault
}
return val == providerID
}

func (c *controller) enqueueProvider(logger klog.Logger, obj any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ func NewController(
consumerDynamicInformer informers.GenericInformer,
providerDynamicInformer multinsinformer.GetterInformer,
serviceNamespaceInformer dynamic.Informer[bindlisters.APIServiceNamespaceLister],
providerID string,
isDefault bool,
) (*controller, error) {
queue := workqueue.NewTypedRateLimitingQueueWithConfig(workqueue.DefaultTypedControllerRateLimiter[string](), workqueue.TypedRateLimitingQueueConfig[string]{Name: controllerName})

Expand Down Expand Up @@ -124,12 +126,21 @@ func NewController(

if _, err := consumerDynamicInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj any) {
if !shouldProcess(obj, providerID, isDefault) {
return
}
c.enqueueConsumer(logger, obj)
},
UpdateFunc: func(_, newObj any) {
if !shouldProcess(newObj, providerID, isDefault) {
return
}
c.enqueueConsumer(logger, newObj)
},
DeleteFunc: func(obj any) {
if !shouldProcess(obj, providerID, isDefault) {
return
}
c.enqueueConsumer(logger, obj)
},
}); err != nil {
Expand Down Expand Up @@ -204,7 +215,31 @@ func (c *controller) enqueueProvider(logger klog.Logger, obj any) {
}

logger.V(2).Info("queueing Unstructured", "queued", key)
c.queue.Add(key)
}

func shouldProcess(obj any, providerID string, isDefault bool) bool {
u, ok := obj.(*unstructured.Unstructured)
if !ok {
// Try DeletedFinalStateUnknown
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
return false
}
u, ok = tombstone.Obj.(*unstructured.Unstructured)
if !ok {
return false
}
}

annotations := u.GetAnnotations()
if annotations == nil {
return isDefault
}
val, ok := annotations["provider.kube-bind.io/provider-id"]
if !ok || val == "" {
return isDefault
}
return val == providerID
}

func (c *controller) enqueueConsumer(logger klog.Logger, obj any) {
Expand Down
Loading
Loading