@@ -444,12 +444,16 @@ func (l *loadbalancers) updateNodeBalancer(
444444 // Add all of the Nodes to the config
445445 newNBNodes := make ([]linodego.NodeBalancerConfigRebuildNodeOptions , 0 , len (nodes ))
446446 subnetID := 0
447+ if Options .NodeBalancerBackendIPv4SubnetID != 0 {
448+ subnetID = Options .NodeBalancerBackendIPv4SubnetID
449+ }
447450 backendIPv4Range , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendIPv4Range ]
448451 if ok {
449452 if err = validateNodeBalancerBackendIPv4Range (backendIPv4Range ); err != nil {
450453 return err
451454 }
452-
455+ }
456+ if Options .VPCNames != "" && ! Options .DisableNodeBalancerVPCBackends {
453457 var id int
454458 id , err = l .getSubnetIDForSVC (ctx , service )
455459 if err != nil {
@@ -717,6 +721,86 @@ func (l *loadbalancers) GetLinodeNBType(service *v1.Service) linodego.NodeBalanc
717721 return linodego .NodeBalancerPlanType (Options .DefaultNBType )
718722}
719723
724+ // getVPCCreateOptions returns the VPC options for the NodeBalancer creation.
725+ // Order of precedence:
726+ // 1. NodeBalancerBackendIPv4Range annotation
727+ // 2. NodeBalancerBackendVPCName and NodeBalancerBackendSubnetName annotation
728+ // 3. NodeBalancerBackendIPv4SubnetID/NodeBalancerBackendIPv4SubnetName flag
729+ // 4. NodeBalancerBackendIPv4Subnet flag
730+ // 5. Default to using the subnet ID of the service's VPC
731+ func (l * loadbalancers ) getVPCCreateOptions (ctx context.Context , service * v1.Service ) ([]linodego.NodeBalancerVPCOptions , error ) {
732+ // Evaluate subnetID based on annotations or flags
733+ subnetID , err := l .getSubnetIDForSVC (ctx , service )
734+ if err != nil {
735+ return nil , err
736+ }
737+
738+ // Precedence 1: If the user has specified a NodeBalancerBackendIPv4Range, use that
739+ backendIPv4Range , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendIPv4Range ]
740+ if ok {
741+ if err := validateNodeBalancerBackendIPv4Range (backendIPv4Range ); err != nil {
742+ return nil , err
743+ }
744+ // If the user has specified a NodeBalancerBackendIPv4Range, use that
745+ // for the NodeBalancer backend ipv4 range
746+ if backendIPv4Range != "" {
747+ vpcCreateOpts := []linodego.NodeBalancerVPCOptions {
748+ {
749+ SubnetID : subnetID ,
750+ IPv4Range : backendIPv4Range ,
751+ },
752+ }
753+ return vpcCreateOpts , nil
754+ }
755+ }
756+
757+ // Precedence 2: If the user wants to overwrite the default VPC name or subnet name
758+ // and have specified it in the annotations, use it to set subnetID
759+ // and auto-allocate subnets from it for the NodeBalancer
760+ _ , vpcInAnnotation := service .GetAnnotations ()[annotations .NodeBalancerBackendVPCName ]
761+ _ , subnetInAnnotation := service .GetAnnotations ()[annotations .NodeBalancerBackendSubnetName ]
762+ if vpcInAnnotation || subnetInAnnotation {
763+ vpcCreateOpts := []linodego.NodeBalancerVPCOptions {
764+ {
765+ SubnetID : subnetID ,
766+ },
767+ }
768+ return vpcCreateOpts , nil
769+ }
770+
771+ // Precedence 3: If the user has specified a NodeBalancerBackendIPv4SubnetID, use that
772+ // and auto-allocate subnets from it for the NodeBalancer
773+ if Options .NodeBalancerBackendIPv4SubnetID != 0 {
774+ vpcCreateOpts := []linodego.NodeBalancerVPCOptions {
775+ {
776+ SubnetID : Options .NodeBalancerBackendIPv4SubnetID ,
777+ },
778+ }
779+ return vpcCreateOpts , nil
780+ }
781+
782+ // Precedence 4: If the user has specified a NodeBalancerBackendIPv4Subnet, use that
783+ // and auto-allocate subnets from it for the NodeBalancer
784+ if Options .NodeBalancerBackendIPv4Subnet != "" {
785+ vpcCreateOpts := []linodego.NodeBalancerVPCOptions {
786+ {
787+ SubnetID : subnetID ,
788+ IPv4Range : Options .NodeBalancerBackendIPv4Subnet ,
789+ IPv4RangeAutoAssign : true ,
790+ },
791+ }
792+ return vpcCreateOpts , nil
793+ }
794+
795+ // Default to using the subnet ID of the service's VPC
796+ vpcCreateOpts := []linodego.NodeBalancerVPCOptions {
797+ {
798+ SubnetID : subnetID ,
799+ },
800+ }
801+ return vpcCreateOpts , nil
802+ }
803+
720804func (l * loadbalancers ) createNodeBalancer (ctx context.Context , clusterName string , service * v1.Service , configs []* linodego.NodeBalancerConfigCreateOptions ) (lb * linodego.NodeBalancer , err error ) {
721805 connThrottle := getConnectionThrottle (service )
722806
@@ -732,21 +816,11 @@ func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName stri
732816 Type : nbType ,
733817 }
734818
735- backendIPv4Range , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendIPv4Range ]
736- if ok {
737- if err := validateNodeBalancerBackendIPv4Range (backendIPv4Range ); err != nil {
738- return nil , err
739- }
740- subnetID , err := l .getSubnetIDForSVC (ctx , service )
819+ if Options .VPCNames != "" && ! Options .DisableNodeBalancerVPCBackends {
820+ createOpts .VPCs , err = l .getVPCCreateOptions (ctx , service )
741821 if err != nil {
742822 return nil , err
743823 }
744- createOpts .VPCs = []linodego.NodeBalancerVPCOptions {
745- {
746- SubnetID : subnetID ,
747- IPv4Range : backendIPv4Range ,
748- },
749- }
750824 }
751825
752826 fwid , ok := service .GetAnnotations ()[annotations .AnnLinodeCloudFirewallID ]
@@ -875,25 +949,49 @@ func (l *loadbalancers) addTLSCert(ctx context.Context, service *v1.Service, nbC
875949 return nil
876950}
877951
878- // getSubnetIDForSVC returns the subnet ID for the service's VPC and subnet.
879- // By default, first VPCName and SubnetName are used to calculate subnet id for the service.
880- // If the service has annotations specifying VPCName and SubnetName, they are used instead.
952+ // getSubnetIDForSVC returns the subnet ID for the service when running within VPC.
953+ // Following precedence rules are applied:
954+ // 1. If the service has an annotation for NodeBalancerBackendSubnetID, use that.
955+ // 2. If the service has annotations specifying VPCName or SubnetName, use them.
956+ // 3. If CCM is configured with --nodebalancer-backend-ipv4-subnet-id, it will be used as the subnet ID.
957+ // 4. Else, use first VPCName and SubnetName to calculate subnet id for the service.
881958func (l * loadbalancers ) getSubnetIDForSVC (ctx context.Context , service * v1.Service ) (int , error ) {
882959 if Options .VPCNames == "" {
883960 return 0 , fmt .Errorf ("CCM not configured with VPC, cannot create NodeBalancer with specified annotation" )
884961 }
962+ // Check if the service has an annotation for NodeBalancerBackendSubnetID
963+ if specifiedSubnetID , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendSubnetID ]; ok {
964+ subnetID , err := strconv .Atoi (specifiedSubnetID )
965+ if err != nil {
966+ return 0 , err
967+ }
968+ return subnetID , nil
969+ }
970+
971+ specifiedVPCName , vpcOk := service .GetAnnotations ()[annotations .NodeBalancerBackendVPCName ]
972+ specifiedSubnetName , subnetOk := service .GetAnnotations ()[annotations .NodeBalancerBackendSubnetName ]
973+
974+ // If no VPCName or SubnetName is specified in annotations, but NodeBalancerBackendIPv4SubnetID is set,
975+ // use the NodeBalancerBackendIPv4SubnetID as the subnet ID.
976+ if ! vpcOk && ! subnetOk && Options .NodeBalancerBackendIPv4SubnetID != 0 {
977+ return Options .NodeBalancerBackendIPv4SubnetID , nil
978+ }
979+
885980 vpcName := strings .Split (Options .VPCNames , "," )[0 ]
886- if specifiedVPCName , ok := service . GetAnnotations ()[ annotations . NodeBalancerBackendVPCName ]; ok {
981+ if vpcOk {
887982 vpcName = specifiedVPCName
888983 }
889984 vpcID , err := GetVPCID (ctx , l .client , vpcName )
890985 if err != nil {
891986 return 0 , err
892987 }
988+
893989 subnetName := strings .Split (Options .SubnetNames , "," )[0 ]
894- if specifiedSubnetName , ok := service . GetAnnotations ()[ annotations . NodeBalancerBackendSubnetName ]; ok {
990+ if subnetOk {
895991 subnetName = specifiedSubnetName
896992 }
993+
994+ // Use the VPC ID and Subnet Name to get the subnet ID
897995 return GetSubnetID (ctx , l .client , vpcID , subnetName )
898996}
899997
@@ -907,11 +1005,17 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam
9071005 configs := make ([]* linodego.NodeBalancerConfigCreateOptions , 0 , len (ports ))
9081006
9091007 subnetID := 0
1008+ if Options .NodeBalancerBackendIPv4SubnetID != 0 {
1009+ subnetID = Options .NodeBalancerBackendIPv4SubnetID
1010+ }
1011+ // Check for the NodeBalancerBackendIPv4Range annotation
9101012 backendIPv4Range , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendIPv4Range ]
9111013 if ok {
9121014 if err := validateNodeBalancerBackendIPv4Range (backendIPv4Range ); err != nil {
9131015 return nil , err
9141016 }
1017+ }
1018+ if Options .VPCNames != "" && ! Options .DisableNodeBalancerVPCBackends {
9151019 id , err := l .getSubnetIDForSVC (ctx , service )
9161020 if err != nil {
9171021 return nil , err
@@ -1088,9 +1192,9 @@ func getPortConfigAnnotation(service *v1.Service, port int) (portConfigAnnotatio
10881192}
10891193
10901194// getNodePrivateIP provides the Linode Backend IP the NodeBalancer will communicate with.
1091- // If a service specifies NodeBalancerBackendIPv4Range annotation , it will
1195+ // If CCM runs within VPC and DisableNodeBalancerVPCBackends is set to false , it will
10921196// use NodeInternalIP of node.
1093- // For services which don't have NodeBalancerBackendIPv4Range annotation,
1197+ // For services outside of VPC, it will use linode specific private IP address
10941198// Backend IP can be overwritten to the one specified using AnnLinodeNodePrivateIP
10951199// annotation over the NodeInternalIP.
10961200func getNodePrivateIP (node * v1.Node , subnetID int ) string {
0 commit comments