Merge pull request #74477 from liggitt/webhook-admission-scope
Webhook admission scope Kubernetes-commit: 8b052158c7381807e7dde41f7ff989e71093676e
This commit is contained in:
commit
733ab0d45c
|
@ -20,6 +20,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
)
|
||||
|
||||
|
@ -31,7 +33,8 @@ type Matcher struct {
|
|||
|
||||
// Matches returns if the Attr matches the Rule.
|
||||
func (r *Matcher) Matches() bool {
|
||||
return r.operation() &&
|
||||
return r.scope() &&
|
||||
r.operation() &&
|
||||
r.group() &&
|
||||
r.version() &&
|
||||
r.resource()
|
||||
|
@ -50,6 +53,25 @@ func exactOrWildcard(items []string, requested string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
var namespaceResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}
|
||||
|
||||
func (r *Matcher) scope() bool {
|
||||
if r.Rule.Scope == nil || *r.Rule.Scope == v1beta1.AllScopes {
|
||||
return true
|
||||
}
|
||||
// attr.GetNamespace() is set to the name of the namespace for requests of the namespace object itself.
|
||||
switch *r.Rule.Scope {
|
||||
case v1beta1.NamespacedScope:
|
||||
// first make sure that we are not requesting a namespace object (namespace objects are cluster-scoped)
|
||||
return r.Attr.GetResource() != namespaceResource && r.Attr.GetNamespace() != metav1.NamespaceNone
|
||||
case v1beta1.ClusterScope:
|
||||
// also return true if the request is for a namespace object (namespace objects are cluster-scoped)
|
||||
return r.Attr.GetResource() == namespaceResource || r.Attr.GetNamespace() == metav1.NamespaceNone
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Matcher) group() bool {
|
||||
return exactOrWildcard(r.Rule.APIGroups, r.Attr.GetResource().Group)
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ limitations under the License.
|
|||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
adreg "k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
)
|
||||
|
||||
|
@ -43,6 +45,30 @@ func a(group, version, resource, subresource, name string, operation admission.O
|
|||
)
|
||||
}
|
||||
|
||||
func namespacedAttributes(group, version, resource, subresource, name string, operation admission.Operation) admission.Attributes {
|
||||
return admission.NewAttributesRecord(
|
||||
nil, nil,
|
||||
schema.GroupVersionKind{Group: group, Version: version, Kind: "k" + resource},
|
||||
"ns", name,
|
||||
schema.GroupVersionResource{Group: group, Version: version, Resource: resource}, subresource,
|
||||
operation,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func clusterScopedAttributes(group, version, resource, subresource, name string, operation admission.Operation) admission.Attributes {
|
||||
return admission.NewAttributesRecord(
|
||||
nil, nil,
|
||||
schema.GroupVersionKind{Group: group, Version: version, Kind: "k" + resource},
|
||||
"", name,
|
||||
schema.GroupVersionResource{Group: group, Version: version, Resource: resource}, subresource,
|
||||
operation,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func attrList(a ...admission.Attributes) []admission.Attributes {
|
||||
return a
|
||||
}
|
||||
|
@ -299,3 +325,93 @@ func TestResource(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScope(t *testing.T) {
|
||||
cluster := adreg.ClusterScope
|
||||
namespace := adreg.NamespacedScope
|
||||
allscopes := adreg.AllScopes
|
||||
table := tests{
|
||||
"cluster scope": {
|
||||
rule: adreg.RuleWithOperations{
|
||||
Rule: adreg.Rule{
|
||||
Resources: []string{"*"},
|
||||
Scope: &cluster,
|
||||
},
|
||||
},
|
||||
match: attrList(
|
||||
clusterScopedAttributes("g", "v", "r", "", "name", admission.Create),
|
||||
clusterScopedAttributes("g", "v", "r", "exec", "name", admission.Create),
|
||||
clusterScopedAttributes("", "v1", "namespaces", "", "ns", admission.Create),
|
||||
clusterScopedAttributes("", "v1", "namespaces", "finalize", "ns", admission.Create),
|
||||
namespacedAttributes("", "v1", "namespaces", "", "ns", admission.Create),
|
||||
namespacedAttributes("", "v1", "namespaces", "finalize", "ns", admission.Create),
|
||||
),
|
||||
noMatch: attrList(
|
||||
namespacedAttributes("g", "v", "r", "", "name", admission.Create),
|
||||
namespacedAttributes("g", "v", "r", "exec", "name", admission.Create),
|
||||
),
|
||||
},
|
||||
"namespace scope": {
|
||||
rule: adreg.RuleWithOperations{
|
||||
Rule: adreg.Rule{
|
||||
Resources: []string{"*"},
|
||||
Scope: &namespace,
|
||||
},
|
||||
},
|
||||
match: attrList(
|
||||
namespacedAttributes("g", "v", "r", "", "name", admission.Create),
|
||||
namespacedAttributes("g", "v", "r", "exec", "name", admission.Create),
|
||||
),
|
||||
noMatch: attrList(
|
||||
clusterScopedAttributes("", "v1", "namespaces", "", "ns", admission.Create),
|
||||
clusterScopedAttributes("", "v1", "namespaces", "finalize", "ns", admission.Create),
|
||||
namespacedAttributes("", "v1", "namespaces", "", "ns", admission.Create),
|
||||
namespacedAttributes("", "v1", "namespaces", "finalize", "ns", admission.Create),
|
||||
clusterScopedAttributes("g", "v", "r", "", "name", admission.Create),
|
||||
clusterScopedAttributes("g", "v", "r", "exec", "name", admission.Create),
|
||||
),
|
||||
},
|
||||
"all scopes": {
|
||||
rule: adreg.RuleWithOperations{
|
||||
Rule: adreg.Rule{
|
||||
Resources: []string{"*"},
|
||||
Scope: &allscopes,
|
||||
},
|
||||
},
|
||||
match: attrList(
|
||||
namespacedAttributes("g", "v", "r", "", "name", admission.Create),
|
||||
namespacedAttributes("g", "v", "r", "exec", "name", admission.Create),
|
||||
clusterScopedAttributes("g", "v", "r", "", "name", admission.Create),
|
||||
clusterScopedAttributes("g", "v", "r", "exec", "name", admission.Create),
|
||||
clusterScopedAttributes("", "v1", "namespaces", "", "ns", admission.Create),
|
||||
clusterScopedAttributes("", "v1", "namespaces", "finalize", "ns", admission.Create),
|
||||
namespacedAttributes("", "v1", "namespaces", "", "ns", admission.Create),
|
||||
namespacedAttributes("", "v1", "namespaces", "finalize", "ns", admission.Create),
|
||||
),
|
||||
noMatch: attrList(),
|
||||
},
|
||||
}
|
||||
keys := sets.NewString()
|
||||
for name := range table {
|
||||
keys.Insert(name)
|
||||
}
|
||||
for _, name := range keys.List() {
|
||||
tt := table[name]
|
||||
for i, m := range tt.match {
|
||||
t.Run(fmt.Sprintf("%s_match_%d", name, i), func(t *testing.T) {
|
||||
r := Matcher{tt.rule, m}
|
||||
if !r.scope() {
|
||||
t.Errorf("%v: expected match %#v", name, m)
|
||||
}
|
||||
})
|
||||
}
|
||||
for i, m := range tt.noMatch {
|
||||
t.Run(fmt.Sprintf("%s_nomatch_%d", name, i), func(t *testing.T) {
|
||||
r := Matcher{tt.rule, m}
|
||||
if r.scope() {
|
||||
t.Errorf("%v: expected no match %#v", name, m)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue