From a077dbb6c12739a7e91957504295dcbdeb3ceee3 Mon Sep 17 00:00:00 2001 From: parth-gr Date: Thu, 4 Dec 2025 19:10:10 +0530 Subject: [PATCH] external: fix endpoint slice type for ipv6 clusters in endpoint slice for the mgr and rgw service it was harcoded to use the ipv4, now dynamically assign the type based on the endpoint ip adrress type Signed-off-by: parth-gr (cherry picked from commit 0a3d9b8517dad679d5202e7d6bcea45406535c04) --- pkg/operator/ceph/controller/spec.go | 9 +++- pkg/operator/ceph/object/spec.go | 18 +++++--- pkg/operator/k8sutil/network.go | 38 ++++++++++++++++ pkg/operator/k8sutil/network_test.go | 67 ++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 7 deletions(-) diff --git a/pkg/operator/ceph/controller/spec.go b/pkg/operator/ceph/controller/spec.go index c21100b39..7e98c9f12 100644 --- a/pkg/operator/ceph/controller/spec.go +++ b/pkg/operator/ceph/controller/spec.go @@ -871,13 +871,18 @@ func createExternalMetricsEndpoints(namespace string, monitoringSpec cephv1.Moni addresses[i] = endpoint.IP } + addressType, err := k8sutil.GetIpAddressType(addresses) + if err != nil { + return nil, errors.Wrap(err, "failed to get the addressType") + } + endpoints := &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Name: ExternalMgrAppName, Namespace: namespace, Labels: labels, }, - AddressType: discoveryv1.AddressTypeIPv4, + AddressType: addressType, Endpoints: []discoveryv1.Endpoint{ { Addresses: addresses, @@ -895,7 +900,7 @@ func createExternalMetricsEndpoints(namespace string, monitoringSpec cephv1.Moni }, } - err := ownerInfo.SetControllerReference(endpoints) + err = ownerInfo.SetControllerReference(endpoints) if err != nil { return nil, errors.Wrapf(err, "failed to set owner reference to metric endpoints %q", endpoints.Name) } diff --git a/pkg/operator/ceph/object/spec.go b/pkg/operator/ceph/object/spec.go index e681b55c9..1afda75f9 100644 --- a/pkg/operator/ceph/object/spec.go +++ b/pkg/operator/ceph/object/spec.go @@ -735,7 +735,7 @@ func (c *clusterConfig) generateService(cephObjectStore *cephv1.CephObjectStore) return svc } -func (c *clusterConfig) generateEndpoint(cephObjectStore *cephv1.CephObjectStore) *discoveryv1.EndpointSlice { +func (c *clusterConfig) generateEndpoint(cephObjectStore *cephv1.CephObjectStore) (*discoveryv1.EndpointSlice, error) { labels := getLabels(cephObjectStore.Name, cephObjectStore.Namespace, true) // Convert to string addresses @@ -744,13 +744,18 @@ func (c *clusterConfig) generateEndpoint(cephObjectStore *cephv1.CephObjectStore k8sEndpointAddrs = append(k8sEndpointAddrs, rookEndpoint.IP) } + addressType, err := k8sutil.GetIpAddressType(k8sEndpointAddrs) + if err != nil { + return nil, errors.Wrap(err, "failed to get the addressType") + } + endpoints := &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Name: instanceName(cephObjectStore.Name), Namespace: cephObjectStore.Namespace, Labels: labels, }, - AddressType: discoveryv1.AddressTypeIPv4, + AddressType: addressType, Endpoints: []discoveryv1.Endpoint{ { Addresses: k8sEndpointAddrs, @@ -765,16 +770,19 @@ func (c *clusterConfig) generateEndpoint(cephObjectStore *cephv1.CephObjectStore addPortToEndpoint(endpoints, "http", cephObjectStore.Spec.Gateway.Port) addPortToEndpoint(endpoints, "https", cephObjectStore.Spec.Gateway.SecurePort) - return endpoints + return endpoints, nil } func (c *clusterConfig) reconcileExternalEndpoint(cephObjectStore *cephv1.CephObjectStore) error { nsName := controller.NsName(c.store.Namespace, c.store.Name) log.NamedInfo(nsName, logger, "reconciling external object store service") - endpoint := c.generateEndpoint(cephObjectStore) + endpoint, err := c.generateEndpoint(cephObjectStore) + if err != nil { + return errors.Wrapf(err, "failed to generate the endpoint %q", endpoint.Name) + } // Set owner ref to the parent object - err := c.ownerInfo.SetControllerReference(endpoint) + err = c.ownerInfo.SetControllerReference(endpoint) if err != nil { return errors.Wrapf(err, "failed to set owner reference to ceph object store endpoint %q", endpoint.Name) } diff --git a/pkg/operator/k8sutil/network.go b/pkg/operator/k8sutil/network.go index b82502c8b..e1de9a984 100644 --- a/pkg/operator/k8sutil/network.go +++ b/pkg/operator/k8sutil/network.go @@ -18,11 +18,13 @@ package k8sutil import ( "encoding/json" + "net/netip" "strings" nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" "github.com/pkg/errors" cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -110,3 +112,39 @@ func ParseLinuxIpAddrOutput(rawOutput string) ([]LinuxIpAddrResult, error) { } return results, nil } + +func GetIpAddressType(addresses []string) (discoveryv1.AddressType, error) { + var addressType discoveryv1.AddressType + + if len(addresses) == 0 { + return addressType, errors.New("failed to get the address type for ip address, address list is empty type") + } + ip, err := netip.ParseAddr(addresses[0]) + if err != nil { + return addressType, errors.Wrapf(err, "failed to get the address type for ip address %q", addresses[0]) + } + addressType = discoveryv1.AddressTypeIPv4 + if ip.Is6() { + addressType = discoveryv1.AddressTypeIPv6 + } + + // validate all addresses are of the same type + for _, addr := range addresses[1:] { + ip, err := netip.ParseAddr(addr) + if err != nil { + return addressType, errors.Wrapf(err, "failed to get the address type for ip address %q", addr) + } + + if ip.Is6() { + if addressType != discoveryv1.AddressTypeIPv6 { + return addressType, errors.Errorf("all ip address are not of same type %v", addresses) + } + } else { + if addressType != discoveryv1.AddressTypeIPv4 { + return addressType, errors.Errorf("all ip address are not of same type %v", addresses) + } + } + } + + return addressType, nil +} diff --git a/pkg/operator/k8sutil/network_test.go b/pkg/operator/k8sutil/network_test.go index d6f832552..fc0986f0f 100644 --- a/pkg/operator/k8sutil/network_test.go +++ b/pkg/operator/k8sutil/network_test.go @@ -22,6 +22,7 @@ import ( cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" "github.com/stretchr/testify/assert" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -307,3 +308,69 @@ func removeLastChar(in string) string { len := len(chars) return string(chars[0 : len-1]) } + +// test for GetIpAddressType +func TestGetIpAddressType(t *testing.T) { + // test cases + tests := []struct { + name string + addresses []string + wantType discoveryv1.AddressType + wantErr bool + }{ + { + name: "single IPv4 address", + addresses: []string{"192.168.1.1"}, + wantType: discoveryv1.AddressTypeIPv4, + wantErr: false, + }, + { + name: "IPv4 address", + addresses: []string{"192.168.1.1", "192.146.1.1"}, + wantType: discoveryv1.AddressTypeIPv4, + wantErr: false, + }, + { + name: "single IPv6 address", + addresses: []string{"2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, + wantType: discoveryv1.AddressTypeIPv6, + wantErr: false, + }, + { + name: "IPv6 address", + addresses: []string{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "fe80::1ff:fe23:4567:890a"}, + wantType: discoveryv1.AddressTypeIPv6, + wantErr: false, + }, + { + name: "Mixed addresses", + addresses: []string{"192.168.1.1", "192.146.1.1", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "fe80::1ff:fe23:4567:890a"}, + wantType: "", + wantErr: true, + }, + { + name: "Empty address list", + addresses: []string{}, + wantType: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotType, err := GetIpAddressType(tt.addresses) + if err != nil { + if !tt.wantErr { + t.Errorf("GetIpAddressType() unexpected error: %v", err) + return + } + } else { + if tt.wantErr { + t.Errorf("GetIpAddressType() expected error but got none") + return + } + assert.Equal(t, tt.wantType, gotType) + } + }) + } +}