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
19 changes: 18 additions & 1 deletion backend/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"time"

"github.com/gorilla/mux"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -220,7 +221,7 @@ func (h *handler) handleKonnectorManifests(w http.ResponseWriter, r *http.Reques
}
konnectorImage := fmt.Sprintf("ghcr.io/kube-bind/konnector:%s", konnectorVersion)

manifests := kuberesources.NewKonnectorManifests(konnectorImage, nil)
manifests := kuberesources.NewKonnectorManifests(konnectorImage, h.kubeManager.GetKonnectorHostAliases())

// Serialize each object to YAML and join with document separators
s := runtime.NewScheme()
Expand Down Expand Up @@ -556,6 +557,9 @@ type applyBindingRequest struct {
ConsumerKubeconfig string `json:"consumerKubeconfig"`
// BindingName is the name for the binding (used for secret and bundle naming).
BindingName string `json:"bindingName"`
// HostAliases is an optional list of host alias entries for konnector pods,
// in the format "IP:hostname1,hostname2". Overrides the server-configured defaults.
HostAliases []string `json:"hostAliases,omitempty"`
}

// handleApplyBinding receives a consumer kubeconfig and applies the konnector + binding
Expand Down Expand Up @@ -610,13 +614,26 @@ func (h *handler) handleApplyBinding(w http.ResponseWriter, r *http.Request) {
}
konnectorImage := fmt.Sprintf("ghcr.io/kube-bind/konnector:%s", konnectorVersion)

// Parse optional host alias overrides from request
var overrideHostAliases []corev1.HostAlias
for _, entry := range req.HostAliases {
parts := strings.SplitN(entry, ":", 2)
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
overrideHostAliases = append(overrideHostAliases, corev1.HostAlias{
IP: parts[0],
Hostnames: strings.Split(parts[1], ","),
})
}
}

