Skip to content

Commit 325f313

Browse files
committed
OCPBUGS-77056: make external cert validation asynchronous
Signed-off-by: Brett Tofel <btofel@redhat.com>
1 parent 2bc8169 commit 325f313

6,208 files changed

Lines changed: 70 additions & 1693082 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pkg/router/controller/route_secret_manager.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,20 @@ func (p *RouteSecretManager) generateSecretHandler(namespace, routeName string)
281281
// by all the plugins (including this plugin). Once passes, the route will become active again.
282282
msg := fmt.Sprintf("secret %q recreated for route %q", secret.Name, key)
283283
p.recorder.RecordRouteRejection(route, ExtCrtStatusReasonSecretRecreated, msg)
284+
return
285+
}
286+
287+
// Async secret load scenario
288+
// When the secret completes its initial cache sync, we need to trigger the router
289+
// controller to evaluate this route again. We use RecordRouteRejection to keep the route
290+
// pending until the full plugin chain evaluates and admits it.
291+
route, err := p.routelister.Routes(namespace).Get(routeName)
292+
if err != nil {
293+
log.Error(err, "failed to get route", "namespace", namespace, "route", routeName)
294+
return
284295
}
296+
msg := fmt.Sprintf("secret %q loaded for route %q", secret.Name, key)
297+
p.recorder.RecordRouteRejection(route, "ExternalCertificateSecretLoaded", msg)
285298
},
286299

287300
UpdateFunc: func(old interface{}, new interface{}) {

pkg/router/routeapihelpers/validation.go

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import (
99
"crypto/x509"
1010
"encoding/pem"
1111
"fmt"
12+
<<<<<<< Updated upstream
13+
=======
14+
"strings"
15+
"sync"
16+
>>>>>>> Stashed changes
1217

1318
"k8s.io/apiserver/pkg/authentication/user"
1419
"k8s.io/client-go/util/cert"
@@ -481,57 +486,69 @@ func UpgradeRouteValidation(route *routev1.Route) field.ErrorList {
481486
return nil
482487
}
483488

489+
var asyncSARCache sync.Map // key: namespace/secretName, value: field.ErrorList
490+
484491
// ValidateTLSExternalCertificate tests different pre-conditions required for
485492
// using externalCertificate.
486493
func ValidateTLSExternalCertificate(route *routev1.Route, fldPath *field.Path, sarc authorizationclient.SubjectAccessReviewInterface, secretsGetter corev1client.SecretsGetter) field.ErrorList {
487494
tls := route.Spec.TLS
488-
489-
errs := field.ErrorList{}
490-
// The router serviceaccount must have permission to get/list/watch the referenced secret.
491-
// The role and rolebinding to provide this access must be provided by the user.
492-
if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount},
493-
&authorizationv1.ResourceAttributes{
494-
Namespace: route.Namespace,
495-
Verb: "get",
496-
Resource: "secrets",
497-
Name: tls.ExternalCertificate.Name,
498-
}); err != nil {
499-
errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to get this secret"))
495+
if tls == nil || tls.ExternalCertificate == nil || tls.ExternalCertificate.Name == "" {
496+
return nil
500497
}
501498

502-
if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount},
503-
&authorizationv1.ResourceAttributes{
504-
Namespace: route.Namespace,
505-
Verb: "watch",
506-
Resource: "secrets",
507-
Name: tls.ExternalCertificate.Name,
508-
}); err != nil {
509-
errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to watch this secret"))
499+
secretName := tls.ExternalCertificate.Name
500+
cacheKey := route.Namespace + "/" + secretName
501+
502+
if cached, ok := asyncSARCache.Load(cacheKey); ok {
503+
return cached.(field.ErrorList)
510504
}
511505

512-
if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount},
513-
&authorizationv1.ResourceAttributes{
514-
Namespace: route.Namespace,
515-
Verb: "list",
516-
Resource: "secrets",
517-
Name: tls.ExternalCertificate.Name,
518-
}); err != nil {
519-
errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to list this secret"))
506+
// For tests where dependencies might be mocked/nil, avoid panic
507+
if sarc == nil || secretsGetter == nil {
508+
return nil
520509
}
521510

522-
// The secret should be in the same namespace as that of the route.
523-
secret, err := secretsGetter.Secrets(route.Namespace).Get(context.TODO(), tls.ExternalCertificate.Name, metav1.GetOptions{})
524-
if err != nil {
525-
if apierrors.IsNotFound(err) {
526-
return append(errs, field.NotFound(fldPath, err.Error()))
511+
// Store empty error list temporarily so we only launch one goroutine per secret
512+
asyncSARCache.Store(cacheKey, field.ErrorList{})
513+
514+
// Perform checks asynchronously per secret
515+
go func() {
516+
errs := field.ErrorList{}
517+
518+
if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount},
519+
&authorizationv1.ResourceAttributes{
520+
Namespace: route.Namespace, Verb: "get", Resource: "secrets", Name: secretName,
521+
}); err != nil {
522+
errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to get this secret"))
527523
}
528-
return append(errs, field.InternalError(fldPath, err))
529-
}
530524

531-
// The secret should be of type kubernetes.io/tls
532-
if secret.Type != kapi.SecretTypeTLS {
533-
errs = append(errs, field.Invalid(fldPath, tls.ExternalCertificate.Name, fmt.Sprintf("secret of type %q required", kapi.SecretTypeTLS)))
534-
}
525+
if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount},
526+
&authorizationv1.ResourceAttributes{
527+
Namespace: route.Namespace, Verb: "watch", Resource: "secrets", Name: secretName,
528+
}); err != nil {
529+
errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to watch this secret"))
530+
}
531+
532+
if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount},
533+
&authorizationv1.ResourceAttributes{
534+
Namespace: route.Namespace, Verb: "list", Resource: "secrets", Name: secretName,
535+
}); err != nil {
536+
errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to list this secret"))
537+
}
538+
539+
secret, err := secretsGetter.Secrets(route.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
540+
if err != nil {
541+
if apierrors.IsNotFound(err) {
542+
errs = append(errs, field.NotFound(fldPath, err.Error()))
543+
} else {
544+
errs = append(errs, field.InternalError(fldPath, err))
545+
}
546+
} else if secret.Type != kapi.SecretTypeTLS {
547+
errs = append(errs, field.Invalid(fldPath, secretName, fmt.Sprintf("secret of type %q required", kapi.SecretTypeTLS)))
548+
}
535549

536-
return errs
550+
asyncSARCache.Store(cacheKey, errs)
551+
}()
552+
553+
return nil
537554
}

vendor/github.com/MakeNowJust/heredoc/LICENSE

Lines changed: 0 additions & 21 deletions
This file was deleted.

vendor/github.com/MakeNowJust/heredoc/README.md

Lines changed: 0 additions & 53 deletions
This file was deleted.

vendor/github.com/MakeNowJust/heredoc/heredoc.go

Lines changed: 0 additions & 98 deletions
This file was deleted.

vendor/github.com/NYTimes/gziphandler/.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

vendor/github.com/NYTimes/gziphandler/.travis.yml

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)