diff --git a/internal/ingress/controller/endpoints.go b/internal/ingress/controller/endpoints.go index 6796b0ca9..7164614db 100644 --- a/internal/ingress/controller/endpoints.go +++ b/internal/ingress/controller/endpoints.go @@ -21,6 +21,7 @@ import ( "net" "reflect" "strconv" + "strings" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/klog/v2" @@ -53,7 +54,8 @@ func getEndpoints(s *corev1.Service, port *corev1.ServicePort, proto corev1.Prot targetPort := port.TargetPort.IntValue() // if the externalName is not an IP address we need to validate is a valid FQDN if net.ParseIP(s.Spec.ExternalName) == nil { - if errs := validation.IsDNS1123Subdomain(s.Spec.ExternalName); len(errs) > 0 { + externalName := strings.TrimSuffix(s.Spec.ExternalName, ".") + if errs := validation.IsDNS1123Subdomain(externalName); len(errs) > 0 { klog.Errorf("Invalid DNS name %s: %v", s.Spec.ExternalName, errs) return upsServers } diff --git a/internal/ingress/controller/endpoints_test.go b/internal/ingress/controller/endpoints_test.go index 6efe518bc..6dad1b55c 100644 --- a/internal/ingress/controller/endpoints_test.go +++ b/internal/ingress/controller/endpoints_test.go @@ -107,6 +107,35 @@ func TestGetEndpoints(t *testing.T) { }, }, }, + { + "a service type ServiceTypeExternalName with an trailing dot ExternalName value should return one endpoints", + &corev1.Service{ + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeExternalName, + ExternalName: "www.google.com.", + Ports: []corev1.ServicePort{ + { + Name: "default", + TargetPort: intstr.FromInt(80), + }, + }, + }, + }, + &corev1.ServicePort{ + Name: "default", + TargetPort: intstr.FromInt(80), + }, + corev1.ProtocolTCP, + func(string) (*corev1.Endpoints, error) { + return &corev1.Endpoints{}, nil + }, + []ingress.Endpoint{ + { + Address: "www.google.com", + Port: "443", + }, + }, + }, { "a service type ServiceTypeExternalName with an invalid ExternalName value should no return endpoints", &corev1.Service{ diff --git a/test/e2e/servicebackend/service_externalname.go b/test/e2e/servicebackend/service_externalname.go index 0738c744f..84476a2f4 100644 --- a/test/e2e/servicebackend/service_externalname.go +++ b/test/e2e/servicebackend/service_externalname.go @@ -218,6 +218,37 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() { Status(http.StatusOK) }) + ginkgo.It("should return 200 for service type=ExternalName using FQDN with trailing dot", func() { + host := "echo" + + svc := &core.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: framework.HTTPBinService, + Namespace: f.Namespace, + }, + Spec: corev1.ServiceSpec{ + ExternalName: "httpbin.org.", + Type: corev1.ServiceTypeExternalName, + }, + } + + f.EnsureService(svc) + + ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBinService, 80, nil) + f.EnsureIngress(ing) + + f.WaitForNginxServer(host, + func(server string) bool { + return strings.Contains(server, "proxy_pass http://upstream_balancer;") + }) + + f.HTTPTestClient(). + GET("/get"). + WithHeader("Host", host). + Expect(). + Status(http.StatusOK) + }) + ginkgo.It("should update the external name after a service update", func() { host := "echo"