5
5
"sort"
6
6
7
7
"k8s.io/apimachinery/pkg/types"
8
+ "k8s.io/apimachinery/pkg/util/validation/field"
8
9
"sigs.k8s.io/controller-runtime/pkg/client"
9
10
"sigs.k8s.io/gateway-api/apis/v1beta1"
10
11
@@ -200,13 +201,16 @@ func validateListener(
200
201
conds = validate (gl )
201
202
202
203
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 ()))
204
207
}
205
208
206
209
validHostnameErr := validateListenerHostname (gl .Hostname )
207
210
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 ()))
210
214
}
211
215
212
216
return conds , validHostnameErr == nil
@@ -248,13 +252,21 @@ func (c *httpListenerConfigurator) loadSecretIntoListener(l *Listener) {
248
252
249
253
l .SecretPath , err = c .secretMemoryMgr .Request (nsname )
250
254
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 ())... )
253
260
l .Valid = false
254
261
}
255
262
}
256
263
257
264
func (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
+
258
270
conds , validHostname := validateListener (gl , c .gateway , c .validate )
259
271
260
272
l := & Listener {
@@ -283,16 +295,19 @@ func newInvalidProtocolListenerConfigurator() *invalidProtocolListenerConfigurat
283
295
}
284
296
285
297
func (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
+ )
288
303
289
304
return & Listener {
290
305
Source : gl ,
291
306
Valid : false ,
292
307
Routes : make (map [types.NamespacedName ]* Route ),
293
308
AcceptedHostnames : make (map [string ]struct {}),
294
309
Conditions : []conditions.Condition {
295
- conditions .NewListenerUnsupportedProtocol (msg ),
310
+ conditions .NewListenerUnsupportedProtocol (valErr . Error () ),
296
311
},
297
312
}
298
313
}
@@ -301,8 +316,9 @@ func validateHTTPListener(listener v1beta1.Listener) []conditions.Condition {
301
316
var conds []conditions.Condition
302
317
303
318
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 ()))
306
322
}
307
323
308
324
// 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
315
331
var conds []conditions.Condition
316
332
317
333
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 ()))
320
337
}
321
338
322
339
// The imported Webhook validation ensures the tls field is not nil for an HTTPS listener.
323
340
// FIXME(pleshakov): Add a unit test for the imported Webhook validation code for this case.
324
341
342
+ tlsPath := field .NewPath ("tls" )
343
+
325
344
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 ()))
328
351
}
329
352
330
353
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 ()))
332
357
}
333
358
334
359
// The imported Webhook validation ensures len(listener.TLS.Certificates) is not 0.
335
360
// FIXME(pleshakov): Add a unit test for the imported Webhook validation code for this case.
336
361
337
362
certRef := listener .TLS .CertificateRefs [0 ]
338
363
364
+ certRefPath := tlsPath .Child ("certificateRefs" ).Index (0 )
365
+
339
366
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 ())... )
342
370
}
343
371
344
372
// for Kind Secret, certRef.Group must be nil or empty
345
373
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 ())... )
348
377
}
349
378
350
379
// secret must be in the same namespace as the gateway
351
380
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 ())... )
355
385
}
356
386
357
387
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 ()))
360
391
}
361
392
362
393
return conds
0 commit comments