// Apply to consumer cluster
result, err := h.kubeManager.ApplyToConsumer(
r.Context(),
consumerKubeconfigData,
handleResult.Kubeconfig,
req.BindingName,
konnectorImage,
overrideHostAliases,
)
if err != nil {
logger.Error(err, "failed to apply binding to consumer cluster")
Expand Down
59 changes: 50 additions & 9 deletions backend/kubernetes/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ type Manager struct {

manager mcmanager.Manager
embeddedOIDC bool

// konnectorHostAliases are default host aliases injected into konnector
// pods deployed via the UI flow (configured via --konnector-host-alias flag).
konnectorHostAliases []corev1.HostAlias
}

func NewKubernetesManager(
Expand All @@ -68,6 +72,7 @@ func NewKubernetesManager(
externalTLSServerName string,
manager mcmanager.Manager,
embeddedOIDC bool,
konnectorHostAliases []corev1.HostAlias,
) (*Manager, error) {
m := &Manager{
namespacePrefix: namespacePrefix,
Expand All @@ -78,8 +83,9 @@ func NewKubernetesManager(
externalCA: externalCA,
externalTLSServerName: externalTLSServerName,

manager: manager,
embeddedOIDC: embeddedOIDC,
manager: manager,
embeddedOIDC: embeddedOIDC,
konnectorHostAliases: konnectorHostAliases,
}

if err := m.manager.GetFieldIndexer().IndexField(ctx, &corev1.Namespace{}, NamespacesByIdentity,
Expand Down Expand Up @@ -464,6 +470,7 @@ func (m *Manager) ApplyToConsumer(
providerKubeconfigData []byte,
bindingName string,
konnectorImage string,
overrideHostAliases []corev1.HostAlias,
) (*ApplyToConsumerResult, error) {
logger := klog.FromContext(ctx).WithValues("bindingName", bindingName)

Expand Down Expand Up @@ -494,10 +501,17 @@ func (m *Manager) ApplyToConsumer(
return nil, fmt.Errorf("failed to create kube-bind namespace: %w", err)
}

// 2. Resolve host aliases from the provider kubeconfig so the konnector
// can reach the provider API server (needed in Kind/Docker environments
// where the hostname resolves to localhost on the host but not in pods).
hostAliases := m.resolveProviderHostAliases(ctx, providerKubeconfigData)
// 2. Build host aliases: start with request overrides (if any), fall back to
// configured defaults, then merge any auto-resolved aliases from the provider kubeconfig.
var hostAliases []corev1.HostAlias
if len(overrideHostAliases) > 0 {
hostAliases = append(hostAliases, overrideHostAliases...)
} else {
hostAliases = append(hostAliases, m.konnectorHostAliases...)
}
if resolved := m.resolveProviderHostAliases(ctx, providerKubeconfigData); len(resolved) > 0 {
hostAliases = mergeHostAliases(hostAliases, resolved)
}

// 3. Deploy konnector (idempotent)
konnectorDeployed, err := m.ensureKonnector(ctx, consumerClient, konnectorImage, hostAliases)
Expand Down Expand Up @@ -632,18 +646,24 @@ func (m *Manager) resolveProviderHostAliases(ctx context.Context, providerKubeco
// ensureKonnector deploys the konnector agent to the consumer cluster.
// Returns true if the konnector was newly deployed, false if it already existed.
func (m *Manager) ensureKonnector(ctx context.Context, c client.Client, konnectorImage string, hostAliases []corev1.HostAlias) (bool, error) {
manifests := kuberesources.NewKonnectorManifests(konnectorImage, hostAliases)

// Check if konnector deployment already exists
existing := &appsv1.Deployment{}
err := c.Get(ctx, types.NamespacedName{Name: kuberesources.KonnectorDeploymentName, Namespace: kuberesources.KonnectorNamespace}, existing)
if err == nil {
return false, nil // already deployed
// Update the deployment if host aliases changed
existing.Spec.Template.Spec.HostAliases = hostAliases
existing.Spec.Template.Spec.Containers[0].Image = konnectorImage
if err := c.Update(ctx, existing); err != nil {
return false, fmt.Errorf("failed to update konnector deployment: %w", err)
}
return false, nil
}
if !errors.IsNotFound(err) {
return false, fmt.Errorf("failed to check for existing konnector: %w", err)
}

manifests := kuberesources.NewKonnectorManifests(konnectorImage, hostAliases)

if err := c.Create(ctx, manifests.ServiceAccount); err != nil && !errors.IsAlreadyExists(err) {
return false, fmt.Errorf("failed to create konnector service account: %w", err)
}
Expand Down Expand Up @@ -697,3 +717,24 @@ func (m *Manager) SeedDefaultCluster(ctx context.Context) error {
logger.Info("Default Cluster resource ensured")
return nil
}

// GetKonnectorHostAliases returns the configured default host aliases for konnector pods.
func (m *Manager) GetKonnectorHostAliases() []corev1.HostAlias {
return m.konnectorHostAliases
}

// mergeHostAliases merges additional host aliases into existing ones,
// skipping entries whose IP is already present.
func mergeHostAliases(existing, additional []corev1.HostAlias) []corev1.HostAlias {
seen := make(map[string]bool, len(existing))
for _, ha := range existing {
seen[ha.IP] = true
}
for _, ha := range additional {
if !seen[ha.IP] {
existing = append(existing, ha)
seen[ha.IP] = true
}
}
return existing
}
12 changes: 12 additions & 0 deletions backend/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ type ExtraOptions struct {
// SchemaSyncInterval is how often the serviceexportrequest controller re-reconciles
// to detect source schema changes when SchemaUpdatePolicy is Always.
SchemaSyncInterval time.Duration

// KonnectorHostAlias is a list of host alias entries for konnector pods
// deployed via the UI flow, in the format IP:hostname1,hostname2.
KonnectorHostAlias []string
}

type completedOptions struct {
Expand Down Expand Up @@ -195,6 +199,7 @@ func (options *Options) AddFlags(fs *pflag.FlagSet) {

fs.StringVar(&options.TestingAutoSelect, "testing-auto-select", options.TestingAutoSelect, "<resource>.<group> that is automatically selected on th bind screen for testing")
fs.MarkHidden("testing-auto-select") //nolint:errcheck
fs.StringSliceVar(&options.KonnectorHostAlias, "konnector-host-alias", options.KonnectorHostAlias, "Add host aliases to konnector pods deployed via the UI flow, in the format IP:hostname1,hostname2. Can be specified multiple times.")
}

func (options *Options) Complete() (*CompletedOptions, error) {
Expand Down Expand Up @@ -323,5 +328,12 @@ func (options *CompletedOptions) Validate() error {
return fmt.Errorf("--schema-sync-interval must be at least 10s, got %v", options.SchemaSyncInterval)
}

for _, entry := range options.KonnectorHostAlias {
parts := strings.SplitN(entry, ":", 2)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return fmt.Errorf("invalid --konnector-host-alias %q, expected format IP:hostname1,hostname2", entry)
}
}

return nil
}
15 changes: 15 additions & 0 deletions backend/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import (
"fmt"
"net"
nethttp "net/http"
"strings"
"sync"

corev1 "k8s.io/api/core/v1"
"k8s.io/klog/v2"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/controller"
Expand Down Expand Up @@ -79,6 +81,18 @@ func NewServer(ctx context.Context, c *Config) (*Server, error) {
Config: c,
}

// Parse konnector host aliases from flag
var konnectorHostAliases []corev1.HostAlias
for _, entry := range c.Options.KonnectorHostAlias {
parts := strings.SplitN(entry, ":", 2)
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
konnectorHostAliases = append(konnectorHostAliases, corev1.HostAlias{
IP: parts[0],
Hostnames: strings.Split(parts[1], ","),
})
}
}

var err error
s.Kubernetes, err = kube.NewKubernetesManager(
ctx,
Expand All @@ -90,6 +104,7 @@ func NewServer(ctx context.Context, c *Config) (*Server, error) {
c.Options.TLSExternalServerName,
s.Config.Manager,
c.Options.OIDC.Type == string(kubebindv1alpha2.OIDCProviderTypeEmbedded),
konnectorHostAliases,
)
if err != nil {
return nil, fmt.Errorf("error setting up Kubernetes Manager: %w", err)
Expand Down
Loading
Loading