@@ -35,6 +35,57 @@ import (
3535var (
3636 errNoNodesAvailable = errors .New ("no nodes available for nodebalancer" )
3737 maxConnThrottleStringLen int = 20
38+
39+ // validProtocols is a map of valid protocols
40+ validProtocols = map [string ]bool {
41+ string (linodego .ProtocolTCP ): true ,
42+ string (linodego .ProtocolUDP ): true ,
43+ string (linodego .ProtocolHTTP ): true ,
44+ string (linodego .ProtocolHTTPS ): true ,
45+ }
46+ // validProxyProtocols is a map of valid proxy protocols
47+ validProxyProtocols = map [string ]bool {
48+ string (linodego .ProxyProtocolNone ): true ,
49+ string (linodego .ProxyProtocolV1 ): true ,
50+ string (linodego .ProxyProtocolV2 ): true ,
51+ }
52+ // validTCPAlgorithms is a map of valid TCP algorithms
53+ validTCPAlgorithms = map [string ]bool {
54+ string (linodego .AlgorithmRoundRobin ): true ,
55+ string (linodego .AlgorithmLeastConn ): true ,
56+ string (linodego .AlgorithmSource ): true ,
57+ }
58+ // validUDPAlgorithms is a map of valid UDP algorithms
59+ validUDPAlgorithms = map [string ]bool {
60+ string (linodego .AlgorithmRoundRobin ): true ,
61+ string (linodego .AlgorithmRingHash ): true ,
62+ string (linodego .AlgorithmLeastConn ): true ,
63+ }
64+ // validHTTPStickiness is a map of valid HTTP stickiness options
65+ validHTTPStickiness = map [string ]bool {
66+ string (linodego .StickinessNone ): true ,
67+ string (linodego .StickinessHTTPCookie ): true ,
68+ string (linodego .StickinessTable ): true ,
69+ }
70+ // validHTTPSStickiness is the same as validHTTPStickiness, but for HTTPS
71+ validHTTPSStickiness = map [string ]bool {
72+ string (linodego .StickinessNone ): true ,
73+ string (linodego .StickinessHTTPCookie ): true ,
74+ string (linodego .StickinessTable ): true ,
75+ }
76+ // validUDPStickiness is a map of valid UDP stickiness options
77+ validUDPStickiness = map [string ]bool {
78+ string (linodego .StickinessNone ): true ,
79+ string (linodego .StickinessSession ): true ,
80+ string (linodego .StickinessSourceIP ): true ,
81+ }
82+ // validNBConfigChecks is a map of valid NodeBalancer config checks
83+ validNBConfigChecks = map [string ]bool {
84+ string (linodego .CheckNone ): true ,
85+ string (linodego .CheckHTTP ): true ,
86+ string (linodego .CheckHTTPBody ): true ,
87+ string (linodego .CheckConnection ): true ,
88+ }
3889)
3990
4091type lbNotFoundError struct {
@@ -61,13 +112,19 @@ type portConfigAnnotation struct {
61112 TLSSecretName string `json:"tls-secret-name"`
62113 Protocol string `json:"protocol"`
63114 ProxyProtocol string `json:"proxy-protocol"`
115+ Algorithm string `json:"algorithm"`
116+ Stickiness string `json:"stickiness"`
117+ UDPCheckPort string `json:"udp-check-port"`
64118}
65119
66120type portConfig struct {
67121 TLSSecretName string
68122 Protocol linodego.ConfigProtocol
69123 ProxyProtocol linodego.ConfigProxyProtocol
70124 Port int
125+ Algorithm linodego.ConfigAlgorithm
126+ Stickiness linodego.ConfigStickiness
127+ UDPCheckPort int
71128}
72129
73130// newLoadbalancers returns a cloudprovider.LoadBalancer whose concrete type is a *loadbalancer.
@@ -351,14 +408,8 @@ func (l *loadbalancers) updateNodeBalancer(
351408
352409 // Add or overwrite configs for each of the Service's ports
353410 for _ , port := range service .Spec .Ports {
354- if port .Protocol == v1 .ProtocolUDP {
355- err := fmt .Errorf ("error updating NodeBalancer Config: ports with the UDP protocol are not supported" )
356- sentry .CaptureError (ctx , err )
357- return err
358- }
359-
360411 // Construct a new config for this port
361- newNBCfg , err := l .buildNodeBalancerConfig (ctx , service , int ( port . Port ) )
412+ newNBCfg , err := l .buildNodeBalancerConfig (ctx , service , port )
362413 if err != nil {
363414 sentry .CaptureError (ctx , err )
364415 return err
@@ -408,7 +459,7 @@ func (l *loadbalancers) updateNodeBalancer(
408459 subnetID = id
409460 }
410461 for _ , node := range nodes {
411- newNodeOpts := l .buildNodeBalancerNodeConfigRebuildOptions (node , port .NodePort , subnetID )
462+ newNodeOpts := l .buildNodeBalancerNodeConfigRebuildOptions (node , port .NodePort , subnetID , newNBCfg . Protocol )
412463 oldNodeID , ok := oldNBNodeIDs [newNodeOpts .Address ]
413464 if ok {
414465 newNodeOpts .ID = oldNodeID
@@ -726,22 +777,31 @@ func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName stri
726777 return l .client .CreateNodeBalancer (ctx , createOpts )
727778}
728779
729- func (l * loadbalancers ) buildNodeBalancerConfig (ctx context.Context , service * v1.Service , port int ) (linodego.NodeBalancerConfig , error ) {
780+ func (l * loadbalancers ) buildNodeBalancerConfig (ctx context.Context , service * v1.Service , port v1. ServicePort ) (linodego.NodeBalancerConfig , error ) {
730781 portConfigResult , err := getPortConfig (service , port )
731782 if err != nil {
732783 return linodego.NodeBalancerConfig {}, err
733784 }
734785
735- health , err := getHealthCheckType (service )
786+ health , err := getHealthCheckType (service , port )
736787 if err != nil {
737788 return linodego.NodeBalancerConfig {}, err
738789 }
739790
740791 config := linodego.NodeBalancerConfig {
741- Port : port ,
792+ Port : int ( port . Port ) ,
742793 Protocol : portConfigResult .Protocol ,
743794 ProxyProtocol : portConfigResult .ProxyProtocol ,
744795 Check : health ,
796+ Algorithm : portConfigResult .Algorithm ,
797+ }
798+
799+ if portConfigResult .Stickiness != "" {
800+ config .Stickiness = portConfigResult .Stickiness
801+ }
802+
803+ if portConfigResult .UDPCheckPort != 0 {
804+ config .UDPCheckPort = portConfigResult .UDPCheckPort
745805 }
746806
747807 if health == linodego .CheckHTTP || health == linodego .CheckHTTPBody {
@@ -784,7 +844,9 @@ func (l *loadbalancers) buildNodeBalancerConfig(ctx context.Context, service *v1
784844 config .CheckAttempts = checkAttempts
785845
786846 checkPassive := true
787- if cp , ok := service .GetAnnotations ()[annotations .AnnLinodeHealthCheckPassive ]; ok {
847+ if config .Protocol == linodego .ProtocolUDP {
848+ checkPassive = false
849+ } else if cp , ok := service .GetAnnotations ()[annotations .AnnLinodeHealthCheckPassive ]; ok {
788850 if checkPassive , err = strconv .ParseBool (cp ); err != nil {
789851 return config , err
790852 }
@@ -858,18 +920,14 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam
858920 }
859921
860922 for _ , port := range ports {
861- if port .Protocol == v1 .ProtocolUDP {
862- return nil , fmt .Errorf ("error creating NodeBalancer Config: ports with the UDP protocol are not supported" )
863- }
864-
865- config , err := l .buildNodeBalancerConfig (ctx , service , int (port .Port ))
923+ config , err := l .buildNodeBalancerConfig (ctx , service , port )
866924 if err != nil {
867925 return nil , err
868926 }
869927 createOpt := config .GetCreateOptions ()
870928
871929 for _ , n := range nodes {
872- createOpt .Nodes = append (createOpt .Nodes , l .buildNodeBalancerNodeConfigRebuildOptions (n , port .NodePort , subnetID ).NodeBalancerNodeCreateOptions )
930+ createOpt .Nodes = append (createOpt .Nodes , l .buildNodeBalancerNodeConfigRebuildOptions (n , port .NodePort , subnetID , config . Protocol ).NodeBalancerNodeCreateOptions )
873931 }
874932
875933 configs = append (configs , & createOpt )
@@ -889,17 +947,20 @@ func coerceString(str string, minLen, maxLen int, padding string) string {
889947 return str
890948}
891949
892- func (l * loadbalancers ) buildNodeBalancerNodeConfigRebuildOptions (node * v1.Node , nodePort int32 , subnetID int ) linodego.NodeBalancerConfigRebuildNodeOptions {
950+ func (l * loadbalancers ) buildNodeBalancerNodeConfigRebuildOptions (node * v1.Node , nodePort int32 , subnetID int , protocol linodego. ConfigProtocol ) linodego.NodeBalancerConfigRebuildNodeOptions {
893951 nodeOptions := linodego.NodeBalancerConfigRebuildNodeOptions {
894952 NodeBalancerNodeCreateOptions : linodego.NodeBalancerNodeCreateOptions {
895953 Address : fmt .Sprintf ("%v:%v" , getNodePrivateIP (node , subnetID ), nodePort ),
896954 // NodeBalancer backends must be 3-32 chars in length
897955 // If < 3 chars, pad node name with "node-" prefix
898956 Label : coerceString (node .Name , 3 , 32 , "node-" ),
899- Mode : "accept" ,
900957 Weight : 100 ,
901958 },
902959 }
960+ // Mode is not set for UDP protocol
961+ if protocol != linodego .ProtocolUDP {
962+ nodeOptions .Mode = "accept"
963+ }
903964 if subnetID != 0 {
904965 nodeOptions .SubnetID = subnetID
905966 }
@@ -937,57 +998,73 @@ func (l *loadbalancers) retrieveKubeClient() error {
937998 return nil
938999}
9391000
940- func getPortConfig (service * v1.Service , port int ) (portConfig , error ) {
1001+ func getPortConfig (service * v1.Service , port v1. ServicePort ) (portConfig , error ) {
9411002 portConfigResult := portConfig {}
942- portConfigAnnotationResult , err := getPortConfigAnnotation (service , port )
1003+ portConfigResult .Port = int (port .Port )
1004+
1005+ portConfigAnnotationResult , err := getPortConfigAnnotation (service , int (port .Port ))
9431006 if err != nil {
9441007 return portConfigResult , err
9451008 }
946- protocol := portConfigAnnotationResult .Protocol
947- if protocol == "" {
948- protocol = "tcp"
949- if p , ok := service .GetAnnotations ()[annotations .AnnLinodeDefaultProtocol ]; ok {
950- protocol = p
951- }
952- }
953- protocol = strings .ToLower (protocol )
9541009
955- proxyProtocol := portConfigAnnotationResult .ProxyProtocol
956- if proxyProtocol == "" {
957- proxyProtocol = string (linodego .ProxyProtocolNone )
958- for _ , ann := range []string {annotations .AnnLinodeDefaultProxyProtocol , annLinodeProxyProtocolDeprecated } {
959- if pp , ok := service .GetAnnotations ()[ann ]; ok {
960- proxyProtocol = pp
961- break
962- }
963- }
1010+ // validate and set protocol
1011+ protocol , err := getPortProtocol (portConfigAnnotationResult , service , port )
1012+ if err != nil {
1013+ return portConfigResult , err
9641014 }
1015+ portConfigResult .Protocol = linodego .ConfigProtocol (protocol )
9651016
966- if protocol != "tcp" && protocol != "http" && protocol != "https" {
967- return portConfigResult , fmt .Errorf ("invalid protocol: %q specified" , protocol )
1017+ // validate and set proxy protocol
1018+ proxyProtocol , err := getPortProxyProtocol (portConfigAnnotationResult , service , portConfigResult .Protocol )
1019+ if err != nil {
1020+ return portConfigResult , err
9681021 }
1022+ portConfigResult .ProxyProtocol = linodego .ConfigProxyProtocol (proxyProtocol )
9691023
970- switch proxyProtocol {
971- case string (linodego .ProxyProtocolNone ), string (linodego .ProxyProtocolV1 ), string (linodego .ProxyProtocolV2 ):
972- break
973- default :
974- return portConfigResult , fmt .Errorf ("invalid NodeBalancer proxy protocol value '%s'" , proxyProtocol )
1024+ // validate and set algorithm
1025+ algorithm , err := getPortAlgorithm (portConfigAnnotationResult , service , portConfigResult .Protocol )
1026+ if err != nil {
1027+ return portConfigResult , err
9751028 }
1029+ portConfigResult .Algorithm = linodego .ConfigAlgorithm (algorithm )
9761030
977- portConfigResult .Port = port
978- portConfigResult .Protocol = linodego .ConfigProtocol (protocol )
979- portConfigResult .ProxyProtocol = linodego .ConfigProxyProtocol (proxyProtocol )
1031+ // set TLS secret name. Its only used for TCP and HTTPS protocols
1032+ if protocol == string (linodego .ProtocolUDP ) && portConfigAnnotationResult .TLSSecretName != "" {
1033+ return portConfigResult , fmt .Errorf ("specifying TLS secret name is not supported for UDP" )
1034+ }
9801035 portConfigResult .TLSSecretName = portConfigAnnotationResult .TLSSecretName
9811036
1037+ // validate and set udp check port
1038+ udpCheckPort , err := getPortUDPCheckPort (portConfigAnnotationResult , service , portConfigResult .Protocol )
1039+ if err != nil {
1040+ return portConfigResult , err
1041+ }
1042+ if protocol == string (linodego .ProtocolUDP ) {
1043+ portConfigResult .UDPCheckPort = udpCheckPort
1044+ }
1045+
1046+ // validate and set stickiness
1047+ stickiness , err := getPortStickiness (portConfigAnnotationResult , service , portConfigResult .Protocol )
1048+ if err != nil {
1049+ return portConfigResult , err
1050+ }
1051+ // Stickiness is not supported for TCP protocol
1052+ if protocol != string (linodego .ProtocolTCP ) {
1053+ portConfigResult .Stickiness = linodego .ConfigStickiness (stickiness )
1054+ }
1055+
9821056 return portConfigResult , nil
9831057}
9841058
985- func getHealthCheckType (service * v1.Service ) (linodego.ConfigCheck , error ) {
1059+ func getHealthCheckType (service * v1.Service , port v1. ServicePort ) (linodego.ConfigCheck , error ) {
9861060 hType , ok := service .GetAnnotations ()[annotations .AnnLinodeHealthCheckType ]
9871061 if ! ok {
1062+ if port .Protocol == v1 .ProtocolUDP {
1063+ return linodego .CheckNone , nil
1064+ }
9881065 return linodego .CheckConnection , nil
9891066 }
990- if hType != "none" && hType != "connection" && hType != "http" && hType != "http_body" {
1067+ if ! validNBConfigChecks [ hType ] {
9911068 return "" , fmt .Errorf ("invalid health check type: %q specified in annotation: %q" , hType , annotations .AnnLinodeHealthCheckType )
9921069 }
9931070 return linodego .ConfigCheck (hType ), nil
0 commit comments