diff --git a/docs/virtualserver-and-virtualserverroute.md b/docs/virtualserver-and-virtualserverroute.md index 1ad0e08cb6..7cb90c6510 100644 --- a/docs/virtualserver-and-virtualserverroute.md +++ b/docs/virtualserver-and-virtualserverroute.md @@ -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 | diff --git a/internal/configs/configurator.go b/internal/configs/configurator.go index 2742dc0d90..9c57c65754 100644 --- a/internal/configs/configurator.go +++ b/internal/configs/configurator.go @@ -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) diff --git a/internal/configs/version2/config.go b/internal/configs/version2/config.go index c069736c43..86506be4d6 100644 --- a/internal/configs/version2/config.go +++ b/internal/configs/version2/config.go @@ -23,6 +23,7 @@ type UpstreamServer struct { MaxFails int MaxConns int FailTimeout string + Resolve bool } // Server defines a server. diff --git a/internal/configs/version2/nginx-plus.virtualserver.tmpl b/internal/configs/version2/nginx-plus.virtualserver.tmpl index 9970cedf57..ce03d63484 100644 --- a/internal/configs/version2/nginx-plus.virtualserver.tmpl +++ b/internal/configs/version2/nginx-plus.virtualserver.tmpl @@ -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 }} diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go index 112d8c4106..586bbffbee 100644 --- a/internal/configs/virtualserver.go +++ b/internal/configs/virtualserver.go @@ -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" @@ -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 { @@ -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 @@ -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 @@ -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 @@ -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 { @@ -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) } @@ -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] } } diff --git a/internal/configs/virtualserver_test.go b/internal/configs/virtualserver_test.go index d2c7ebe028..ab9cb8a0de 100644 --- a/internal/configs/virtualserver_test.go +++ b/internal/configs/virtualserver_test.go @@ -284,8 +284,9 @@ func TestGenerateVirtualServerConfig(t *testing.T) { } isPlus := false + isResolverConfigured := false tlsPemFileName := "" - result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus) + result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus, isResolverConfigured) if !reflect.DeepEqual(result, expected) { t.Errorf("generateVirtualServerConfig returned \n%v but expected \n%v", result, expected) } @@ -499,8 +500,9 @@ func TestGenerateVirtualServerConfigForVirtualServerWithSplits(t *testing.T) { } isPlus := false + isResolverConfigured := false tlsPemFileName := "" - result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus) + result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus, isResolverConfigured) if !reflect.DeepEqual(result, expected) { t.Errorf("generateVirtualServerConfig returned \n%v but expected \n%v", result, expected) } @@ -755,8 +757,9 @@ func TestGenerateVirtualServerConfigForVirtualServerWithRules(t *testing.T) { } isPlus := false + isResolverConfigured := false tlsPemFileName := "" - result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus) + result := generateVirtualServerConfig(&virtualServerEx, tlsPemFileName, &baseCfgParams, isPlus, isResolverConfigured) if !reflect.DeepEqual(result, expected) { t.Errorf("generateVirtualServerConfig returned \n%v but expected \n%v", result, expected) } @@ -764,11 +767,10 @@ func TestGenerateVirtualServerConfigForVirtualServerWithRules(t *testing.T) { func TestGenerateUpstream(t *testing.T) { name := "test-upstream" - upstream := conf_v1alpha1.Upstream{} + upstream := conf_v1alpha1.Upstream{Service: name, Port: 80} endpoints := []string{ "192.168.10.10:8080", } - isPlus := false cfgParams := ConfigParams{ LBMethod: "random", MaxFails: 1, @@ -791,7 +793,7 @@ func TestGenerateUpstream(t *testing.T) { Keepalive: 21, } - result := generateUpstream(name, upstream, endpoints, isPlus, &cfgParams) + result := generateUpstream(name, upstream, false, endpoints, &cfgParams) if !reflect.DeepEqual(result, expected) { t.Errorf("generateUpstream() returned %v but expected %v", result, expected) } @@ -804,7 +806,6 @@ func TestGenerateUpstreamWithKeepalive(t *testing.T) { endpoints := []string{ "192.168.10.10:8080", } - isPlus := false tests := []struct { upstream conf_v1alpha1.Upstream @@ -813,7 +814,7 @@ func TestGenerateUpstreamWithKeepalive(t *testing.T) { msg string }{ { - conf_v1alpha1.Upstream{Keepalive: &keepalive}, + conf_v1alpha1.Upstream{Keepalive: &keepalive, Service: name, Port: 80}, &ConfigParams{Keepalive: 21}, version2.Upstream{ Name: "test-upstream", @@ -827,7 +828,7 @@ func TestGenerateUpstreamWithKeepalive(t *testing.T) { "upstream keepalive set, configparam set", }, { - conf_v1alpha1.Upstream{}, + conf_v1alpha1.Upstream{Service: name, Port: 80}, &ConfigParams{Keepalive: 21}, version2.Upstream{ Name: "test-upstream", @@ -841,7 +842,7 @@ func TestGenerateUpstreamWithKeepalive(t *testing.T) { "upstream keepalive not set, configparam set", }, { - conf_v1alpha1.Upstream{Keepalive: &noKeepalive}, + conf_v1alpha1.Upstream{Keepalive: &noKeepalive, Service: name, Port: 80}, &ConfigParams{Keepalive: 21}, version2.Upstream{ Name: "test-upstream", @@ -856,49 +857,34 @@ func TestGenerateUpstreamWithKeepalive(t *testing.T) { } for _, test := range tests { - result := generateUpstream(name, test.upstream, endpoints, isPlus, test.cfgParams) + result := generateUpstream(name, test.upstream, false, endpoints, test.cfgParams) if !reflect.DeepEqual(result, test.expected) { t.Errorf("generateUpstream() returned %v but expected %v for the case of %v", result, test.expected, test.msg) } } } -func TestGenerateUpstreamForZeroEndpoints(t *testing.T) { +func TestGenerateUpstreamForExternalNameService(t *testing.T) { name := "test-upstream" - upstream := conf_v1alpha1.Upstream{} - var endpoints []string // nil - cfgParams := ConfigParams{ - MaxFails: 1, - FailTimeout: "10s", - } + endpoints := []string{"example.com"} + upstream := conf_v1alpha1.Upstream{Service: name} + cfgParams := ConfigParams{} - isPlus := false - expectedForNGINX := version2.Upstream{ - Name: "test-upstream", + expected := version2.Upstream{ + Name: name, Servers: []version2.UpstreamServer{ { - Address: nginx502Server, - MaxFails: 1, - FailTimeout: "10s", + Address: "example.com", + Resolve: true, }, }, } - result := generateUpstream(name, upstream, endpoints, isPlus, &cfgParams) - if !reflect.DeepEqual(result, expectedForNGINX) { - t.Errorf("generateUpstream(isPlus=%v) returned %v but expected %v", isPlus, result, expectedForNGINX) - } - - isPlus = true - expectedForNGINXPlus := version2.Upstream{ - Name: "test-upstream", - Servers: nil, + result := generateUpstream(name, upstream, true, endpoints, &cfgParams) + if !reflect.DeepEqual(result, expected) { + t.Errorf("generateUpstream() returned %v but expected %v", result, expected) } - result = generateUpstream(name, upstream, endpoints, isPlus, &cfgParams) - if !reflect.DeepEqual(result, expectedForNGINXPlus) { - t.Errorf("generateUpstream(isPlus=%v) returned %v but expected %v", isPlus, result, expectedForNGINXPlus) - } } func TestGenerateProxyPassProtocol(t *testing.T) { @@ -1091,6 +1077,11 @@ func TestCreateUpstreamServersForPlus(t *testing.T) { Service: "test-svc", Port: 80, }, + { + Name: "external", + Service: "external-svc", + Port: 80, + }, }, Routes: []conf_v1alpha1.Route{ { @@ -1101,6 +1092,10 @@ func TestCreateUpstreamServersForPlus(t *testing.T) { Path: "/coffee", Route: "default/coffee", }, + { + Path: "/external", + Upstream: "external", + }, }, }, }, @@ -1112,6 +1107,12 @@ func TestCreateUpstreamServersForPlus(t *testing.T) { "default/coffee-svc:80": { "10.0.0.30:80", }, + "default/external-svc:80": { + "example.com:80", + }, + }, + ExternalNameSvcs: map[string]bool{ + "default/external-svc": true, }, VirtualServerRoutes: []*conf_v1alpha1.VirtualServerRoute{ { @@ -1829,3 +1830,133 @@ func TestGenerateHealthCheck(t *testing.T) { } } } + +func TestGenerateEndpointsForUpstream(t *testing.T) { + name := "test" + namespace := "test-namespace" + + tests := []struct { + upstream conf_v1alpha1.Upstream + vsEx *VirtualServerEx + isPlus bool + isResolverConfigured bool + expected []string + msg string + }{ + { + upstream: conf_v1alpha1.Upstream{ + Service: name, + Port: 80, + }, + vsEx: &VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + }, + Endpoints: map[string][]string{ + "test-namespace/test:80": []string{"example.com:80"}, + }, + ExternalNameSvcs: map[string]bool{ + "test-namespace/test": true, + }, + }, + isPlus: true, + isResolverConfigured: true, + expected: []string{"example.com:80"}, + msg: "ExternalName service", + }, + { + upstream: conf_v1alpha1.Upstream{ + Service: name, + Port: 80, + }, + vsEx: &VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + }, + Endpoints: map[string][]string{ + "test-namespace/test:80": []string{"example.com:80"}, + }, + ExternalNameSvcs: map[string]bool{ + "test-namespace/test": true, + }, + }, + isPlus: true, + isResolverConfigured: false, + expected: []string{}, + msg: "ExternalName service without resolver configured", + }, + { + upstream: conf_v1alpha1.Upstream{ + Service: name, + Port: 8080, + }, + vsEx: &VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + }, + Endpoints: map[string][]string{ + "test-namespace/test:8080": []string{"192.168.10.10:8080"}, + }, + }, + isPlus: false, + isResolverConfigured: false, + expected: []string{"192.168.10.10:8080"}, + msg: "Service with endpoints", + }, + { + upstream: conf_v1alpha1.Upstream{ + Service: name, + Port: 8080, + }, + vsEx: &VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + }, + Endpoints: map[string][]string{}, + }, + isPlus: false, + isResolverConfigured: false, + expected: []string{nginx502Server}, + msg: "Service with no endpoints", + }, + { + upstream: conf_v1alpha1.Upstream{ + Service: name, + Port: 8080, + }, + vsEx: &VirtualServerEx{ + VirtualServer: &conf_v1alpha1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + }, + Endpoints: map[string][]string{}, + }, + isPlus: true, + isResolverConfigured: false, + expected: nil, + msg: "Service with no endpoints", + }, + } + + for _, test := range tests { + result := generateEndpointsForUpstream(namespace, test.upstream, test.vsEx, test.isResolverConfigured, test.isPlus) + if !reflect.DeepEqual(result, test.expected) { + t.Errorf("generateEndpointsForUpstream(isPlus=%v, isResolverConfigured=%v) returned %v, but expected %v for case: %v", + test.isPlus, test.isResolverConfigured, result, test.expected, test.msg) + } + } +} diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 2c5cc4f012..c7f896a393 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -1504,10 +1504,20 @@ func (lbc *LoadBalancerController) createVirtualServer(virtualServer *conf_v1alp } endpoints := make(map[string][]string) + externalNameSvcs := make(map[string]bool) for _, u := range virtualServer.Spec.Upstreams { endpointsKey := configs.GenerateEndpointsKey(virtualServer.Namespace, u.Service, u.Port) - endpoints[endpointsKey] = lbc.getEndpointsForService(virtualServer.Namespace, u.Service, int(u.Port)) + endps, external, err := lbc.getEndpointsForUpstream(virtualServer.Namespace, u) + if err != nil { + glog.Warningf("Error getting Endpoints for Upstream %v: %v", u.Name, err) + } + + if err == nil && external && lbc.isNginxPlus { + externalNameSvcs[configs.GenerateExternalNameSvcKey(virtualServer.Namespace, u.Service)] = true + } + + endpoints[endpointsKey] = endps } var virtualServerRoutes []*conf_v1alpha1.VirtualServerRoute @@ -1552,35 +1562,43 @@ func (lbc *LoadBalancerController) createVirtualServer(virtualServer *conf_v1alp for _, u := range vsr.Spec.Upstreams { endpointsKey := configs.GenerateEndpointsKey(vsr.Namespace, u.Service, u.Port) - endpoints[endpointsKey] = lbc.getEndpointsForService(vsr.Namespace, u.Service, int(u.Port)) + endps, external, err := lbc.getEndpointsForUpstream(vsr.Namespace, u) + if err != nil { + glog.Warningf("Error getting Endpoints for Upstream %v: %v", u.Name, err) + } + + if err == nil && external && lbc.isNginxPlus { + externalNameSvcs[configs.GenerateExternalNameSvcKey(vsr.Namespace, u.Service)] = true + } + + endpoints[endpointsKey] = endps } } virtualServerEx.Endpoints = endpoints virtualServerEx.VirtualServerRoutes = virtualServerRoutes + virtualServerEx.ExternalNameSvcs = externalNameSvcs return &virtualServerEx, virtualServerRouteErrors } -func (lbc *LoadBalancerController) getEndpointsForService(namespace string, name string, port int) []string { - backend := &extensions.IngressBackend{ - ServiceName: name, - ServicePort: intstr.FromInt(port), +func (lbc *LoadBalancerController) getEndpointsForUpstream(namespace string, upstream conf_v1alpha1.Upstream) (result []string, isExternal bool, err error) { + svc, err := lbc.getServiceForUpstream(upstream, namespace) + if err != nil { + return nil, false, fmt.Errorf("Error getting service %v: %v", upstream.Service, err) } - svc, err := lbc.getServiceForIngressBackend(backend, namespace) - if err != nil { - glog.Warningf("Error getting service %v: %v", name, err) - return nil + backend := &extensions.IngressBackend{ + ServiceName: upstream.Service, + ServicePort: intstr.FromInt(int(upstream.Port)), } - endps, _, err := lbc.getEndpointsForIngressBackend(backend, namespace, svc) + endps, isExternal, err := lbc.getEndpointsForIngressBackend(backend, namespace, svc) if err != nil { - glog.Warningf("Error retrieving endpoints for the service %v: %v", name, err) - return nil + return nil, false, fmt.Errorf("Error retrieving endpoints for the service %v: %v", upstream.Service, err) } - return endps + return endps, isExternal, err } func (lbc *LoadBalancerController) getPodsForIngressBackend(svc *api_v1.Service, namespace string) *api_v1.PodList { @@ -1741,6 +1759,14 @@ func (lbc *LoadBalancerController) getTargetPort(svcPort *api_v1.ServicePort, sv return portNum, nil } +func (lbc *LoadBalancerController) getServiceForUpstream(u conf_v1alpha1.Upstream, namespace string) (*api_v1.Service, error) { + backend := &extensions.IngressBackend{ + ServiceName: u.Service, + ServicePort: intstr.FromInt(int(u.Port)), + } + return lbc.getServiceForIngressBackend(backend, namespace) +} + func (lbc *LoadBalancerController) getServiceForIngressBackend(backend *extensions.IngressBackend, namespace string) (*api_v1.Service, error) { svcKey := namespace + "/" + backend.ServiceName svcObj, svcExists, err := lbc.svcLister.GetByKey(svcKey)