@@ -48,6 +48,7 @@ import (
48
48
"k8s.io/client-go/kubernetes"
49
49
cloudprovider "k8s.io/cloud-provider"
50
50
"k8s.io/klog/v2"
51
+ netutils "k8s.io/utils/net"
51
52
52
53
"k8s.io/cloud-provider-openstack/pkg/metrics"
53
54
cpoutil "k8s.io/cloud-provider-openstack/pkg/util"
@@ -59,10 +60,11 @@ import (
59
60
// Note: when creating a new Loadbalancer (VM), it can take some time before it is ready for use,
60
61
// this timeout is used for waiting until the Loadbalancer provisioning status goes to ACTIVE state.
61
62
const (
62
- servicePrefix = "kube_service_"
63
- defaultLoadBalancerSourceRanges = "0.0.0.0/0"
64
- activeStatus = "ACTIVE"
65
- annotationXForwardedFor = "X-Forwarded-For"
63
+ servicePrefix = "kube_service_"
64
+ defaultLoadBalancerSourceRangesIPv4 = "0.0.0.0/0"
65
+ defaultLoadBalancerSourceRangesIPv6 = "::/0"
66
+ activeStatus = "ACTIVE"
67
+ annotationXForwardedFor = "X-Forwarded-For"
66
68
67
69
ServiceAnnotationLoadBalancerInternal = "service.beta.kubernetes.io/openstack-internal-load-balancer"
68
70
ServiceAnnotationLoadBalancerConnLimit = "loadbalancer.openstack.org/connection-limit"
@@ -346,6 +348,7 @@ type serviceConfig struct {
346
348
healthMonitorDelay int
347
349
healthMonitorTimeout int
348
350
healthMonitorMaxRetries int
351
+ preferredIPFamily corev1.IPFamily // preferred (the first) IP family indicated in service's `spec.ipFamilies`
349
352
}
350
353
351
354
type listenerKey struct {
@@ -651,8 +654,7 @@ func (lbaas *LbaasV2) createFullyPopulatedOctaviaLoadBalancer(name, clusterName
651
654
}
652
655
653
656
// In case subnet ID is not configured
654
- if lbaas .opts .SubnetID == "" {
655
- lbaas .opts .SubnetID = loadbalancer .VipSubnetID
657
+ if svcConf .lbMemberSubnetID == "" {
656
658
svcConf .lbMemberSubnetID = loadbalancer .VipSubnetID
657
659
}
658
660
@@ -726,7 +728,8 @@ func cutString(original string) string {
726
728
// In case no InternalIP can be found, ExternalIP is tried.
727
729
// If neither InternalIP nor ExternalIP can be found an error is
728
730
// returned.
729
- func nodeAddressForLB (node * corev1.Node ) (string , error ) {
731
+ // If preferredIPFamily is specified, only address of the specified IP family can be returned.
732
+ func nodeAddressForLB (node * corev1.Node , preferredIPFamily corev1.IPFamily ) (string , error ) {
730
733
addrs := node .Status .Addresses
731
734
if len (addrs ) == 0 {
732
735
return "" , cpoerrors .ErrNoAddressFound
@@ -737,7 +740,18 @@ func nodeAddressForLB(node *corev1.Node) (string, error) {
737
740
for _ , allowedAddrType := range allowedAddrTypes {
738
741
for _ , addr := range addrs {
739
742
if addr .Type == allowedAddrType {
740
- return addr .Address , nil
743
+ switch preferredIPFamily {
744
+ case corev1 .IPv4Protocol :
745
+ if netutils .IsIPv4String (addr .Address ) {
746
+ return addr .Address , nil
747
+ }
748
+ case corev1 .IPv6Protocol :
749
+ if netutils .IsIPv6String (addr .Address ) {
750
+ return addr .Address , nil
751
+ }
752
+ default :
753
+ return addr .Address , nil
754
+ }
741
755
}
742
756
}
743
757
}
@@ -801,8 +815,8 @@ func getBoolFromServiceAnnotation(service *corev1.Service, annotationKey string,
801
815
}
802
816
803
817
// getSubnetIDForLB returns subnet-id for a specific node
804
- func getSubnetIDForLB (compute * gophercloud.ServiceClient , node corev1.Node ) (string , error ) {
805
- ipAddress , err := nodeAddressForLB (& node )
818
+ func getSubnetIDForLB (compute * gophercloud.ServiceClient , node corev1.Node , preferredIPFamily corev1. IPFamily ) (string , error ) {
819
+ ipAddress , err := nodeAddressForLB (& node , preferredIPFamily )
806
820
if err != nil {
807
821
return "" , err
808
822
}
@@ -1308,7 +1322,7 @@ func (lbaas *LbaasV2) buildBatchUpdateMemberOpts(port corev1.ServicePort, nodes
1308
1322
newMembers := sets .NewString ()
1309
1323
1310
1324
for _ , node := range nodes {
1311
- addr , err := nodeAddressForLB (node )
1325
+ addr , err := nodeAddressForLB (node , svcConf . preferredIPFamily )
1312
1326
if err != nil {
1313
1327
if err == cpoerrors .ErrNoAddressFound {
1314
1328
// Node failure, do not create member
@@ -1477,6 +1491,12 @@ func (lbaas *LbaasV2) checkServiceUpdate(service *corev1.Service, nodes []*corev
1477
1491
}
1478
1492
serviceName := fmt .Sprintf ("%s/%s" , service .Namespace , service .Name )
1479
1493
1494
+ if len (service .Spec .IPFamilies ) > 0 {
1495
+ // Since OCCM does not support multiple load-balancers per service yet,
1496
+ // the first IP family will determine the IP family of the load-balancer
1497
+ svcConf .preferredIPFamily = service .Spec .IPFamilies [0 ]
1498
+ }
1499
+
1480
1500
svcConf .lbID = getStringFromServiceAnnotation (service , ServiceAnnotationLoadBalancerID , "" )
1481
1501
svcConf .supportLBTags = openstackutil .IsOctaviaFeatureSupported (lbaas .lb , openstackutil .OctaviaFeatureTags , lbaas .opts .LBProvider )
1482
1502
@@ -1497,7 +1517,7 @@ func (lbaas *LbaasV2) checkServiceUpdate(service *corev1.Service, nodes []*corev
1497
1517
} else {
1498
1518
svcConf .lbMemberSubnetID = getStringFromServiceAnnotation (service , ServiceAnnotationLoadBalancerSubnetID , lbaas .opts .SubnetID )
1499
1519
if len (svcConf .lbMemberSubnetID ) == 0 && len (nodes ) > 0 {
1500
- subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ])
1520
+ subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ], svcConf . preferredIPFamily )
1501
1521
if err != nil {
1502
1522
return fmt .Errorf ("no subnet-id found for service %s: %v" , serviceName , err )
1503
1523
}
@@ -1549,6 +1569,12 @@ func (lbaas *LbaasV2) checkService(service *corev1.Service, nodes []*corev1.Node
1549
1569
return fmt .Errorf ("no service ports provided" )
1550
1570
}
1551
1571
1572
+ if len (service .Spec .IPFamilies ) > 0 {
1573
+ // Since OCCM does not support multiple load-balancers per service yet,
1574
+ // the first IP family will determine the IP family of the load-balancer
1575
+ svcConf .preferredIPFamily = service .Spec .IPFamilies [0 ]
1576
+ }
1577
+
1552
1578
svcConf .lbID = getStringFromServiceAnnotation (service , ServiceAnnotationLoadBalancerID , "" )
1553
1579
svcConf .supportLBTags = openstackutil .IsOctaviaFeatureSupported (lbaas .lb , openstackutil .OctaviaFeatureTags , lbaas .opts .LBProvider )
1554
1580
@@ -1558,6 +1584,9 @@ func (lbaas *LbaasV2) checkService(service *corev1.Service, nodes []*corev1.Node
1558
1584
klog .V (3 ).InfoS ("Enforcing internal LB" , "annotation" , true , "config" , false )
1559
1585
}
1560
1586
svcConf .internal = true
1587
+ } else if svcConf .preferredIPFamily == corev1 .IPv6Protocol {
1588
+ // floating IPs are not supported in IPv6 networks
1589
+ svcConf .internal = true
1561
1590
} else {
1562
1591
svcConf .internal = getBoolFromServiceAnnotation (service , ServiceAnnotationLoadBalancerInternal , lbaas .opts .InternalLB )
1563
1592
}
@@ -1590,13 +1619,12 @@ func (lbaas *LbaasV2) checkService(service *corev1.Service, nodes []*corev1.Node
1590
1619
svcConf .lbMemberSubnetID = svcConf .lbSubnetID
1591
1620
}
1592
1621
if len (svcConf .lbNetworkID ) == 0 && len (svcConf .lbSubnetID ) == 0 {
1593
- subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ])
1622
+ subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ], svcConf . preferredIPFamily )
1594
1623
if err != nil {
1595
1624
return fmt .Errorf ("failed to get subnet to create load balancer for service %s: %v" , serviceName , err )
1596
1625
}
1597
1626
svcConf .lbSubnetID = subnetID
1598
1627
svcConf .lbMemberSubnetID = subnetID
1599
- lbaas .opts .SubnetID = subnetID
1600
1628
}
1601
1629
1602
1630
if ! svcConf .internal {
@@ -1698,7 +1726,7 @@ func (lbaas *LbaasV2) checkService(service *corev1.Service, nodes []*corev1.Node
1698
1726
}
1699
1727
1700
1728
var listenerAllowedCIDRs []string
1701
- sourceRanges , err := GetLoadBalancerSourceRanges (service )
1729
+ sourceRanges , err := GetLoadBalancerSourceRanges (service , svcConf . preferredIPFamily )
1702
1730
if err != nil {
1703
1731
return fmt .Errorf ("failed to get source ranges for loadbalancer service %s: %v" , serviceName , err )
1704
1732
}
@@ -1932,7 +1960,7 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
1932
1960
}
1933
1961
1934
1962
if lbaas .opts .ManageSecurityGroups {
1935
- err := lbaas .ensureSecurityGroup (clusterName , service , nodes , loadbalancer )
1963
+ err := lbaas .ensureSecurityGroup (clusterName , service , nodes , loadbalancer , svcConf . preferredIPFamily , svcConf . lbMemberSubnetID )
1936
1964
if err != nil {
1937
1965
return status , fmt .Errorf ("failed when reconciling security groups for LB service %v/%v: %v" , service .Namespace , service .Name , err )
1938
1966
}
@@ -1972,7 +2000,7 @@ func (lbaas *LbaasV2) ensureLoadBalancer(ctx context.Context, clusterName string
1972
2000
if len (lbaas .opts .SubnetID ) == 0 && len (lbaas .opts .NetworkID ) == 0 {
1973
2001
// Get SubnetID automatically.
1974
2002
// The LB needs to be configured with instance addresses on the same subnet, so get SubnetID by one node.
1975
- subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ])
2003
+ subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ], "" )
1976
2004
if err != nil {
1977
2005
klog .Warningf ("Failed to find subnet-id for loadbalancer service %s/%s: %v" , apiService .Namespace , apiService .Name , err )
1978
2006
return nil , fmt .Errorf ("no subnet-id for service %s/%s : subnet-id not set in cloud provider config, " +
@@ -2063,7 +2091,7 @@ func (lbaas *LbaasV2) ensureLoadBalancer(ctx context.Context, clusterName string
2063
2091
}
2064
2092
}
2065
2093
2066
- sourceRanges , err := GetLoadBalancerSourceRanges (apiService )
2094
+ sourceRanges , err := GetLoadBalancerSourceRanges (apiService , "" )
2067
2095
if err != nil {
2068
2096
return nil , fmt .Errorf ("failed to get source ranges for loadbalancer service %s: %v" , serviceName , err )
2069
2097
}
@@ -2201,7 +2229,7 @@ func (lbaas *LbaasV2) ensureLoadBalancer(ctx context.Context, clusterName string
2201
2229
return nil , fmt .Errorf ("error getting pool members %s: %v" , pool .ID , err )
2202
2230
}
2203
2231
for _ , node := range nodes {
2204
- addr , err := nodeAddressForLB (node )
2232
+ addr , err := nodeAddressForLB (node , "" )
2205
2233
if err != nil {
2206
2234
if err == cpoerrors .ErrNotFound {
2207
2235
// Node failure, do not create member
@@ -2418,7 +2446,7 @@ func (lbaas *LbaasV2) ensureLoadBalancer(ctx context.Context, clusterName string
2418
2446
}
2419
2447
2420
2448
if lbaas .opts .ManageSecurityGroups {
2421
- err := lbaas .ensureSecurityGroup (clusterName , apiService , nodes , loadbalancer )
2449
+ err := lbaas .ensureSecurityGroup (clusterName , apiService , nodes , loadbalancer , "" , lbaas . opts . SubnetID )
2422
2450
if err != nil {
2423
2451
return status , fmt .Errorf ("failed when reconciling security groups for LB service %v/%v: %v" , apiService .Namespace , apiService .Name , err )
2424
2452
}
@@ -2476,7 +2504,8 @@ func (lbaas *LbaasV2) getSubnet(subnet string) (*subnets.Subnet, error) {
2476
2504
2477
2505
// ensureSecurityGroup ensures security group exist for specific loadbalancer service.
2478
2506
// Creating security group for specific loadbalancer service when it does not exist.
2479
- func (lbaas * LbaasV2 ) ensureSecurityGroup (clusterName string , apiService * corev1.Service , nodes []* corev1.Node , loadbalancer * loadbalancers.LoadBalancer ) error {
2507
+ func (lbaas * LbaasV2 ) ensureSecurityGroup (clusterName string , apiService * corev1.Service , nodes []* corev1.Node ,
2508
+ loadbalancer * loadbalancers.LoadBalancer , preferredIPFamily corev1.IPFamily , memberSubnetID string ) error {
2480
2509
// find node-security-group for service
2481
2510
var err error
2482
2511
if len (lbaas .opts .NodeSecurityGroupIDs ) == 0 && ! lbaas .opts .UseOctavia {
@@ -2495,7 +2524,7 @@ func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *corev1
2495
2524
}
2496
2525
2497
2526
// get service source ranges
2498
- sourceRanges , err := GetLoadBalancerSourceRanges (apiService )
2527
+ sourceRanges , err := GetLoadBalancerSourceRanges (apiService , preferredIPFamily )
2499
2528
if err != nil {
2500
2529
return fmt .Errorf ("failed to get source ranges for loadbalancer service %s/%s: %v" , apiService .Namespace , apiService .Name , err )
2501
2530
}
@@ -2628,9 +2657,9 @@ func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *corev1
2628
2657
// traffic from Octavia amphorae to the node port on the worker nodes.
2629
2658
if lbaas .opts .UseOctavia {
2630
2659
mc := metrics .NewMetricContext ("subnet" , "get" )
2631
- subnet , err := subnets .Get (lbaas .network , lbaas . opts . SubnetID ).Extract ()
2660
+ subnet , err := subnets .Get (lbaas .network , memberSubnetID ).Extract ()
2632
2661
if mc .ObserveRequest (err ) != nil {
2633
- return fmt .Errorf ("failed to find subnet %s from openstack: %v" , lbaas . opts . SubnetID , err )
2662
+ return fmt .Errorf ("failed to find subnet %s from openstack: %v" , memberSubnetID , err )
2634
2663
}
2635
2664
2636
2665
sgListopts := rules.ListOpts {
@@ -2649,6 +2678,11 @@ func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *corev1
2649
2678
continue
2650
2679
}
2651
2680
2681
+ ethertype := rules .EtherType4
2682
+ if netutils .IsIPv6CIDRString (subnet .CIDR ) {
2683
+ ethertype = rules .EtherType6
2684
+ }
2685
+
2652
2686
// The Octavia amphorae and worker nodes are supposed to be in the same subnet. We allow the ingress traffic
2653
2687
// from the amphorae to the specific node port on the nodes.
2654
2688
sgRuleCreateOpts := rules.CreateOpts {
@@ -2658,7 +2692,7 @@ func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *corev1
2658
2692
Protocol : toRuleProtocol (port .Protocol ),
2659
2693
RemoteIPPrefix : subnet .CIDR ,
2660
2694
SecGroupID : lbSecGroupID ,
2661
- EtherType : rules . EtherType4 ,
2695
+ EtherType : ethertype ,
2662
2696
}
2663
2697
mc = metrics .NewMetricContext ("security_group_rule" , "create" )
2664
2698
_ , err = rules .Create (lbaas .network , sgRuleCreateOpts ).Extract ()
@@ -2797,7 +2831,7 @@ func (lbaas *LbaasV2) updateLoadBalancer(ctx context.Context, clusterName string
2797
2831
if len (lbaas .opts .SubnetID ) == 0 && len (nodes ) > 0 {
2798
2832
// Get SubnetID automatically.
2799
2833
// The LB needs to be configured with instance addresses on the same subnet, so get SubnetID by one node.
2800
- subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ])
2834
+ subnetID , err := getSubnetIDForLB (lbaas .compute , * nodes [0 ], "" )
2801
2835
if err != nil {
2802
2836
klog .Warningf ("Failed to find subnet-id for loadbalancer service %s/%s: %v" , service .Namespace , service .Name , err )
2803
2837
return fmt .Errorf ("no subnet-id for service %s/%s : subnet-id not set in cloud provider config, " +
@@ -2851,7 +2885,7 @@ func (lbaas *LbaasV2) updateLoadBalancer(ctx context.Context, clusterName string
2851
2885
// Compose Set of member (addresses) that _should_ exist
2852
2886
addrs := make (map [string ]* corev1.Node )
2853
2887
for _ , node := range nodes {
2854
- addr , err := nodeAddressForLB (node )
2888
+ addr , err := nodeAddressForLB (node , "" )
2855
2889
if err != nil {
2856
2890
return err
2857
2891
}
@@ -3332,7 +3366,7 @@ func IsAllowAll(ipnets netsets.IPNet) bool {
3332
3366
// GetLoadBalancerSourceRanges first try to parse and verify LoadBalancerSourceRanges field from a service.
3333
3367
// If the field is not specified, turn to parse and verify the AnnotationLoadBalancerSourceRangesKey annotation from a service,
3334
3368
// extracting the source ranges to allow, and if not present returns a default (allow-all) value.
3335
- func GetLoadBalancerSourceRanges (service * corev1.Service ) (netsets.IPNet , error ) {
3369
+ func GetLoadBalancerSourceRanges (service * corev1.Service , preferredIPFamily corev1. IPFamily ) (netsets.IPNet , error ) {
3336
3370
var ipnets netsets.IPNet
3337
3371
var err error
3338
3372
// if SourceRange field is specified, ignore sourceRange annotation
@@ -3347,7 +3381,11 @@ func GetLoadBalancerSourceRanges(service *corev1.Service) (netsets.IPNet, error)
3347
3381
val := service .Annotations [corev1 .AnnotationLoadBalancerSourceRangesKey ]
3348
3382
val = strings .TrimSpace (val )
3349
3383
if val == "" {
3350
- val = defaultLoadBalancerSourceRanges
3384
+ if preferredIPFamily == corev1 .IPv6Protocol {
3385
+ val = defaultLoadBalancerSourceRangesIPv6
3386
+ } else {
3387
+ val = defaultLoadBalancerSourceRangesIPv4
3388
+ }
3351
3389
}
3352
3390
specs := strings .Split (val , "," )
3353
3391
ipnets , err = netsets .ParseIPNets (specs ... )
0 commit comments