Skip to content

Commit

Permalink
Webhook: validate the combination of port, protocol, and hostname are…
Browse files Browse the repository at this point in the history
… unique for each listener. (#1457)

* Add validateHostnameProtocolPort to validate that the combination of port, protocol, and name are unique for each listener.

Signed-off-by: Huang Xin <xin1.huang@intel.com>

* Fix format.

Signed-off-by: Huang Xin <xin1.huang@intel.com>

* Fix format.

Signed-off-by: Huang Xin <xin1.huang@intel.com>

* Skip Insert if the set has the combination already

Signed-off-by: Huang Xin <xin1.huang@intel.com>

* Add more unit tests.

Signed-off-by: Huang Xin <xin1.huang@intel.com>

* Fix nits.

Signed-off-by: Huang Xin <xin1.huang@intel.com>

---------

Signed-off-by: Huang Xin <xin1.huang@intel.com>
  • Loading branch information
gyohuangxin authored Apr 6, 2023
1 parent 2897160 commit bb88d58
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 3 deletions.
24 changes: 24 additions & 0 deletions apis/v1beta1/validation/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package validation
import (
"fmt"

"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"

gatewayv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
Expand Down Expand Up @@ -70,6 +71,7 @@ func validateGatewayListeners(listeners []gatewayv1b1.Listener, path *field.Path
errs = append(errs, validateListenerHostname(listeners, path)...)
errs = append(errs, ValidateTLSCertificateRefs(listeners, path)...)
errs = append(errs, ValidateListenerNames(listeners, path)...)
errs = append(errs, validateHostnameProtocolPort(listeners, path)...)
return errs
}

Expand Down Expand Up @@ -133,3 +135,25 @@ func ValidateListenerNames(listeners []gatewayv1b1.Listener, path *field.Path) f
}
return errs
}

