diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go index f4277fdb4ca..27ced5552ad 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go @@ -197,6 +197,10 @@ func NewCustomResourceDefinitionHandler( // and on the client side (by restarting the watch) var longRunningFilter = genericfilters.BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString()) +// possiblyAcrossAllNamespacesVerbs contains those verbs which can be per-namespace and across all +// namespaces for namespaces resources. I.e. for these an empty namespace in the requestInfo is fine. +var possiblyAcrossAllNamespacesVerbs = sets.NewString("list", "watch") + func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { ctx := req.Context() requestInfo, ok := apirequest.RequestInfoFrom(ctx) @@ -232,10 +236,24 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } + + // if the scope in the CRD and the scope in request differ (with exception of the verbs in possiblyAcrossAllNamespacesVerbs + // for namespaced resources), pass request to the delegate, which is supposed to lead to a 404. + namespacedCRD, namespacedReq := crd.Spec.Scope == apiextensions.NamespaceScoped, len(requestInfo.Namespace) > 0 + if !namespacedCRD && namespacedReq { + r.delegate.ServeHTTP(w, req) + return + } + if namespacedCRD && !namespacedReq && !possiblyAcrossAllNamespacesVerbs.Has(requestInfo.Verb) { + r.delegate.ServeHTTP(w, req) + return + } + if !apiextensions.HasServedCRDVersion(crd, requestInfo.APIVersion) { r.delegate.ServeHTTP(w, req) return } + // There is a small chance that a CRD is being served because NamesAccepted condition is true, // but it becomes "unserved" because another names update leads to a conflict // and EstablishingController wasn't fast enough to put the CRD into the Established condition.