Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ExternalName Services for vs/vsr #641

Merged
merged 1 commit into from
Aug 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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