Skip to content

Commit

Permalink
Add support for ExternalName Services for vs/vsr
Browse files Browse the repository at this point in the history
  • Loading branch information
Raul Marrero committed Aug 12, 2019
1 parent 2aefb44 commit c9bc9c8
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 73 deletions.
2 changes: 1 addition & 1 deletion docs/virtualserver-and-virtualserverroute.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ tls:
| Field | Description | Type | Required |
| ----- | ----------- | ---- | -------- |
| `name` | The name of the upstream. Must be a valid DNS label as defined in RFC 1035. For example, `hello` and `upstream-123` are valid. The name must be unique among all upstreams of the resource. | `string` | Yes |
| `service` | The name of a [service](https://kubernetes.io/docs/concepts/services-networking/service/). The service must belong to the same namespace as the resource. If the service doesn't exist, NGINX will assume the service has zero endpoints and return a `502` response for requests for this upstream. | `string` | Yes |
| `service` | The name of a [service](https://kubernetes.io/docs/concepts/services-networking/service/). The service must belong to the same namespace as the resource. If the service doesn't exist, NGINX will assume the service has zero endpoints and return a `502` response for requests for this upstream. For NGINX Plus only, services of type [ExternalName](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) are also supported (check the [prerequisites](../examples/externalname-services#prerequisites)). | `string` | Yes |
| `port` | The port of the service. If the service doesn't define that port, NGINX will assume the service has zero endpoints and return a `502` response for requests for this upstream. The port must fall into the range `1..65553`. | `uint16` | Yes |
| `lb-method` | The load [balancing method](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#choosing-a-load-balancing-method). To use the round-robin method, specify `round_robin`. The default is specified in the `lb-method` ConfigMap key. | `string` | No |
| `fail-timeout` | The time during which the specified number of unsuccessful attempts to communicate with an upstream server should happen to consider the server unavailable. See the [fail_timeout](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#fail_timeout) parameter of the server directive. The default is set in the `fail-timeout` ConfigMap key. | `string` | No |
Expand Down
3 changes: 1 addition & 2 deletions internal/configs/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ func (cnf *Configurator) addOrUpdateVirtualServer(virtualServerEx *VirtualServer
if virtualServerEx.TLSSecret != nil {
tlsPemFileName = cnf.addOrUpdateTLSSecret(virtualServerEx.TLSSecret)
}

vsCfg := generateVirtualServerConfig(virtualServerEx, tlsPemFileName, cnf.cfgParams, cnf.isPlus)
vsCfg := generateVirtualServerConfig(virtualServerEx, tlsPemFileName, cnf.cfgParams, cnf.isPlus, cnf.IsResolverConfigured())

name := getFileNameForVirtualServer(virtualServerEx.VirtualServer)
content, err := cnf.templateExecutorV2.ExecuteVirtualServerTemplate(&vsCfg)
Expand Down
1 change: 1 addition & 0 deletions internal/configs/version2/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type UpstreamServer struct {
MaxFails int
MaxConns int
FailTimeout string
Resolve bool
}

// Server defines a server.
Expand Down
2 changes: 1 addition & 1 deletion internal/configs/version2/nginx-plus.virtualserver.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ upstream {{ $u.Name }} {
{{ if $u.LBMethod }}{{ $u.LBMethod }};{{ end }}

{{ range $s := $u.Servers }}
server {{ $s.Address }} max_fails={{ $s.MaxFails }} fail_timeout={{ $s.FailTimeout }} max_conns={{ $s.MaxConns }};
server {{ $s.Address }} max_fails={{ $s.MaxFails }} fail_timeout={{ $s.FailTimeout }} max_conns={{ $s.MaxConns }}{{ if $s.Resolve }} resolve{{ end }};
{{ end }}

{{ if $u.Keepalive }}
Expand Down
65 changes: 47 additions & 18 deletions internal/configs/virtualserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"github.com/golang/glog"
"github.com/nginxinc/kubernetes-ingress/internal/nginx"
api_v1 "k8s.io/api/core/v1"

Expand All @@ -19,6 +20,7 @@ type VirtualServerEx struct {
Endpoints map[string][]string
TLSSecret *api_v1.Secret
VirtualServerRoutes []*conf_v1alpha1.VirtualServerRoute
ExternalNameSvcs map[string]bool
}

func (vsx *VirtualServerEx) String() string {
Expand Down Expand Up @@ -167,7 +169,29 @@ func generateUpstreamStatusMatch(upstreamName string, status string) version2.St
}
}

func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileName string, baseCfgParams *ConfigParams, isPlus bool) version2.VirtualServerConfig {
// GenerateExternalNameSvcKey returns the key to identify an ExternalName service.
func GenerateExternalNameSvcKey(namespace string, service string) string {
return fmt.Sprintf("%v/%v", namespace, service)
}

func generateEndpointsForUpstream(namespace string, upstream conf_v1alpha1.Upstream, virtualServerEx *VirtualServerEx, isResolverConfigured bool, isPlus bool) []string {
endpointsKey := GenerateEndpointsKey(namespace, upstream.Service, upstream.Port)
externalNameSvcKey := GenerateExternalNameSvcKey(namespace, upstream.Service)
endpoints, _ := virtualServerEx.Endpoints[endpointsKey]
if !isPlus && len(endpoints) == 0 {
return []string{nginx502Server}
}

_, isExternalNameSvc := virtualServerEx.ExternalNameSvcs[externalNameSvcKey]
if isExternalNameSvc && !isResolverConfigured {
glog.Warningf("A resolver must be configured for Type ExternalName service %s, no upstream servers will be created", upstream.Service)
endpoints = []string{}
}

return endpoints
}

func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileName string, baseCfgParams *ConfigParams, isPlus bool, isResolverConfigured bool) version2.VirtualServerConfig {
ssl := generateSSLConfig(virtualServerEx.VirtualServer.Spec.TLS, tlsPemFileName, baseCfgParams)

// crUpstreams maps an UpstreamName to its conf_v1alpha1.Upstream as they are generated
Expand All @@ -183,8 +207,11 @@ func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileNam
// generate upstreams for VirtualServer
for _, u := range virtualServerEx.VirtualServer.Spec.Upstreams {
upstreamName := virtualServerUpstreamNamer.GetNameForUpstream(u.Name)
endpointsKey := GenerateEndpointsKey(virtualServerEx.VirtualServer.Namespace, u.Service, u.Port)
ups := generateUpstream(upstreamName, u, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams)
upstreamNamespace := virtualServerEx.VirtualServer.Namespace
endpoints := generateEndpointsForUpstream(upstreamNamespace, u, virtualServerEx, isResolverConfigured, isPlus)
// isExternalNameSvc is always false for OSS
_, isExternalNameSvc := virtualServerEx.ExternalNameSvcs[GenerateExternalNameSvcKey(upstreamNamespace, u.Service)]
ups := generateUpstream(upstreamName, u, isExternalNameSvc, endpoints, baseCfgParams)
upstreams = append(upstreams, ups)
crUpstreams[upstreamName] = u

Expand All @@ -200,8 +227,11 @@ func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileNam
upstreamNamer := newUpstreamNamerForVirtualServerRoute(virtualServerEx.VirtualServer, vsr)
for _, u := range vsr.Spec.Upstreams {
upstreamName := upstreamNamer.GetNameForUpstream(u.Name)
endpointsKey := GenerateEndpointsKey(vsr.Namespace, u.Service, u.Port)
ups := generateUpstream(upstreamName, u, virtualServerEx.Endpoints[endpointsKey], isPlus, baseCfgParams)
upstreamNamespace := vsr.Namespace
endpoints := generateEndpointsForUpstream(upstreamNamespace, u, virtualServerEx, isResolverConfigured, isPlus)
// isExternalNameSvc is always false for OSS
_, isExternalNameSvc := virtualServerEx.ExternalNameSvcs[GenerateExternalNameSvcKey(upstreamNamespace, u.Service)]
ups := generateUpstream(upstreamName, u, isExternalNameSvc, endpoints, baseCfgParams)
upstreams = append(upstreams, ups)
crUpstreams[upstreamName] = u

Expand Down Expand Up @@ -302,7 +332,7 @@ func generateVirtualServerConfig(virtualServerEx *VirtualServerEx, tlsPemFileNam
}
}

func generateUpstream(upstreamName string, upstream conf_v1alpha1.Upstream, endpoints []string, isPlus bool, cfgParams *ConfigParams) version2.Upstream {
func generateUpstream(upstreamName string, upstream conf_v1alpha1.Upstream, isExternalNameSvc bool, endpoints []string, cfgParams *ConfigParams) version2.Upstream {
var upsServers []version2.UpstreamServer

for _, e := range endpoints {
Expand All @@ -311,16 +341,7 @@ func generateUpstream(upstreamName string, upstream conf_v1alpha1.Upstream, endp
MaxFails: generateIntFromPointer(upstream.MaxFails, cfgParams.MaxFails),
FailTimeout: generateString(upstream.FailTimeout, cfgParams.FailTimeout),
MaxConns: generateIntFromPointer(upstream.MaxConns, cfgParams.MaxConns),
}
upsServers = append(upsServers, s)
}

if !isPlus && len(upsServers) == 0 {
s := version2.UpstreamServer{
Address: nginx502Server,
MaxFails: generateIntFromPointer(upstream.MaxFails, cfgParams.MaxFails),
FailTimeout: generateString(upstream.FailTimeout, cfgParams.FailTimeout),
MaxConns: generateIntFromPointer(upstream.MaxConns, cfgParams.MaxConns),
Resolve: isExternalNameSvc,
}
upsServers = append(upsServers, s)
}
Expand Down Expand Up @@ -630,18 +651,26 @@ func createUpstreamServersForPlus(virtualServerEx *VirtualServerEx) map[string][
virtualServerUpstreamNamer := newUpstreamNamerForVirtualServer(virtualServerEx.VirtualServer)

for _, u := range virtualServerEx.VirtualServer.Spec.Upstreams {
if _, isExternalNameSvc := virtualServerEx.ExternalNameSvcs[GenerateExternalNameSvcKey(virtualServerEx.VirtualServer.Namespace, u.Service)]; isExternalNameSvc {
glog.V(3).Infof("Service %s is Type ExternalName, skipping NGINX Plus endpoints update via API", u.Service)
continue
}

endpointsKey := GenerateEndpointsKey(virtualServerEx.VirtualServer.Namespace, u.Service, u.Port)
name := virtualServerUpstreamNamer.GetNameForUpstream(u.Name)

upstreamEndpoints[name] = virtualServerEx.Endpoints[endpointsKey]
}

for _, vsr := range virtualServerEx.VirtualServerRoutes {
upstreamNamer := newUpstreamNamerForVirtualServerRoute(virtualServerEx.VirtualServer, vsr)
for _, u := range vsr.Spec.Upstreams {
if _, isExternalNameSvc := virtualServerEx.ExternalNameSvcs[GenerateExternalNameSvcKey(vsr.Namespace, u.Service)]; isExternalNameSvc {
glog.V(3).Infof("Service %s is Type ExternalName, skipping NGINX Plus endpoints update via API", u.Service)
continue
}

endpointsKey := GenerateEndpointsKey(vsr.Namespace, u.Service, u.Port)
name := upstreamNamer.GetNameForUpstream(u.Name)

upstreamEndpoints[name] = virtualServerEx.Endpoints[endpointsKey]
}
}
Expand Down
Loading

0 comments on commit c9bc9c8

Please sign in to comment.