Skip to content

Commit

Permalink
Trigger syncIngress on Service addition/deletion #7346
Browse files Browse the repository at this point in the history
Normally Ingress sinchronization for Services is triggered when
corresponding Service's Endpoints are added, deleted or modified.
Services of type ExternalName, however, do not have any endpoints
and hence do not trigger Ingress synchronization as only Update
events are being watched. This commit makes sure that Update and
Delete Service events also enqueue a syncIngress task.
  • Loading branch information
bsod90 committed Jul 26, 2021
1 parent 9e274dd commit f88a1c9
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 54 deletions.
18 changes: 18 additions & 0 deletions internal/ingress/controller/store/store.go
Expand Up @@ -592,6 +592,24 @@ func New(
}

serviceHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
svc := obj.(*corev1.Service)
if svc.Spec.Type == corev1.ServiceTypeExternalName {
updateCh.In() <- Event{
Type: CreateEvent,
Obj: obj,
}
}
},
DeleteFunc: func(obj interface{}) {
svc := obj.(*corev1.Service)
if svc.Spec.Type == corev1.ServiceTypeExternalName {
updateCh.In() <- Event{
Type: DeleteEvent,
Obj: obj,
}
}
},
UpdateFunc: func(old, cur interface{}) {
oldSvc := old.(*corev1.Service)
curSvc := cur.(*corev1.Service)
Expand Down
125 changes: 71 additions & 54 deletions test/e2e/servicebackend/service_externalname.go
Expand Up @@ -34,6 +34,27 @@ import (
"k8s.io/ingress-nginx/test/e2e/framework"
)

func buildHTTPBinExternalNameService(f *framework.Framework, portName string) *core.Service {
return &core.Service{
ObjectMeta: metav1.ObjectMeta{
Name: framework.HTTPBinService,
Namespace: f.Namespace,
},
Spec: corev1.ServiceSpec{
ExternalName: "httpbin.org",
Type: corev1.ServiceTypeExternalName,
Ports: []corev1.ServicePort{
{
Name: portName,
Port: 80,
TargetPort: intstr.FromInt(80),
Protocol: "TCP",
},
},
},
}
}

var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() {
f := framework.NewDefaultFramework("type-externalname")

Expand Down Expand Up @@ -107,24 +128,7 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() {
ginkgo.It("should return 200 for service type=ExternalName with a port defined", func() {
host := "echo"

svc := &core.Service{
ObjectMeta: metav1.ObjectMeta{
Name: framework.HTTPBinService,
Namespace: f.Namespace,
},
Spec: corev1.ServiceSpec{
ExternalName: "httpbin.org",
Type: corev1.ServiceTypeExternalName,
Ports: []corev1.ServicePort{
{
Name: host,
Port: 80,
TargetPort: intstr.FromInt(80),
Protocol: "TCP",
},
},
},
}
svc := buildHTTPBinExternalNameService(f, host)
f.EnsureService(svc)

annotations := map[string]string{
Expand Down Expand Up @@ -179,24 +183,7 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() {
ginkgo.It("should return 200 for service type=ExternalName using a port name", func() {
host := "echo"

svc := &core.Service{
ObjectMeta: metav1.ObjectMeta{
Name: framework.HTTPBinService,
Namespace: f.Namespace,
},
Spec: corev1.ServiceSpec{
ExternalName: "httpbin.org",
Type: corev1.ServiceTypeExternalName,
Ports: []corev1.ServicePort{
{
Name: host,
Port: 80,
TargetPort: intstr.FromInt(80),
Protocol: "TCP",
},
},
},
}
svc := buildHTTPBinExternalNameService(f, host)
f.EnsureService(svc)

annotations := map[string]string{
Expand Down Expand Up @@ -252,24 +239,7 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() {
ginkgo.It("should update the external name after a service update", func() {
host := "echo"

svc := &core.Service{
ObjectMeta: metav1.ObjectMeta{
Name: framework.HTTPBinService,
Namespace: f.Namespace,
},
Spec: corev1.ServiceSpec{
ExternalName: "httpbin.org",
Type: corev1.ServiceTypeExternalName,
Ports: []corev1.ServicePort{
{
Name: host,
Port: 80,
TargetPort: intstr.FromInt(80),
Protocol: "TCP",
},
},
},
}
svc := buildHTTPBinExternalNameService(f, host)
f.EnsureService(svc)

annotations := map[string]string{
Expand Down Expand Up @@ -320,4 +290,51 @@ var _ = framework.IngressNginxDescribe("[Service] Type ExternalName", func() {
assert.Nil(ginkgo.GinkgoT(), err)
assert.Contains(ginkgo.GinkgoT(), output, `{"address":"eu.httpbin.org"`)
})

ginkgo.It("should sync ingress on external name service addition/deletion", func() {
host := "echo"

// Create the Ingress first
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.HTTPBinService, 80, nil)
ing.Spec.Rules[0].HTTP.Paths[0].Backend.ServicePort = intstr.FromString(host)
f.EnsureIngress(ing)

f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, "proxy_pass http://upstream_balancer;")
})

// Nginx should return 503 without the underlying service being available
f.HTTPTestClient().
GET("/get").
WithHeader("Host", host).
Expect().
Status(http.StatusServiceUnavailable)

// Now create the service
svc := buildHTTPBinExternalNameService(f, host)
f.EnsureService(svc)

framework.Sleep()

// 503 should change to 200 OK
f.HTTPTestClient().
GET("/get").
WithHeader("Host", host).
Expect().
Status(http.StatusOK)

// And back to 503 after deleting the service

err := f.KubeClientSet.CoreV1().Services(f.Namespace).Delete(context.TODO(), framework.HTTPBinService, metav1.DeleteOptions{})
assert.Nil(ginkgo.GinkgoT(), err, "unexpected error deleting httpbin service")

framework.Sleep()

f.HTTPTestClient().
GET("/get").
WithHeader("Host", host).
Expect().
Status(http.StatusServiceUnavailable)
})
})

0 comments on commit f88a1c9

Please sign in to comment.