Change:
If I am not mistaken, means that cluster-scoped resources other than namespaces are broken with admission webhooks. There is even a unit test checking that:
To clear any ambiguity around the word "broken", the current code behavior is that most cluster-scoped resources are not subjected to any webhooks. I wrote them like this because I was concerned with the potential bootstrapping deadlocks: a webhook that enacts policies for cluster-scoped resources might cause bootstrapping deadlocks for the cluster or for its own deployment. Thinking out loud, perhaps for cluster-scoped resources, the apiserver should only call the webhooks if the resources' labels match the , so that the critical bootstrapping resources can be excluded from the webhooks, thus breaking the bootstrapping deadlocks. This aligns with what we do for (see ). The downside is that the field name "namespaceSelector" becomes inappropriate.
I think we still need namespaceSelectors. We don't want to label pods. But I agree we actually want another objectLabelSelector. For namespaces these two coincide, i.e. we only have to define how they are applied if both are set or we forbid both at the same time. I tend to the later.
raised up a security issue regarding . A user can change labels to skip webhooks. doesn't have this issue because a user usually doesn't have permission to change the namespace's labels.
Users normally cannot create cluster-scoped objects either. But techincally it's a valid point. On the other hand, my main use-case are CRDs. They are not critical to the cluster health (maybe self-hosted clusters are different with etcd operator), i.e. you normally cannot shoot yourself into the foot with a webhook on them. For CRDs we do not need a selector at all. In other words: IMO we should enable webhooks for cluster-scoped objects, even if we have no in place.
Sounds reasonable. I'll create a PR ()
We would like to have the ability to apply the admission controller on specific namespaces only, and not on cluster level resources. This is useful if the list of possible resource Kinds that we want to apply the controller for is large, and we want to limit when it runs as much as possible to avoid affecting the whole cluster in case there's an issue. I think this can be done by adding a SkipClusterResources flag (or something similar) in addition to the NamespaceSelector, to the Webhook struct. What do you think?
I think adding a flag is reasonable. It doesn't have the security issue as the idea does (). The expressiveness of is limited compared to , e..g., admins cannot use to run webhooks on some cluster-level resources while skipping others. However it's not a big drawback, because admins can specify more detailed GVK to limit the scope of webhook in that case.
Zeroth thought: s/SkipClusterResources/scope=[clusterboth]/g First thought: can't you just exclude cluster scoped resources, or include only namespace scoped resources in the webhook definition? Second thought: can a namespace selector be crafted that excludes the empty string?
0: Are you sure that's cleaner? The namespace selector picks which namespaces to use, and skipCluster decides whether to include cluster resources or not. Not setting it maintains current behavior (skipCluster=false) and whether cluster level resources are included is very clear. scope=cluster/namespace/both will require us to default to "both" which I find slightly more confusing. Also what to do if I specify cluster AND include namespace selector - is that an invalid spec or do we ignore the selector? 1: Yes. This is the current best thing we can do. If the list of resources is large though (for example, if I am implementing an alternative resource quota controller) then this solution requires us to maintain and update a long list of resources. This may be ok, since as long as our controller can't support certain types of resources it doesn't make sense to watch for them... 2: Doesn't look like it - the current code specifically checks if the attribute is a cluster level resource and skips the entire label matching process.
Re: 0: fair point, but let's at least name it in the positive: matchClusterScopedResources=true|false or something like that.
I proposed it in the negative to make it backwards compatible, so that "false" keeps current behavior. I agree that negatively named booleans are not not bad. I'll treat it as true if unspecified and use matchClusterScopedResources.
In today's semantic, such a namespace selector will exclude namespaced objects whose namespace has no label. I find it confusing if we make it to also exclude non-namespaced objects.

