From dc10dc3a3a39691a354492b8132340131b54887e Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Tue, 6 Aug 2024 13:33:25 +0000 Subject: [PATCH] Fix the VIP URL generation for IPv6 When configuring Kubernetes with multiple nodes, EIB requires an IP address to be used as a target for load balancing purposes. However, it assumes this address to be in an IPv4 format, resulting in a broken mangling when IPv6 is used instead. Correct this problem by letting netip package do the handling of IP addresses, hiding version specific issues. This also allows for easier and additional input validation with minimal effort. Signed-off-by: Marco Chiappero --- RELEASE_NOTES.md | 2 ++ docs/building-images.md | 4 ++-- pkg/kubernetes/cluster.go | 8 +++++++- pkg/kubernetes/cluster_test.go | 3 +++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5a99dce4..54803e93 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,8 @@ ## Bug Fixes +* [#512](https://github.com/suse-edge/edge-image-builder/issues/512) - EIB assumes apiVIP to be IPv4 only when creating the target URL + --- # v1.1.0-rc1 diff --git a/docs/building-images.md b/docs/building-images.md index 85257db1..9eb2a917 100644 --- a/docs/building-images.md +++ b/docs/building-images.md @@ -258,8 +258,8 @@ kubernetes: * `version` - Required; Specifies the version of a particular K3s or RKE2 release (e.g.`v1.28.8+k3s1` or `v1.28.8+rke2r1`) * `network` - Required for multi-node clusters, optional for single-node clusters; Defines the network configuration for bootstrapping a cluster. - * `apiVIP` - Required for multi-node clusters, optional for single-node clusters; Specifies the IP address which - will serve as the cluster LoadBalancer, backed by MetalLB. + * `apiVIP` - Required for multi-node clusters, optional for single-node clusters; Specifies the IPv4 or IPv6 address + which will serve as the cluster LoadBalancer, backed by MetalLB. * `apiHost` - Optional; Specifies the domain address for accessing the cluster. * `nodes` - Required for multi-node clusters; Defines a list of all nodes that form the cluster. * `hostname` - Required; Indicates the fully qualified domain name (FQDN) to identify the particular node on which diff --git a/pkg/kubernetes/cluster.go b/pkg/kubernetes/cluster.go index 2816aa3b..e672e597 100644 --- a/pkg/kubernetes/cluster.go +++ b/pkg/kubernetes/cluster.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io/fs" + "net/netip" "os" "path/filepath" "strings" @@ -202,7 +203,12 @@ func setClusterAPIAddress(config map[string]any, apiAddress string, port uint16) return } - config[serverKey] = fmt.Sprintf("https://%s:%d", apiAddress, port) + ip, err := netip.ParseAddr(apiAddress) + if err != nil { + panic("Invalid Kubernetes VIP address") + } + + config[serverKey] = fmt.Sprintf("https://%s", netip.AddrPortFrom(ip, port).String()) } func setSELinux(config map[string]any) { diff --git a/pkg/kubernetes/cluster_test.go b/pkg/kubernetes/cluster_test.go index fba10a6e..9c5909cc 100644 --- a/pkg/kubernetes/cluster_test.go +++ b/pkg/kubernetes/cluster_test.go @@ -281,6 +281,9 @@ func TestSetClusterAPIAddress(t *testing.T) { setClusterAPIAddress(config, "192.168.122.50", 9345) assert.Equal(t, "https://192.168.122.50:9345", config["server"]) + + setClusterAPIAddress(config, "FC00:1:2:3::50", 9345) + assert.Equal(t, "https://[fc00:1:2:3::50]:9345", config["server"]) } func TestAppendClusterTLSSAN(t *testing.T) {