@@ -383,8 +383,18 @@ func (l *loadbalancers) updateNodeBalancer(
383383 }
384384 // Add all of the Nodes to the config
385385 newNBNodes := make ([]linodego.NodeBalancerConfigRebuildNodeOptions , 0 , len (nodes ))
386+ subnetID := 0
387+ _ , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendIPv4Range ]
388+ if ok {
389+ id , err := l .getSubnetIDForSVC (ctx , service )
390+ if err != nil {
391+ sentry .CaptureError (ctx , err )
392+ return fmt .Errorf ("Error getting subnet ID for service %s: %v" , service .Name , err )
393+ }
394+ subnetID = id
395+ }
386396 for _ , node := range nodes {
387- newNodeOpts := l .buildNodeBalancerNodeConfigRebuildOptions (node , port .NodePort )
397+ newNodeOpts := l .buildNodeBalancerNodeConfigRebuildOptions (node , port .NodePort , subnetID )
388398 oldNodeID , ok := oldNBNodeIDs [newNodeOpts .Address ]
389399 if ok {
390400 newNodeOpts .ID = oldNodeID
@@ -652,6 +662,20 @@ func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName stri
652662 Type : nbType ,
653663 }
654664
665+ backendIPv4Range , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendIPv4Range ]
666+ if ok {
667+ subnetID , err := l .getSubnetIDForSVC (ctx , service )
668+ if err != nil {
669+ return nil , err
670+ }
671+ createOpts .VPCs = []linodego.NodeBalancerVPCOptions {
672+ {
673+ SubnetID : subnetID ,
674+ IPv4Range : backendIPv4Range ,
675+ },
676+ }
677+ }
678+
655679 fwid , ok := service .GetAnnotations ()[annotations .AnnLinodeCloudFirewallID ]
656680 if ok {
657681 firewallID , err := strconv .Atoi (fwid )
@@ -768,6 +792,28 @@ func (l *loadbalancers) addTLSCert(ctx context.Context, service *v1.Service, nbC
768792 return nil
769793}
770794
795+ // getSubnetIDForSVC returns the subnet ID for the service's VPC and subnet.
796+ // By default, first VPCName and SubnetName are used to calculate subnet id for the service.
797+ // If the service has annotations specifying VPCName and SubnetName, they are used instead.
798+ func (l * loadbalancers ) getSubnetIDForSVC (ctx context.Context , service * v1.Service ) (int , error ) {
799+ if Options .VPCNames == "" {
800+ return 0 , fmt .Errorf ("CCM not configured with VPC, cannot create NodeBalancer with specified annotation" )
801+ }
802+ vpcName := strings .Split (Options .VPCNames , "," )[0 ]
803+ if specifiedVPCName , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendVPCName ]; ok {
804+ vpcName = specifiedVPCName
805+ }
806+ vpcID , err := GetVPCID (ctx , l .client , vpcName )
807+ if err != nil {
808+ return 0 , err
809+ }
810+ subnetName := strings .Split (Options .SubnetNames , "," )[0 ]
811+ if specifiedSubnetName , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendSubnetName ]; ok {
812+ subnetName = specifiedSubnetName
813+ }
814+ return GetSubnetID (ctx , l .client , vpcID , subnetName )
815+ }
816+
771817// buildLoadBalancerRequest returns a linodego.NodeBalancer
772818// requests for service across nodes.
773819func (l * loadbalancers ) buildLoadBalancerRequest (ctx context.Context , clusterName string , service * v1.Service , nodes []* v1.Node ) (* linodego.NodeBalancer , error ) {
@@ -777,6 +823,16 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam
777823 ports := service .Spec .Ports
778824 configs := make ([]* linodego.NodeBalancerConfigCreateOptions , 0 , len (ports ))
779825
826+ subnetID := 0
827+ _ , ok := service .GetAnnotations ()[annotations .NodeBalancerBackendIPv4Range ]
828+ if ok {
829+ id , err := l .getSubnetIDForSVC (ctx , service )
830+ if err != nil {
831+ return nil , err
832+ }
833+ subnetID = id
834+ }
835+
780836 for _ , port := range ports {
781837 if port .Protocol == v1 .ProtocolUDP {
782838 return nil , fmt .Errorf ("error creating NodeBalancer Config: ports with the UDP protocol are not supported" )
@@ -789,7 +845,7 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam
789845 createOpt := config .GetCreateOptions ()
790846
791847 for _ , n := range nodes {
792- createOpt .Nodes = append (createOpt .Nodes , l .buildNodeBalancerNodeConfigRebuildOptions (n , port .NodePort ).NodeBalancerNodeCreateOptions )
848+ createOpt .Nodes = append (createOpt .Nodes , l .buildNodeBalancerNodeConfigRebuildOptions (n , port .NodePort , subnetID ).NodeBalancerNodeCreateOptions )
793849 }
794850
795851 configs = append (configs , & createOpt )
@@ -809,17 +865,21 @@ func coerceString(s string, minLen, maxLen int, padding string) string {
809865 return s
810866}
811867
812- func (l * loadbalancers ) buildNodeBalancerNodeConfigRebuildOptions (node * v1.Node , nodePort int32 ) linodego.NodeBalancerConfigRebuildNodeOptions {
813- return linodego.NodeBalancerConfigRebuildNodeOptions {
868+ func (l * loadbalancers ) buildNodeBalancerNodeConfigRebuildOptions (node * v1.Node , nodePort int32 , subnetID int ) linodego.NodeBalancerConfigRebuildNodeOptions {
869+ nodeOptions := linodego.NodeBalancerConfigRebuildNodeOptions {
814870 NodeBalancerNodeCreateOptions : linodego.NodeBalancerNodeCreateOptions {
815- Address : fmt .Sprintf ("%v:%v" , getNodePrivateIP (node ), nodePort ),
871+ Address : fmt .Sprintf ("%v:%v" , getNodePrivateIP (node , subnetID ), nodePort ),
816872 // NodeBalancer backends must be 3-32 chars in length
817873 // If < 3 chars, pad node name with "node-" prefix
818874 Label : coerceString (node .Name , 3 , 32 , "node-" ),
819875 Mode : "accept" ,
820876 Weight : 100 ,
821877 },
822878 }
879+ if subnetID != 0 {
880+ nodeOptions .NodeBalancerNodeCreateOptions .SubnetID = subnetID
881+ }
882+ return nodeOptions
823883}
824884
825885func (l * loadbalancers ) retrieveKubeClient () error {
@@ -926,13 +986,17 @@ func getPortConfigAnnotation(service *v1.Service, port int) (portConfigAnnotatio
926986 return annotation , nil
927987}
928988
929- // getNodePrivateIP should provide the Linode Private IP the NodeBalance
930- // will communicate with. When using a VLAN or VPC for the Kubernetes cluster
931- // network, this will not be the NodeInternalIP, so this prefers an annotation
932- // cluster operators may specify in such a situation.
933- func getNodePrivateIP (node * v1.Node ) string {
934- if address , exists := node .Annotations [annotations .AnnLinodeNodePrivateIP ]; exists {
935- return address
989+ // getNodePrivateIP provides the Linode Backend IP the NodeBalancer will communicate with.
990+ // If a service specifies NodeBalancerBackendIPv4Range annotation, it will
991+ // use NodeInternalIP of node.
992+ // For services which don't have NodeBalancerBackendIPv4Range annotation,
993+ // Backend IP can be overwritten to the one specified using AnnLinodeNodePrivateIP
994+ // annotation over the NodeInternalIP.
995+ func getNodePrivateIP (node * v1.Node , subnetID int ) string {
996+ if subnetID == 0 {
997+ if address , exists := node .Annotations [annotations .AnnLinodeNodePrivateIP ]; exists {
998+ return address
999+ }
9361000 }
9371001
9381002 klog .Infof ("Node %s, assigned IP addresses: %v" , node .Name , node .Status .Addresses )
0 commit comments