Discussion:
"type": "string" }, "namespaceSelector": { <del> "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.nnFor example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1";  you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "runlevel",n      "operator": "NotIn",n      "values": [n        "0",n        "1"n      ]n    }n  ]n}nnIf instead you want to only run the webhook on any objects whose namespace is associated with the "environment" of "prod" or "staging"; you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "environment",n      "operator": "In",n      "values": [n        "prod",n        "staging"n      ]n    }n  ]n}nnSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.nnDefault to the empty LabelSelector, which matches everything.", </del> <ins> "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.nnFor example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1";  you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "runlevel",n      "operator": "NotIn",n      "values": [n        "0",n        "1"n      ]n    }n  ]n}nnIf instead you want to only run the webhook on any objects whose namespace is associated with the "environment" of "prod" or "staging"; you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "environment",n      "operator": "In",n      "values": [n        "prod",n        "staging"n      ]n    }n  ]n}nnSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.nnDefault to the empty LabelSelector, which matches everything.", </ins> "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" }, "rules": {

}, "namespaceSelector": { "$ref": "v1.LabelSelector", <del> "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.nnFor example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1";  you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "runlevel",n      "operator": "NotIn",n      "values": [n        "0",n        "1"n      ]n    }n  ]n}nnIf instead you want to only run the webhook on any objects whose namespace is associated with the "environment" of "prod" or "staging"; you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "environment",n      "operator": "In",n      "values": [n        "prod",n        "staging"n      ]n    }n  ]n}nnSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.nnDefault to the empty LabelSelector, which matches everything." </del> <ins> "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.nnFor example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1";  you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "runlevel",n      "operator": "NotIn",n      "values": [n        "0",n        "1"n      ]n    }n  ]n}nnIf instead you want to only run the webhook on any objects whose namespace is associated with the "environment" of "prod" or "staging"; you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "environment",n      "operator": "In",n      "values": [n        "prod",n        "staging"n      ]n    }n  ]n}nnSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.nnDefault to the empty LabelSelector, which matches everything." </ins> } } },

</tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">namespaceSelector</p></td> <del> <td class="tableblock halign-left valign-top"><p class="tableblock">NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.<br> </del> <ins> <td class="tableblock halign-left valign-top"><p class="tableblock">NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.<br> </ins> <br> For example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1";  you will set the selector as follows: "namespaceSelector": {<br> "matchExpressions": [<br>

// NamespaceSelector decides whether to run the webhook on an object based // on whether the namespace for that object matches the selector. If the // object itself is a namespace, the matching is performed on <del> // object.metadata.labels. If the object is other cluster scoped resource, // it is not subjected to the webhook. </del> <ins> // object.metadata.labels. If the object is another cluster scoped resource, // it never skips the webhook. </ins> // // For example, to run the webhook on any objects whose namespace is not // associated with "runlevel" of "0" or "1";  you will set the selector as

"clientConfig":      "ClientConfig defines how to communicate with the hook. Required", "rules":             "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule.", "failurePolicy":     "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", <del> "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is other cluster scoped resource, it is not subjected to the webhook.nnFor example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1";  you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "runlevel",n      "operator": "NotIn",n      "values": [n        "0",n        "1"n      ]n    }n  ]n}nnIf instead you want to only run the webhook on any objects whose namespace is associated with the "environment" of "prod" or "staging"; you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "environment",n      "operator": "In",n      "values": [n        "prod",n        "staging"n      ]n    }n  ]n}nnSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.nnDefault to the empty LabelSelector, which matches everything.", </del> <ins> "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.nnFor example, to run the webhook on any objects whose namespace is not associated with "runlevel" of "0" or "1";  you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "runlevel",n      "operator": "NotIn",n      "values": [n        "0",n        "1"n      ]n    }n  ]n}nnIf instead you want to only run the webhook on any objects whose namespace is associated with the "environment" of "prod" or "staging"; you will set the selector as follows: "namespaceSelector": {n  "matchExpressions": [n    {n      "key": "environment",n      "operator": "In",n      "values": [n        "prod",n        "staging"n      ]n    }n  ]n}nnSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.nnDefault to the empty LabelSelector, which matches everything.", </ins> } func (Webhook) SwaggerDoc() map[string]string {

namespaceName := attr.GetNamespace() if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" { // If the request is about a cluster scoped resource, and it is not a <del> // namespace, it is exempted from all webhooks for now. </del> <ins> // namespace, it is never exempted. </ins> // TODO: figure out a way selective exempt cluster scoped resources. // Also update the comment in types.go <del> return false, nil </del> <ins> return true, nil </ins> } namespaceLabels, err := m.GetNamespaceLabels(attr) // this means the namespace is not found, for backwards compatibility,

} } <del> func TestExemptClusterScopedResource(t *testing.T) { </del> <ins> func TestNotExemptClusterScopedResource(t *testing.T) { </ins> hook := &registrationv1beta1.Webhook{ NamespaceSelector: &metav1.LabelSelector{}, }

if err != nil { t.Fatal(err) } <del> if matches { t.Errorf("cluster scoped resources (but not a namespace) should be exempted from all webhooks") </del> <ins> if !matches { t.Errorf("cluster scoped resources (but not a namespace) should not be exempted from webhooks") </ins> } }