@@ -19,6 +19,8 @@ package kubernetes
1919import (
2020 "context"
2121 "fmt"
22+ "net"
23+ "net/url"
2224 "strings"
2325 "time"
2426
@@ -475,6 +477,9 @@ func (m *Manager) ApplyToConsumer(
475477 if err := kubebindv1alpha2 .AddToScheme (s ); err != nil {
476478 return nil , fmt .Errorf ("failed to add kube-bind scheme: %w" , err )
477479 }
480+ if err := apiextensionsv1 .AddToScheme (s ); err != nil {
481+ return nil , fmt .Errorf ("failed to add apiextensions scheme: %w" , err )
482+ }
478483
479484 consumerClient , err := client .New (consumerConfig , client.Options {Scheme : s })
480485 if err != nil {
@@ -489,13 +494,26 @@ func (m *Manager) ApplyToConsumer(
489494 return nil , fmt .Errorf ("failed to create kube-bind namespace: %w" , err )
490495 }
491496
492- // 2. Deploy konnector (idempotent)
493- konnectorDeployed , err := m .ensureKonnector (ctx , consumerClient , konnectorImage )
497+ // 2. Resolve host aliases from the provider kubeconfig so the konnector
498+ // can reach the provider API server (needed in Kind/Docker environments
499+ // where the hostname resolves to localhost on the host but not in pods).
500+ hostAliases := m .resolveProviderHostAliases (ctx , providerKubeconfigData )
501+
502+ // 3. Deploy konnector (idempotent)
503+ konnectorDeployed , err := m .ensureKonnector (ctx , consumerClient , konnectorImage , hostAliases )
494504 if err != nil {
495505 return nil , fmt .Errorf ("failed to deploy konnector: %w" , err )
496506 }
497507
498- // 3. Create kubeconfig secret for the provider
508+ // 3b. If konnector was newly deployed, wait for it to bootstrap CRDs
509+ if konnectorDeployed {
510+ logger .Info ("Waiting for konnector to bootstrap CRDs on consumer cluster" )
511+ if err := m .waitForCRD (ctx , consumerClient , "apiservicebindingbundles.kube-bind.io" ); err != nil {
512+ return nil , fmt .Errorf ("timed out waiting for konnector CRDs: %w" , err )
513+ }
514+ }
515+
516+ // 4. Create kubeconfig secret for the provider
499517 secretName := fmt .Sprintf ("kubeconfig-%s" , bindingName )
500518 kubeconfigSecret := & corev1.Secret {
501519 ObjectMeta : metav1.ObjectMeta {
@@ -523,7 +541,7 @@ func (m *Manager) ApplyToConsumer(
523541 }
524542 }
525543
526- // 4 . Create APIServiceBindingBundle
544+ // 5 . Create APIServiceBindingBundle
527545 bundle := & kubebindv1alpha2.APIServiceBindingBundle {
528546 ObjectMeta : metav1.ObjectMeta {
529547 Name : bindingName ,
@@ -562,9 +580,61 @@ func (m *Manager) ApplyToConsumer(
562580 }, nil
563581}
564582
583+ // resolveProviderHostAliases extracts the provider API server hostname from the
584+ // kubeconfig and resolves it to IP addresses. This allows the konnector pods on
585+ // the consumer cluster to reach the provider even when the hostname only resolves
586+ // correctly from the backend pod's network (e.g., Kind/Docker environments).
587+ func (m * Manager ) resolveProviderHostAliases (_ context.Context , providerKubeconfigData []byte ) []corev1.HostAlias {
588+ config , err := clientcmd .RESTConfigFromKubeConfig (providerKubeconfigData )
589+ if err != nil {
590+ return nil
591+ }
592+
593+ u , err := url .Parse (config .Host )
594+ if err != nil {
595+ return nil
596+ }
597+
598+ hostname := u .Hostname ()
599+ if hostname == "" {
600+ return nil
601+ }
602+
603+ // Skip if the host is already an IP address
604+ if net .ParseIP (hostname ) != nil {
605+ return nil
606+ }
607+
608+ // Resolve the hostname from the backend pod's perspective
609+ ips , err := net .LookupHost (hostname )
610+ if err != nil || len (ips ) == 0 {
611+ return nil
612+ }
613+
614+ // Filter out loopback addresses — they won't work in consumer pods
615+ var validIPs []string
616+ for _ , ip := range ips {
617+ parsed := net .ParseIP (ip )
618+ if parsed != nil && ! parsed .IsLoopback () {
619+ validIPs = append (validIPs , ip )
620+ }
621+ }
622+
623+ if len (validIPs ) == 0 {
624+ return nil
625+ }
626+
627+ return []corev1.HostAlias {
628+ {
629+ IP : validIPs [0 ],
630+ Hostnames : []string {hostname },
631+ },
632+ }
633+ }
634+
565635// ensureKonnector deploys the konnector agent to the consumer cluster.
566636// Returns true if the konnector was newly deployed, false if it already existed.
567- func (m * Manager ) ensureKonnector (ctx context.Context , c client.Client , konnectorImage string ) (bool , error ) {
637+ func (m * Manager ) ensureKonnector (ctx context.Context , c client.Client , konnectorImage string , hostAliases []corev1. HostAlias ) (bool , error ) {
568638 // Check if konnector deployment already exists
569639 existing := & appsv1.Deployment {}
570640 err := c .Get (ctx , types.NamespacedName {Name : kuberesources .KonnectorDeploymentName , Namespace : kuberesources .KonnectorNamespace }, existing )
@@ -575,7 +645,7 @@ func (m *Manager) ensureKonnector(ctx context.Context, c client.Client, konnecto
575645 return false , fmt .Errorf ("failed to check for existing konnector: %w" , err )
576646 }
577647
578- manifests := kuberesources .NewKonnectorManifests (konnectorImage )
648+ manifests := kuberesources .NewKonnectorManifests (konnectorImage , hostAliases )
579649
580650 if err := c .Create (ctx , manifests .ServiceAccount ); err != nil && ! errors .IsAlreadyExists (err ) {
581651 return false , fmt .Errorf ("failed to create konnector service account: %w" , err )
@@ -593,6 +663,27 @@ func (m *Manager) ensureKonnector(ctx context.Context, c client.Client, konnecto
593663 return true , nil
594664}
595665
666+ // waitForCRD polls until a CRD is registered on the target cluster.
667+ func (m * Manager ) waitForCRD (ctx context.Context , c client.Client , crdName string ) error {
668+ return wait .PollUntilContextTimeout (ctx , 2 * time .Second , 120 * time .Second , true , func (ctx context.Context ) (bool , error ) {
669+ crd := & apiextensionsv1.CustomResourceDefinition {}
670+ err := c .Get (ctx , types.NamespacedName {Name : crdName }, crd )
671+ if err != nil {
672+ if errors .IsNotFound (err ) {
673+ return false , nil // keep polling
674+ }
675+ return false , nil // transient error, keep polling
676+ }
677+ // Check if the CRD is established
678+ for _ , cond := range crd .Status .Conditions {
679+ if cond .Type == apiextensionsv1 .Established && cond .Status == apiextensionsv1 .ConditionTrue {
680+ return true , nil
681+ }
682+ }
683+ return false , nil
684+ })
685+ }
686+
596687func (m * Manager ) SeedDefaultCluster (ctx context.Context ) error {
597688 logger := klog .FromContext (ctx )
598689
0 commit comments