@@ -35,6 +35,57 @@ import (
35
35
var (
36
36
errNoNodesAvailable = errors .New ("no nodes available for nodebalancer" )
37
37
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
+ }
38
89
)
39
90
40
91
type lbNotFoundError struct {
@@ -61,13 +112,19 @@ type portConfigAnnotation struct {
61
112
TLSSecretName string `json:"tls-secret-name"`
62
113
Protocol string `json:"protocol"`
63
114
ProxyProtocol string `json:"proxy-protocol"`
115
+ Algorithm string `json:"algorithm"`
116
+ Stickiness string `json:"stickiness"`
117
+ UDPCheckPort string `json:"udp-check-port"`
64
118
}
65
119
66
120
type portConfig struct {
67
121
TLSSecretName string
68
122
Protocol linodego.ConfigProtocol
69
123
ProxyProtocol linodego.ConfigProxyProtocol
70
124
Port int
125
+ Algorithm linodego.ConfigAlgorithm
126
+ Stickiness linodego.ConfigStickiness
127
+ UDPCheckPort int
71
128
}
72
129
73
130
// newLoadbalancers returns a cloudprovider.LoadBalancer whose concrete type is a *loadbalancer.
@@ -351,14 +408,8 @@ func (l *loadbalancers) updateNodeBalancer(
351
408
352
409
// Add or overwrite configs for each of the Service's ports
353
410
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
-
360
411
// 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 )
362
413
if err != nil {
363
414
sentry .CaptureError (ctx , err )
364
415
return err
@@ -408,7 +459,7 @@ func (l *loadbalancers) updateNodeBalancer(
408
459
subnetID = id
409
460
}
410
461
for _ , node := range nodes {
411
- newNodeOpts := l .buildNodeBalancerNodeConfigRebuildOptions (node , port .NodePort , subnetID )
462
+ newNodeOpts := l .buildNodeBalancerNodeConfigRebuildOptions (node , port .NodePort , subnetID , newNBCfg . Protocol )
412
463
oldNodeID , ok := oldNBNodeIDs [newNodeOpts .Address ]
413
464
if ok {
414
465
newNodeOpts .ID = oldNodeID
@@ -726,22 +777,31 @@ func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName stri
726
777
return l .client .CreateNodeBalancer (ctx , createOpts )
727
778
}
728
779
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 ) {
730
781
portConfigResult , err := getPortConfig (service , port )
731
782
if err != nil {
732
783
return linodego.NodeBalancerConfig {}, err
733
784
}
734
785
735
- health , err := getHealthCheckType (service )
786
+ health , err := getHealthCheckType (service , port )
736
787
if err != nil {
737
788
return linodego.NodeBalancerConfig {}, err
738
789
}
739
790
740
791
config := linodego.NodeBalancerConfig {
741
- Port : port ,
792
+ Port : int ( port . Port ) ,
742
793
Protocol : portConfigResult .Protocol ,
743
794
ProxyProtocol : portConfigResult .ProxyProtocol ,
744
795
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
745
805
}
746
806
747
807
if health == linodego .CheckHTTP || health == linodego .CheckHTTPBody {
@@ -784,7 +844,9 @@ func (l *loadbalancers) buildNodeBalancerConfig(ctx context.Context, service *v1
784
844
config .CheckAttempts = checkAttempts
785
845
786
846
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 {
788
850
if checkPassive , err = strconv .ParseBool (cp ); err != nil {
789
851
return config , err
790
852
}
@@ -858,18 +920,14 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam
858
920
}
859
921
860
922
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 )
866
924
if err != nil {
867
925
return nil , err
868
926
}
869
927
createOpt := config .GetCreateOptions ()
870
928
871
929
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 )
873
931
}
874
932
875
933
configs = append (configs , & createOpt )
@@ -889,17 +947,20 @@ func coerceString(str string, minLen, maxLen int, padding string) string {
889
947
return str
890
948
}
891
949
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 {
893
951
nodeOptions := linodego.NodeBalancerConfigRebuildNodeOptions {
894
952
NodeBalancerNodeCreateOptions : linodego.NodeBalancerNodeCreateOptions {
895
953
Address : fmt .Sprintf ("%v:%v" , getNodePrivateIP (node , subnetID ), nodePort ),
896
954
// NodeBalancer backends must be 3-32 chars in length
897
955
// If < 3 chars, pad node name with "node-" prefix
898
956
Label : coerceString (node .Name , 3 , 32 , "node-" ),
899
- Mode : "accept" ,
900
957
Weight : 100 ,
901
958
},
902
959
}
960
+ // Mode is not set for UDP protocol
961
+ if protocol != linodego .ProtocolUDP {
962
+ nodeOptions .Mode = "accept"
963
+ }
903
964
if subnetID != 0 {
904
965
nodeOptions .SubnetID = subnetID
905
966
}
@@ -937,57 +998,73 @@ func (l *loadbalancers) retrieveKubeClient() error {
937
998
return nil
938
999
}
939
1000
940
- func getPortConfig (service * v1.Service , port int ) (portConfig , error ) {
1001
+ func getPortConfig (service * v1.Service , port v1. ServicePort ) (portConfig , error ) {
941
1002
portConfigResult := portConfig {}
942
- portConfigAnnotationResult , err := getPortConfigAnnotation (service , port )
1003
+ portConfigResult .Port = int (port .Port )
1004
+
1005
+ portConfigAnnotationResult , err := getPortConfigAnnotation (service , int (port .Port ))
943
1006
if err != nil {
944
1007
return portConfigResult , err
945
1008
}
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 )
954
1009
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
964
1014
}
1015
+ portConfigResult .Protocol = linodego .ConfigProtocol (protocol )
965
1016
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
968
1021
}
1022
+ portConfigResult .ProxyProtocol = linodego .ConfigProxyProtocol (proxyProtocol )
969
1023
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
975
1028
}
1029
+ portConfigResult .Algorithm = linodego .ConfigAlgorithm (algorithm )
976
1030
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
+ }
980
1035
portConfigResult .TLSSecretName = portConfigAnnotationResult .TLSSecretName
981
1036
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
+
982
1056
return portConfigResult , nil
983
1057
}
984
1058
985
- func getHealthCheckType (service * v1.Service ) (linodego.ConfigCheck , error ) {
1059
+ func getHealthCheckType (service * v1.Service , port v1. ServicePort ) (linodego.ConfigCheck , error ) {
986
1060
hType , ok := service .GetAnnotations ()[annotations .AnnLinodeHealthCheckType ]
987
1061
if ! ok {
1062
+ if port .Protocol == v1 .ProtocolUDP {
1063
+ return linodego .CheckNone , nil
1064
+ }
988
1065
return linodego .CheckConnection , nil
989
1066
}
990
- if hType != "none" && hType != "connection" && hType != "http" && hType != "http_body" {
1067
+ if ! validNBConfigChecks [ hType ] {
991
1068
return "" , fmt .Errorf ("invalid health check type: %q specified in annotation: %q" , hType , annotations .AnnLinodeHealthCheckType )
992
1069
}
993
1070
return linodego .ConfigCheck (hType ), nil
0 commit comments