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  }