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) + } + }) + } +}