github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/configs/transportserver.go (about) 1 package configs 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" 8 conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" 9 ) 10 11 const nginxNonExistingUnixSocket = "unix:/var/lib/nginx/non-existing-unix-socket.sock" 12 13 // TransportServerEx holds a TransportServer along with the resources referenced by it. 14 type TransportServerEx struct { 15 ListenerPort int 16 TransportServer *conf_v1alpha1.TransportServer 17 Endpoints map[string][]string 18 PodsByIP map[string]string 19 } 20 21 func (tsEx *TransportServerEx) String() string { 22 if tsEx == nil { 23 return "<nil>" 24 } 25 26 if tsEx.TransportServer == nil { 27 return "TransportServerEx has no TransportServer" 28 } 29 30 return fmt.Sprintf("%s/%s", tsEx.TransportServer.Namespace, tsEx.TransportServer.Name) 31 } 32 33 // generateTransportServerConfig generates a full configuration for a TransportServer. 34 func generateTransportServerConfig(transportServerEx *TransportServerEx, listenerPort int, isPlus bool) *version2.TransportServerConfig { 35 upstreamNamer := newUpstreamNamerForTransportServer(transportServerEx.TransportServer) 36 37 upstreams := generateStreamUpstreams(transportServerEx, upstreamNamer, isPlus) 38 39 healthCheck, match := generateTransportServerHealthCheck(transportServerEx.TransportServer.Spec.Action.Pass, 40 upstreamNamer.GetNameForUpstream(transportServerEx.TransportServer.Spec.Action.Pass), 41 transportServerEx.TransportServer.Spec.Upstreams) 42 43 var proxyRequests, proxyResponses *int 44 var connectTimeout, nextUpstreamTimeout string 45 var nextUpstream bool 46 var nextUpstreamTries int 47 if transportServerEx.TransportServer.Spec.UpstreamParameters != nil { 48 proxyRequests = transportServerEx.TransportServer.Spec.UpstreamParameters.UDPRequests 49 proxyResponses = transportServerEx.TransportServer.Spec.UpstreamParameters.UDPResponses 50 51 nextUpstream = transportServerEx.TransportServer.Spec.UpstreamParameters.NextUpstream 52 if nextUpstream { 53 nextUpstreamTries = transportServerEx.TransportServer.Spec.UpstreamParameters.NextUpstreamTries 54 nextUpstreamTimeout = transportServerEx.TransportServer.Spec.UpstreamParameters.NextUpstreamTimeout 55 } 56 57 connectTimeout = transportServerEx.TransportServer.Spec.UpstreamParameters.ConnectTimeout 58 } 59 60 var proxyTimeout string 61 if transportServerEx.TransportServer.Spec.SessionParameters != nil { 62 proxyTimeout = transportServerEx.TransportServer.Spec.SessionParameters.Timeout 63 } 64 65 serverSnippets := generateSnippets(true, transportServerEx.TransportServer.Spec.ServerSnippets, []string{}) 66 67 streamSnippets := generateSnippets(true, transportServerEx.TransportServer.Spec.StreamSnippets, []string{}) 68 69 statusZone := transportServerEx.TransportServer.Spec.Listener.Name 70 if transportServerEx.TransportServer.Spec.Listener.Name == conf_v1alpha1.TLSPassthroughListenerName { 71 statusZone = transportServerEx.TransportServer.Spec.Host 72 } 73 74 tsConfig := &version2.TransportServerConfig{ 75 Server: version2.StreamServer{ 76 TLSPassthrough: transportServerEx.TransportServer.Spec.Listener.Name == conf_v1alpha1.TLSPassthroughListenerName, 77 UnixSocket: generateUnixSocket(transportServerEx), 78 Port: listenerPort, 79 UDP: transportServerEx.TransportServer.Spec.Listener.Protocol == "UDP", 80 StatusZone: statusZone, 81 ProxyRequests: proxyRequests, 82 ProxyResponses: proxyResponses, 83 ProxyPass: upstreamNamer.GetNameForUpstream(transportServerEx.TransportServer.Spec.Action.Pass), 84 Name: transportServerEx.TransportServer.Name, 85 Namespace: transportServerEx.TransportServer.Namespace, 86 ProxyConnectTimeout: generateTimeWithDefault(connectTimeout, "60s"), 87 ProxyTimeout: generateTimeWithDefault(proxyTimeout, "10m"), 88 ProxyNextUpstream: nextUpstream, 89 ProxyNextUpstreamTimeout: generateTimeWithDefault(nextUpstreamTimeout, "0s"), 90 ProxyNextUpstreamTries: nextUpstreamTries, 91 HealthCheck: healthCheck, 92 ServerSnippets: serverSnippets, 93 }, 94 Match: match, 95 Upstreams: upstreams, 96 StreamSnippets: streamSnippets, 97 } 98 99 return tsConfig 100 } 101 102 func generateUnixSocket(transportServerEx *TransportServerEx) string { 103 if transportServerEx.TransportServer.Spec.Listener.Name == conf_v1alpha1.TLSPassthroughListenerName { 104 return fmt.Sprintf("unix:/var/lib/nginx/passthrough-%s_%s.sock", transportServerEx.TransportServer.Namespace, transportServerEx.TransportServer.Name) 105 } 106 107 return "" 108 } 109 110 func generateStreamUpstreams(transportServerEx *TransportServerEx, upstreamNamer *upstreamNamer, isPlus bool) []version2.StreamUpstream { 111 var upstreams []version2.StreamUpstream 112 113 for _, u := range transportServerEx.TransportServer.Spec.Upstreams { 114 115 // subselector is not supported yet in TransportServer upstreams. That's why we pass "nil" here 116 endpointsKey := GenerateEndpointsKey(transportServerEx.TransportServer.Namespace, u.Service, nil, uint16(u.Port)) 117 endpoints := transportServerEx.Endpoints[endpointsKey] 118 119 ups := generateStreamUpstream(u, upstreamNamer, endpoints, isPlus) 120 121 ups.UpstreamLabels.Service = u.Service 122 ups.UpstreamLabels.ResourceType = "transportserver" 123 ups.UpstreamLabels.ResourceName = transportServerEx.TransportServer.Name 124 ups.UpstreamLabels.ResourceNamespace = transportServerEx.TransportServer.Namespace 125 126 upstreams = append(upstreams, ups) 127 } 128 129 return upstreams 130 } 131 132 func generateTransportServerHealthCheck(upstreamName string, generatedUpstreamName string, upstreams []conf_v1alpha1.Upstream) (*version2.StreamHealthCheck, *version2.Match) { 133 var hc *version2.StreamHealthCheck 134 var match *version2.Match 135 136 for _, u := range upstreams { 137 if u.Name == upstreamName { 138 if u.HealthCheck == nil || !u.HealthCheck.Enabled { 139 return nil, nil 140 } 141 hc = generateTransportServerHealthCheckWithDefaults(u) 142 143 hc.Enabled = u.HealthCheck.Enabled 144 hc.Interval = generateTimeWithDefault(u.HealthCheck.Interval, hc.Interval) 145 hc.Jitter = generateTimeWithDefault(u.HealthCheck.Jitter, hc.Jitter) 146 hc.Timeout = generateTimeWithDefault(u.HealthCheck.Timeout, hc.Timeout) 147 148 if u.HealthCheck.Fails > 0 { 149 hc.Fails = u.HealthCheck.Fails 150 } 151 152 if u.HealthCheck.Passes > 0 { 153 hc.Passes = u.HealthCheck.Passes 154 } 155 156 if u.HealthCheck.Port > 0 { 157 hc.Port = u.HealthCheck.Port 158 } 159 160 if u.HealthCheck.Match != nil { 161 name := "match_" + generatedUpstreamName 162 match = generateHealthCheckMatch(u.HealthCheck.Match, name) 163 hc.Match = name 164 } 165 166 break 167 } 168 } 169 170 return hc, match 171 } 172 173 func generateTransportServerHealthCheckWithDefaults(up conf_v1alpha1.Upstream) *version2.StreamHealthCheck { 174 return &version2.StreamHealthCheck{ 175 Enabled: false, 176 Timeout: "5s", 177 Jitter: "0s", 178 Port: up.Port, 179 Interval: "5s", 180 Passes: 1, 181 Fails: 1, 182 Match: "", 183 } 184 } 185 186 func generateHealthCheckMatch(match *conf_v1alpha1.Match, name string) *version2.Match { 187 var modifier string 188 var expect string 189 190 if strings.HasPrefix(match.Expect, "~*") { 191 modifier = "~*" 192 expect = strings.TrimPrefix(match.Expect, "~*") 193 } else if strings.HasPrefix(match.Expect, "~") { 194 modifier = "~" 195 expect = strings.TrimPrefix(match.Expect, "~") 196 } else { 197 expect = match.Expect 198 } 199 200 return &version2.Match{ 201 Name: name, 202 Send: match.Send, 203 ExpectRegexModifier: modifier, 204 Expect: expect, 205 } 206 } 207 208 func generateStreamUpstream(upstream conf_v1alpha1.Upstream, upstreamNamer *upstreamNamer, endpoints []string, isPlus bool) version2.StreamUpstream { 209 var upsServers []version2.StreamUpstreamServer 210 211 name := upstreamNamer.GetNameForUpstream(upstream.Name) 212 maxFails := generateIntFromPointer(upstream.MaxFails, 1) 213 maxConns := generateIntFromPointer(upstream.MaxConns, 0) 214 failTimeout := generateTimeWithDefault(upstream.FailTimeout, "10s") 215 216 for _, e := range endpoints { 217 s := version2.StreamUpstreamServer{ 218 Address: e, 219 MaxFails: maxFails, 220 FailTimeout: failTimeout, 221 MaxConnections: maxConns, 222 } 223 224 upsServers = append(upsServers, s) 225 } 226 227 if !isPlus && len(endpoints) == 0 { 228 upsServers = append(upsServers, version2.StreamUpstreamServer{ 229 Address: nginxNonExistingUnixSocket, 230 MaxFails: maxFails, 231 FailTimeout: failTimeout, 232 }) 233 } 234 235 return version2.StreamUpstream{ 236 Name: name, 237 Servers: upsServers, 238 LoadBalancingMethod: generateLoadBalancingMethod(upstream.LoadBalancingMethod), 239 } 240 } 241 242 func generateLoadBalancingMethod(method string) string { 243 if method == "" { 244 // By default, if unspecified, Nginx uses the 'round_robin' load balancing method. 245 // We override this default which suits the Ingress Controller better. 246 return "random two least_conn" 247 } 248 if method == "round_robin" { 249 // By default, Nginx uses round robin. We select this method by not specifying any method. 250 return "" 251 } 252 return method 253 }