55 "sort"
66
77 "k8s.io/apimachinery/pkg/types"
8+ "k8s.io/apimachinery/pkg/util/validation/field"
89 "sigs.k8s.io/controller-runtime/pkg/client"
910 "sigs.k8s.io/gateway-api/apis/v1beta1"
1011
@@ -200,13 +201,16 @@ func validateListener(
200201 conds = validate (gl )
201202
202203 if len (gw .Spec .Addresses ) > 0 {
203- conds = append (conds , conditions .NewListenerUnsupportedAddress ("Specifying Gateway addresses is not supported" ))
204+ path := field .NewPath ("spec" , "addresses" )
205+ valErr := field .Forbidden (path , "addresses are not supported" )
206+ conds = append (conds , conditions .NewListenerUnsupportedAddress (valErr .Error ()))
204207 }
205208
206209 validHostnameErr := validateListenerHostname (gl .Hostname )
207210 if validHostnameErr != nil {
208- msg := fmt .Sprintf ("Invalid hostname: %v" , validHostnameErr )
209- conds = append (conds , conditions .NewListenerUnsupportedValue (msg ))
211+ path := field .NewPath ("hostname" )
212+ valErr := field .Invalid (path , gl .Hostname , validHostnameErr .Error ())
213+ conds = append (conds , conditions .NewListenerUnsupportedValue (valErr .Error ()))
210214 }
211215
212216 return conds , validHostnameErr == nil
@@ -248,13 +252,21 @@ func (c *httpListenerConfigurator) loadSecretIntoListener(l *Listener) {
248252
249253 l .SecretPath , err = c .secretMemoryMgr .Request (nsname )
250254 if err != nil {
251- msg := fmt .Sprintf ("Failed to get the certificate %s: %v" , nsname .String (), err )
252- l .Conditions = append (l .Conditions , conditions .NewListenerInvalidCertificateRef (msg )... )
255+ path := field .NewPath ("tls" , "certificateRefs" ).Index (0 )
256+ // field.NotFound could be better, but it doesn't allow us to set the error message.
257+ valErr := field .Invalid (path , nsname , err .Error ())
258+
259+ l .Conditions = append (l .Conditions , conditions .NewListenerInvalidCertificateRef (valErr .Error ())... )
253260 l .Valid = false
254261 }
255262}
256263
257264func (c * httpListenerConfigurator ) configure (gl v1beta1.Listener ) * Listener {
265+ // The functions called by configure() generate conditions for invalid fields of the listener.
266+ // Because the Gateway status includes a status field for each listener, the messages in those conditions
267+ // don't need to include the full path to the field (e.g. "spec.listeners[0].hostname"). They will include
268+ // a path starting from the field of a listener (e.g. "hostname", "tls.options").
269+
258270 conds , validHostname := validateListener (gl , c .gateway , c .validate )
259271
260272 l := & Listener {
@@ -283,16 +295,19 @@ func newInvalidProtocolListenerConfigurator() *invalidProtocolListenerConfigurat
283295}
284296
285297func (c * invalidProtocolListenerConfigurator ) configure (gl v1beta1.Listener ) * Listener {
286- msg := fmt .Sprintf ("Protocol %q is not supported, use %q or %q" ,
287- gl .Protocol , v1beta1 .HTTPProtocolType , v1beta1 .HTTPSProtocolType )
298+ valErr := field .NotSupported (
299+ field .NewPath ("protocol" ),
300+ gl .Protocol ,
301+ []string {string (v1beta1 .HTTPProtocolType ), string (v1beta1 .HTTPSProtocolType )},
302+ )
288303
289304 return & Listener {
290305 Source : gl ,
291306 Valid : false ,
292307 Routes : make (map [types.NamespacedName ]* Route ),
293308 AcceptedHostnames : make (map [string ]struct {}),
294309 Conditions : []conditions.Condition {
295- conditions .NewListenerUnsupportedProtocol (msg ),
310+ conditions .NewListenerUnsupportedProtocol (valErr . Error () ),
296311 },
297312 }
298313}
@@ -301,8 +316,9 @@ func validateHTTPListener(listener v1beta1.Listener) []conditions.Condition {
301316 var conds []conditions.Condition
302317
303318 if listener .Port != 80 {
304- msg := fmt .Sprintf ("Port %d is not supported for HTTP, use 80" , listener .Port )
305- conds = append (conds , conditions .NewListenerPortUnavailable (msg ))
319+ path := field .NewPath ("port" )
320+ valErr := field .NotSupported (path , listener .Port , []string {"80" })
321+ conds = append (conds , conditions .NewListenerPortUnavailable (valErr .Error ()))
306322 }
307323
308324 // The imported Webhook validation ensures the tls field is not set for an HTTP listener.
@@ -315,48 +331,63 @@ func validateHTTPSListener(listener v1beta1.Listener, gwNsName string) []conditi
315331 var conds []conditions.Condition
316332
317333 if listener .Port != 443 {
318- msg := fmt .Sprintf ("Port %d is not supported for HTTPS, use 443" , listener .Port )
319- conds = append (conds , conditions .NewListenerPortUnavailable (msg ))
334+ path := field .NewPath ("port" )
335+ valErr := field .NotSupported (path , listener .Port , []string {"443" })
336+ conds = append (conds , conditions .NewListenerPortUnavailable (valErr .Error ()))
320337 }
321338
322339 // The imported Webhook validation ensures the tls field is not nil for an HTTPS listener.
323340 // FIXME(pleshakov): Add a unit test for the imported Webhook validation code for this case.
324341
342+ tlsPath := field .NewPath ("tls" )
343+
325344 if * listener .TLS .Mode != v1beta1 .TLSModeTerminate {
326- msg := fmt .Sprintf ("tls.mode %q is not supported, use %q" , * listener .TLS .Mode , v1beta1 .TLSModeTerminate )
327- conds = append (conds , conditions .NewListenerUnsupportedValue (msg ))
345+ valErr := field .NotSupported (
346+ tlsPath .Child ("mode" ),
347+ * listener .TLS .Mode ,
348+ []string {string (v1beta1 .TLSModeTerminate )},
349+ )
350+ conds = append (conds , conditions .NewListenerUnsupportedValue (valErr .Error ()))
328351 }
329352
330353 if len (listener .TLS .Options ) > 0 {
331- conds = append (conds , conditions .NewListenerUnsupportedValue ("tls.options are not supported" ))
354+ path := tlsPath .Child ("options" )
355+ valErr := field .Forbidden (path , "options are not supported" )
356+ conds = append (conds , conditions .NewListenerUnsupportedValue (valErr .Error ()))
332357 }
333358
334359 // The imported Webhook validation ensures len(listener.TLS.Certificates) is not 0.
335360 // FIXME(pleshakov): Add a unit test for the imported Webhook validation code for this case.
336361
337362 certRef := listener .TLS .CertificateRefs [0 ]
338363
364+ certRefPath := tlsPath .Child ("certificateRefs" ).Index (0 )
365+
339366 if certRef .Kind != nil && * certRef .Kind != "Secret" {
340- msg := fmt .Sprintf ("Kind must be Secret, got %q" , * certRef .Kind )
341- conds = append (conds , conditions .NewListenerInvalidCertificateRef (msg )... )
367+ path := certRefPath .Child ("kind" )
368+ valErr := field .NotSupported (path , * certRef .Kind , []string {"Secret" })
369+ conds = append (conds , conditions .NewListenerInvalidCertificateRef (valErr .Error ())... )
342370 }
343371
344372 // for Kind Secret, certRef.Group must be nil or empty
345373 if certRef .Group != nil && * certRef .Group != "" {
346- msg := fmt .Sprintf ("Group must be empty, got %q" , * certRef .Group )
347- conds = append (conds , conditions .NewListenerInvalidCertificateRef (msg )... )
374+ path := certRefPath .Child ("group" )
375+ valErr := field .NotSupported (path , * certRef .Group , []string {"" })
376+ conds = append (conds , conditions .NewListenerInvalidCertificateRef (valErr .Error ())... )
348377 }
349378
350379 // secret must be in the same namespace as the gateway
351380 if certRef .Namespace != nil && string (* certRef .Namespace ) != gwNsName {
352- const msg = "Referenced Secret must belong to the same namespace as the Gateway"
353- conds = append (conds , conditions .NewListenerInvalidCertificateRef (msg )... )
354-
381+ const detail = "Referenced Secret must belong to the same namespace as the Gateway"
382+ path := certRefPath .Child ("namespace" )
383+ valErr := field .Invalid (path , certRef .Namespace , detail )
384+ conds = append (conds , conditions .NewListenerInvalidCertificateRef (valErr .Error ())... )
355385 }
356386
357387 if l := len (listener .TLS .CertificateRefs ); l > 1 {
358- msg := fmt .Sprintf ("Only 1 certificateRef is supported, got %d" , l )
359- conds = append (conds , conditions .NewListenerUnsupportedValue (msg ))
388+ path := tlsPath .Child ("certificateRefs" )
389+ valErr := field .TooMany (path , l , 1 )
390+ conds = append (conds , conditions .NewListenerUnsupportedValue (valErr .Error ()))
360391 }
361392
362393 return conds
0 commit comments