// validateHostnameProtocolPort validates that the combination of port, protocol, and hostname are
// unique for each listener.
func validateHostnameProtocolPort(listeners []gatewayv1b1.Listener, path *field.Path) field.ErrorList {
var errs field.ErrorList
hostnameProtocolPortSets := sets.Set[string]{}
for i, listener := range listeners {
hostname := new(gatewayv1b1.Hostname)
if listener.Hostname != nil {
hostname = listener.Hostname
}
protocol := listener.Protocol
port := listener.Port
hostnameProtocolPort := fmt.Sprintf("%s:%s:%d", *hostname, protocol, port)
if hostnameProtocolPortSets.Has(hostnameProtocolPort) {
errs = append(errs, field.Forbidden(path.Index(i), fmt.Sprintln("combination of port, protocol, and hostname must be unique for each listener")))
} else {
hostnameProtocolPortSets.Insert(hostnameProtocolPort)
}
}
return errs
}
109 changes: 106 additions & 3 deletions apis/v1beta1/validation/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,117 @@ func TestValidateGateway(t *testing.T) {
},
"names are not unique within the Gateway": {
mutate: func(gw *gatewayv1b1.Gateway) {
hostnameFoo := gatewayv1b1.Hostname("foo.com")
hostnameBar := gatewayv1b1.Hostname("bar.com")
gw.Spec.Listeners[0].Name = "foo"
gw.Spec.Listeners = append(gw.Spec.Listeners, gatewayv1b1.Listener{
Name: "foo",
},
gw.Spec.Listeners[0].Hostname = &hostnameFoo
gw.Spec.Listeners = append(gw.Spec.Listeners,
gatewayv1b1.Listener{
Name: "foo",
Hostname: &hostnameBar,
},
)
},
expectErrsOnFields: []string{"spec.listeners[1].name"},
},
"combination of port, protocol, and hostname are not unique for each listener": {
mutate: func(gw *gatewayv1b1.Gateway) {
hostnameFoo := gatewayv1b1.Hostname("foo.com")
gw.Spec.Listeners[0].Name = "foo"
gw.Spec.Listeners[0].Hostname = &hostnameFoo
gw.Spec.Listeners[0].Protocol = gatewayv1b1.HTTPProtocolType
gw.Spec.Listeners[0].Port = 80
gw.Spec.Listeners = append(gw.Spec.Listeners,
gatewayv1b1.Listener{
Name: "bar",
Hostname: &hostnameFoo,
Protocol: gatewayv1b1.HTTPProtocolType,
Port: 80,
},
)
},
expectErrsOnFields: []string{"spec.listeners[1]"},
},
"combination of port and protocol are not unique for each listenr when hostnames not set": {
mutate: func(gw *gatewayv1b1.Gateway) {
gw.Spec.Listeners[0].Name = "foo"
gw.Spec.Listeners[0].Protocol = gatewayv1b1.HTTPProtocolType
gw.Spec.Listeners[0].Port = 80
gw.Spec.Listeners = append(gw.Spec.Listeners,
gatewayv1b1.Listener{
Name: "bar",
Protocol: gatewayv1b1.HTTPProtocolType,
Port: 80,
},
)
},
expectErrsOnFields: []string{"spec.listeners[1]"},
},
"port is unique when protocol and hostname are the same": {
mutate: func(gw *gatewayv1b1.Gateway) {
hostnameFoo := gatewayv1b1.Hostname("foo.com")
gw.Spec.Listeners[0].Name = "foo"
gw.Spec.Listeners[0].Hostname = &hostnameFoo
gw.Spec.Listeners[0].Protocol = gatewayv1b1.HTTPProtocolType
gw.Spec.Listeners[0].Port = 80
gw.Spec.Listeners = append(gw.Spec.Listeners,
gatewayv1b1.Listener{
Name: "bar",
Hostname: &hostnameFoo,
Protocol: gatewayv1b1.HTTPProtocolType,
Port: 8080,
},
)
},
expectErrsOnFields: nil,
},
"hostname is unique when protocol and port are the same": {
mutate: func(gw *gatewayv1b1.Gateway) {
hostnameFoo := gatewayv1b1.Hostname("foo.com")
hostnameBar := gatewayv1b1.Hostname("bar.com")
gw.Spec.Listeners[0].Name = "foo"
gw.Spec.Listeners[0].Hostname = &hostnameFoo
gw.Spec.Listeners[0].Protocol = gatewayv1b1.HTTPProtocolType
gw.Spec.Listeners[0].Port = 80
gw.Spec.Listeners = append(gw.Spec.Listeners,
gatewayv1b1.Listener{
Name: "bar",
Hostname: &hostnameBar,
Protocol: gatewayv1b1.HTTPProtocolType,
Port: 80,
},
)
},
expectErrsOnFields: nil,
},
"protocol is unique when port and hostname are the same": {
mutate: func(gw *gatewayv1b1.Gateway) {
hostnameFoo := gatewayv1b1.Hostname("foo.com")
tlsConfigFoo := tlsConfig
tlsModeFoo := gatewayv1b1.TLSModeType("Terminate")
tlsConfigFoo.Mode = &tlsModeFoo
tlsConfigFoo.CertificateRefs = []gatewayv1b1.SecretObjectReference{
{
Name: "FooCertificateRefs",
},
}
gw.Spec.Listeners[0].Name = "foo"
gw.Spec.Listeners[0].Hostname = &hostnameFoo
gw.Spec.Listeners[0].Protocol = gatewayv1b1.HTTPSProtocolType
gw.Spec.Listeners[0].Port = 8000
gw.Spec.Listeners[0].TLS = &tlsConfigFoo
gw.Spec.Listeners = append(gw.Spec.Listeners,
gatewayv1b1.Listener{
Name: "bar",
Hostname: &hostnameFoo,
Protocol: gatewayv1b1.TLSProtocolType,
Port: 8000,
TLS: &tlsConfigFoo,
},
)
},
expectErrsOnFields: nil,
},
}

for name, tc := range testCases {
Expand Down

0 comments on commit bb88d58

Please sign in to comment.