mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 18:38:40 +00:00
feat: use kind selectors (#6514)
* fix: compile regex globally Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * feat: use kind selectors Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * clean Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * cache Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * kuttl Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * kuttl Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * webhooks rules Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * kuttl Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
a61dac613b
commit
5160b63154
39 changed files with 776 additions and 26 deletions
pkg
clients/dclient
controllers/webhook
utils/kube
test/conformance/kuttl/webhooks
all-scale
expected-webhooks
only-pod
pod-all-subresources
scale
wildcard
|
@ -8,14 +8,17 @@ import (
|
|||
|
||||
openapiv2 "github.com/google/gnostic/openapiv2"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/discovery"
|
||||
)
|
||||
|
||||
// IDiscovery provides interface to mange Kind and GVR mapping
|
||||
type IDiscovery interface {
|
||||
FindResources(group, version, kind, subresource string) ([]schema.GroupVersionResource, error)
|
||||
FindResource(groupVersion string, kind string) (apiResource, parentAPIResource *metav1.APIResource, gvr schema.GroupVersionResource, err error)
|
||||
// TODO: there's no mapping from GVK to GVR, this is very error prone
|
||||
GetGVRFromGVK(schema.GroupVersionKind) (schema.GroupVersionResource, error)
|
||||
|
@ -145,6 +148,97 @@ func (c serverResources) FindResource(groupVersion string, kind string) (apiReso
|
|||
return nil, nil, schema.GroupVersionResource{}, err
|
||||
}
|
||||
|
||||
func (c serverResources) FindResources(group, version, kind, subresource string) ([]schema.GroupVersionResource, error) {
|
||||
resources, err := c.findResources(group, version, kind, subresource)
|
||||
if err != nil {
|
||||
if !c.cachedClient.Fresh() {
|
||||
c.cachedClient.Invalidate()
|
||||
return c.findResources(group, version, kind, subresource)
|
||||
}
|
||||
}
|
||||
return resources, err
|
||||
}
|
||||
|
||||
func (c serverResources) findResources(group, version, kind, subresource string) ([]schema.GroupVersionResource, error) {
|
||||
_, serverGroupsAndResources, err := c.cachedClient.ServerGroupsAndResources()
|
||||
if err != nil && !strings.Contains(err.Error(), "Got empty response for") {
|
||||
if discovery.IsGroupDiscoveryFailedError(err) {
|
||||
logDiscoveryErrors(err)
|
||||
} else if isServerCurrentlyUnableToHandleRequest(err) {
|
||||
logger.Error(err, "failed to find preferred resource version")
|
||||
} else {
|
||||
logger.Error(err, "failed to find preferred resource version")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
getGVK := func(gv schema.GroupVersion, group, version, kind string) schema.GroupVersionKind {
|
||||
if group == "" {
|
||||
group = gv.Group
|
||||
}
|
||||
if version == "" {
|
||||
version = gv.Version
|
||||
}
|
||||
return schema.GroupVersionKind{
|
||||
Group: group,
|
||||
Version: version,
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
resources := sets.New[schema.GroupVersionResource]()
|
||||
// first match resouces
|
||||
for _, list := range serverGroupsAndResources {
|
||||
gv, err := schema.ParseGroupVersion(list.GroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, resource := range list.APIResources {
|
||||
if !strings.Contains(resource.Name, "/") {
|
||||
gvk := getGVK(gv, resource.Group, resource.Version, resource.Kind)
|
||||
if wildcard.Match(group, gvk.Group) && wildcard.Match(version, gvk.Version) && wildcard.Match(kind, gvk.Kind) {
|
||||
resources.Insert(gvk.GroupVersion().WithResource(resource.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// second match subresouces if necessary
|
||||
subresources := sets.New[schema.GroupVersionResource]()
|
||||
if subresource != "" {
|
||||
for _, list := range serverGroupsAndResources {
|
||||
for _, resource := range list.APIResources {
|
||||
for parent := range resources {
|
||||
if wildcard.Match(parent.Resource+"/"+subresource, resource.Name) {
|
||||
subresources.Insert(parent.GroupVersion().WithResource(resource.Name))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// third if no resource matched, try again but consider subresources this time
|
||||
if resources.Len() == 0 {
|
||||
for _, list := range serverGroupsAndResources {
|
||||
gv, err := schema.ParseGroupVersion(list.GroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, resource := range list.APIResources {
|
||||
gvk := getGVK(gv, resource.Group, resource.Version, resource.Kind)
|
||||
if wildcard.Match(group, gvk.Group) && wildcard.Match(version, gvk.Version) && wildcard.Match(kind, gvk.Kind) {
|
||||
resources.Insert(gv.WithResource(resource.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if kind == "*" && subresource == "*" {
|
||||
return resources.Union(subresources).UnsortedList(), nil
|
||||
} else if subresource != "" {
|
||||
return subresources.UnsortedList(), nil
|
||||
}
|
||||
return resources.UnsortedList(), nil
|
||||
}
|
||||
|
||||
func (c serverResources) findResource(groupVersion string, kind string) (apiResource, parentAPIResource *metav1.APIResource,
|
||||
gvr schema.GroupVersionResource, err error,
|
||||
) {
|
||||
|
|
|
@ -86,6 +86,10 @@ func (c *fakeDiscoveryClient) FindResource(groupVersion string, kind string) (ap
|
|||
return nil, nil, schema.GroupVersionResource{}, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) FindResources(group, version, kind, subresource string) ([]schema.GroupVersionResource, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) OpenAPISchema() (*openapiv2.Document, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -20,9 +20,16 @@ func logDiscoveryErrors(err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// isServerCurrentlyUnableToHandleRequest returns true if the error is related to the discovery not able to handle the request
|
||||
// this can happen with aggregated services when the api server can't get a `TokenReview` and is not able to send requests to
|
||||
// the underlying service, this is typically due to kyverno blocking `TokenReview` admission requests.
|
||||
func isServerCurrentlyUnableToHandleRequest(err error) bool {
|
||||
return err != nil && strings.Contains(err.Error(), "the server is currently unable to handle the request")
|
||||
}
|
||||
|
||||
func isMetricsServerUnavailable(gv schema.GroupVersion, err error) bool {
|
||||
// error message is defined at:
|
||||
// https://github.com/kubernetes/apimachinery/blob/2456ebdaba229616fab2161a615148884b46644b/pkg/api/errors/errors.go#L432
|
||||
return (gv.Group == "metrics.k8s.io" || gv.Group == "custom.metrics.k8s.io" || gv.Group == "external.metrics.k8s.io") &&
|
||||
strings.Contains(err.Error(), "the server is currently unable to handle the request")
|
||||
isServerCurrentlyUnableToHandleRequest(err)
|
||||
}
|
||||
|
|
105
pkg/clients/dclient/utils_test.go
Normal file
105
pkg/clients/dclient/utils_test.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package dclient
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func Test_isServerCurrentlyUnableToHandleRequest(t *testing.T) {
|
||||
type args struct {
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{{
|
||||
args: args{
|
||||
err: nil,
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
args: args{
|
||||
err: errors.New("another error"),
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
args: args{
|
||||
err: errors.New("the server is currently unable to handle the request"),
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
args: args{
|
||||
err: errors.New("a prefix : the server is currently unable to handle the request"),
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
args: args{
|
||||
err: errors.New("the server is currently unable to handle the request - a suffix"),
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
args: args{
|
||||
err: errors.New("a prefix : the server is currently unable to handle the request - a suffix"),
|
||||
},
|
||||
want: true,
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isServerCurrentlyUnableToHandleRequest(tt.args.err); got != tt.want {
|
||||
t.Errorf("isServerCurrentlyUnableToHandleRequest() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isMetricsServerUnavailable(t *testing.T) {
|
||||
type args struct {
|
||||
gv schema.GroupVersion
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{{
|
||||
args: args{
|
||||
gv: schema.GroupVersion{Group: "core", Version: "v1"},
|
||||
err: nil,
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
args: args{
|
||||
gv: schema.GroupVersion{Group: "core", Version: "v1"},
|
||||
err: errors.New("the server is currently unable to handle the request"),
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
args: args{
|
||||
gv: schema.GroupVersion{Group: "metrics.k8s.io", Version: "v1"},
|
||||
err: errors.New("the server is currently unable to handle the request"),
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
args: args{
|
||||
gv: schema.GroupVersion{Group: "custom.metrics.k8s.io", Version: "v1"},
|
||||
err: errors.New("the server is currently unable to handle the request"),
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
args: args{
|
||||
gv: schema.GroupVersion{Group: "external.metrics.k8s.io", Version: "v1"},
|
||||
err: errors.New("the server is currently unable to handle the request"),
|
||||
},
|
||||
want: true,
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isMetricsServerUnavailable(tt.args.gv, tt.args.err); got != tt.want {
|
||||
t.Errorf("isMetricsServerUnavailable() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -847,22 +847,13 @@ func (c *controller) mergeWebhook(dst *webhook, policy kyvernov1.PolicyInterface
|
|||
if _, ok := gvkMap[gvk]; !ok {
|
||||
gvkMap[gvk] = 1
|
||||
// NOTE: webhook stores GVR in its rules while policy stores GVK in its rules definition
|
||||
gv, k := kubeutils.GetKindFromGVK(gvk)
|
||||
_, parentAPIResource, gvr, err := c.discoveryClient.FindResource(gv, k)
|
||||
group, version, kind, subresource := kubeutils.ParseKindSelector(gvk)
|
||||
gvrs, err := c.discoveryClient.FindResources(group, version, kind, subresource)
|
||||
if err != nil {
|
||||
logger.Error(err, "unable to convert GVK to GVR", "GVK", gvk)
|
||||
logger.Error(err, "unable to find resource", "group", group, "version", version, "kind", kind, "subresource", subresource)
|
||||
continue
|
||||
}
|
||||
if parentAPIResource != nil {
|
||||
gvr = schema.GroupVersionResource{
|
||||
Group: parentAPIResource.Group,
|
||||
Version: parentAPIResource.Version,
|
||||
Resource: gvr.Resource,
|
||||
}
|
||||
}
|
||||
if strings.Contains(gvk, "*") {
|
||||
gvrList = append(gvrList, schema.GroupVersionResource{Group: gvr.Group, Version: "*", Resource: gvr.Resource})
|
||||
} else {
|
||||
for _, gvr := range gvrs {
|
||||
logger.V(4).Info("configuring webhook", "GVK", gvk, "GVR", gvr)
|
||||
gvrList = append(gvrList, gvr)
|
||||
}
|
||||
|
|
|
@ -17,30 +17,28 @@ import (
|
|||
type webhook struct {
|
||||
maxWebhookTimeout int32
|
||||
failurePolicy admissionregistrationv1.FailurePolicyType
|
||||
rules map[schema.GroupVersionResource]struct{}
|
||||
rules map[schema.GroupVersion]sets.Set[string]
|
||||
}
|
||||
|
||||
func newWebhook(timeout int32, failurePolicy admissionregistrationv1.FailurePolicyType) *webhook {
|
||||
return &webhook{
|
||||
maxWebhookTimeout: timeout,
|
||||
failurePolicy: failurePolicy,
|
||||
rules: map[schema.GroupVersionResource]struct{}{},
|
||||
rules: map[schema.GroupVersion]sets.Set[string]{},
|
||||
}
|
||||
}
|
||||
|
||||
func (wh *webhook) buildRulesWithOperations(ops ...admissionregistrationv1.OperationType) []admissionregistrationv1.RuleWithOperations {
|
||||
var rules []admissionregistrationv1.RuleWithOperations
|
||||
for gvr := range wh.rules {
|
||||
resources := sets.New(gvr.Resource)
|
||||
ephemeralContainersGVR := schema.GroupVersionResource{Resource: "pods/ephemeralcontainers", Group: "", Version: "v1"}
|
||||
_, rulesContainEphemeralContainers := wh.rules[ephemeralContainersGVR]
|
||||
if resources.Has("pods") && !rulesContainEphemeralContainers {
|
||||
for gv, resources := range wh.rules {
|
||||
// if we have pods, we add pods/ephemeralcontainers by default
|
||||
if gv.Group == "" && gv.Version == "v1" && resources.Has("pods") {
|
||||
resources.Insert("pods/ephemeralcontainers")
|
||||
}
|
||||
rules = append(rules, admissionregistrationv1.RuleWithOperations{
|
||||
Rule: admissionregistrationv1.Rule{
|
||||
APIGroups: []string{gvr.Group},
|
||||
APIVersions: []string{gvr.Version},
|
||||
APIGroups: []string{gv.Group},
|
||||
APIVersions: []string{gv.Version},
|
||||
Resources: sets.List(resources),
|
||||
},
|
||||
Operations: ops,
|
||||
|
@ -73,7 +71,13 @@ func (wh *webhook) buildRulesWithOperations(ops ...admissionregistrationv1.Opera
|
|||
}
|
||||
|
||||
func (wh *webhook) set(gvr schema.GroupVersionResource) {
|
||||
wh.rules[gvr] = struct{}{}
|
||||
gv := gvr.GroupVersion()
|
||||
resources := wh.rules[gv]
|
||||
if resources == nil {
|
||||
wh.rules[gv] = sets.New(gvr.Resource)
|
||||
} else {
|
||||
resources.Insert(gvr.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
func (wh *webhook) isEmpty() bool {
|
||||
|
@ -81,8 +85,8 @@ func (wh *webhook) isEmpty() bool {
|
|||
}
|
||||
|
||||
func (wh *webhook) setWildcard() {
|
||||
wh.rules = map[schema.GroupVersionResource]struct{}{
|
||||
{Group: "*", Version: "*", Resource: "*/*"}: {},
|
||||
wh.rules = map[schema.GroupVersion]sets.Set[string]{
|
||||
{Group: "*", Version: "*"}: sets.New("*/*"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,45 @@ import (
|
|||
|
||||
var versionRegex = regexp.MustCompile(`^v\d((alpha|beta)\d)?|\*$`)
|
||||
|
||||
func ParseKindSelector(input string) (string, string, string, string) {
|
||||
parts := strings.Split(input, "/")
|
||||
if len(parts) > 0 {
|
||||
parts = append(parts[:len(parts)-1], strings.Split(parts[len(parts)-1], ".")...)
|
||||
}
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
// we have only kind
|
||||
return "*", "*", parts[0], ""
|
||||
case 2:
|
||||
// `*/*` means all resources and subresources
|
||||
if parts[0] == "*" && parts[1] == "*" {
|
||||
return "*", "*", "*", "*"
|
||||
}
|
||||
// detect the `*/subresource` case when part[1] is all lowercase
|
||||
if parts[0] == "*" && strings.ToLower(parts[1]) == parts[1] {
|
||||
return "*", "*", parts[0], parts[1]
|
||||
}
|
||||
// if the first part is `*` or a version we have version/kind
|
||||
if versionRegex.MatchString(parts[0]) {
|
||||
return "*", parts[0], parts[1], ""
|
||||
}
|
||||
// we have kind/subresource
|
||||
return "*", "*", parts[0], parts[1]
|
||||
case 3:
|
||||
// if the first part is `*` or a version we have version/kind/subresource
|
||||
if versionRegex.MatchString(parts[0]) {
|
||||
return "*", parts[0], parts[1], parts[2]
|
||||
}
|
||||
// we have group/version/kind
|
||||
return parts[0], parts[1], parts[2], ""
|
||||
case 4:
|
||||
// we have group/version/kind/subresource
|
||||
return parts[0], parts[1], parts[2], parts[3]
|
||||
default:
|
||||
return "", "", "", ""
|
||||
}
|
||||
}
|
||||
|
||||
// GetKindFromGVK - get kind and APIVersion from GVK
|
||||
func GetKindFromGVK(str string) (string, string) {
|
||||
parts := strings.Split(str, "/")
|
||||
|
|
|
@ -108,3 +108,106 @@ func Test_GroupVersionMatches(t *testing.T) {
|
|||
groupVersion, serverResourceGroupVersion = "certificates.k8s.io/*", "networking.k8s.io/v1"
|
||||
assert.Equal(t, GroupVersionMatches(groupVersion, serverResourceGroupVersion), false)
|
||||
}
|
||||
|
||||
func TestParseKindSelector(t *testing.T) {
|
||||
type args struct {
|
||||
input string
|
||||
}
|
||||
type want struct {
|
||||
group string
|
||||
version string
|
||||
kind string
|
||||
subresource string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{{
|
||||
args: args{"*"},
|
||||
want: want{"*", "*", "*", ""},
|
||||
}, {
|
||||
args: args{"*.*"},
|
||||
want: want{"*", "*", "*", "*"},
|
||||
}, {
|
||||
args: args{"*/*"},
|
||||
want: want{"*", "*", "*", "*"},
|
||||
}, {
|
||||
args: args{"Pod"},
|
||||
want: want{"*", "*", "Pod", ""},
|
||||
}, {
|
||||
args: args{"v1/Pod"},
|
||||
want: want{"*", "v1", "Pod", ""},
|
||||
}, {
|
||||
args: args{"batch/*/CronJob"},
|
||||
want: want{"batch", "*", "CronJob", ""},
|
||||
}, {
|
||||
args: args{"storage.k8s.io/v1/CSIDriver"},
|
||||
want: want{"storage.k8s.io", "v1", "CSIDriver", ""},
|
||||
}, {
|
||||
args: args{"tekton.dev/v1beta1/TaskRun/status"},
|
||||
want: want{"tekton.dev", "v1beta1", "TaskRun", "status"},
|
||||
}, {
|
||||
args: args{"v1/Pod.status"},
|
||||
want: want{"*", "v1", "Pod", "status"},
|
||||
}, {
|
||||
args: args{"v1/Pod/status"},
|
||||
want: want{"*", "v1", "Pod", "status"},
|
||||
}, {
|
||||
args: args{"Pod.status"},
|
||||
want: want{"*", "*", "Pod", "status"},
|
||||
}, {
|
||||
args: args{"Pod/status"},
|
||||
want: want{"*", "*", "Pod", "status"},
|
||||
}, {
|
||||
args: args{"apps/v1/Deployment/scale"},
|
||||
want: want{"apps", "v1", "Deployment", "scale"},
|
||||
}, {
|
||||
args: args{"v1/ReplicationController/scale"},
|
||||
want: want{"*", "v1", "ReplicationController", "scale"},
|
||||
}, {
|
||||
args: args{"*/ReplicationController/scale"},
|
||||
want: want{"*", "*", "ReplicationController", "scale"},
|
||||
}, {
|
||||
args: args{"*/Deployment/scale"},
|
||||
want: want{"*", "*", "Deployment", "scale"},
|
||||
}, {
|
||||
args: args{"*/Deployment.scale"},
|
||||
want: want{"*", "*", "Deployment", "scale"},
|
||||
}, {
|
||||
args: args{"apps/v1/Deployment.scale"},
|
||||
want: want{"apps", "v1", "Deployment", "scale"},
|
||||
}, {
|
||||
args: args{"*/scale"},
|
||||
want: want{"*", "*", "*", "scale"},
|
||||
}, {
|
||||
args: args{"Pod/*"},
|
||||
want: want{"*", "*", "Pod", "*"},
|
||||
}, {
|
||||
args: args{"*/*/*"},
|
||||
want: want{"*", "*", "*", "*"},
|
||||
}, {
|
||||
args: args{"*/*/*/*"},
|
||||
want: want{"*", "*", "*", "*"},
|
||||
}, {
|
||||
args: args{"*/*/*/*/*"},
|
||||
want: want{"", "", "", ""},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
group, version, kind, subresource := ParseKindSelector(tt.args.input)
|
||||
if group != tt.want.group {
|
||||
t.Errorf("ParseKindSelector() group = %v, want %v", group, tt.want.group)
|
||||
}
|
||||
if version != tt.want.version {
|
||||
t.Errorf("ParseKindSelector() version = %v, want %v", version, tt.want.version)
|
||||
}
|
||||
if kind != tt.want.kind {
|
||||
t.Errorf("ParseKindSelector() kind = %v, want %v", kind, tt.want.kind)
|
||||
}
|
||||
if subresource != tt.want.subresource {
|
||||
t.Errorf("ParseKindSelector() subresource = %v, want %v", subresource, tt.want.subresource)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
6
test/conformance/kuttl/webhooks/all-scale/01-policy.yaml
Normal file
6
test/conformance/kuttl/webhooks/all-scale/01-policy.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
9
test/conformance/kuttl/webhooks/all-scale/README.md
Normal file
9
test/conformance/kuttl/webhooks/all-scale/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies the resource validation webhook is configured correctly when a policy targets all `*/scale` subresources.
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy targeting `*/scale`
|
||||
- Assert policy gets ready
|
||||
1. - Assert that the resource validation webhook is configured correctly
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
22
test/conformance/kuttl/webhooks/all-scale/policy.yaml
Normal file
22
test/conformance/kuttl/webhooks/all-scale/policy.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
34
test/conformance/kuttl/webhooks/all-scale/webhooks.yaml
Normal file
34
test/conformance/kuttl/webhooks/all-scale/webhooks.yaml
Normal file
|
@ -0,0 +1,34 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- replicationcontrollers/scale
|
||||
scope: '*'
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- deployments/scale
|
||||
- replicasets/scale
|
||||
- statefulsets/scale
|
||||
scope: '*'
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
assert:
|
||||
- webhooks.yaml
|
6
test/conformance/kuttl/webhooks/only-pod/01-policy.yaml
Normal file
6
test/conformance/kuttl/webhooks/only-pod/01-policy.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
assert:
|
||||
- webhooks.yaml
|
9
test/conformance/kuttl/webhooks/only-pod/README.md
Normal file
9
test/conformance/kuttl/webhooks/only-pod/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies the resource validation webhook is configured correctly when a policy targets `Pod`.
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy targeting `Pod`
|
||||
- Assert policy gets ready
|
||||
1. - Assert that the resource validation webhook is configured correctly
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
22
test/conformance/kuttl/webhooks/only-pod/policy.yaml
Normal file
22
test/conformance/kuttl/webhooks/only-pod/policy.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
21
test/conformance/kuttl/webhooks/only-pod/webhooks.yaml
Normal file
21
test/conformance/kuttl/webhooks/only-pod/webhooks.yaml
Normal file
|
@ -0,0 +1,21 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- pods
|
||||
- pods/ephemeralcontainers
|
||||
scope: '*'
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
assert:
|
||||
- webhooks.yaml
|
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies the resource validation webhook is configured correctly when a policy targets all `Pod/*` subresources.
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy targeting `Pod/*`
|
||||
- Assert policy gets ready
|
||||
1. - Assert that the resource validation webhook is configured correctly
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,22 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod/*
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- pods/attach
|
||||
- pods/binding
|
||||
- pods/ephemeralcontainers
|
||||
- pods/eviction
|
||||
- pods/exec
|
||||
- pods/log
|
||||
- pods/portforward
|
||||
- pods/proxy
|
||||
- pods/status
|
||||
scope: '*'
|
6
test/conformance/kuttl/webhooks/scale/01-policy.yaml
Normal file
6
test/conformance/kuttl/webhooks/scale/01-policy.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
4
test/conformance/kuttl/webhooks/scale/02-webhooks.yaml
Normal file
4
test/conformance/kuttl/webhooks/scale/02-webhooks.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
assert:
|
||||
- webhooks.yaml
|
10
test/conformance/kuttl/webhooks/scale/README.md
Normal file
10
test/conformance/kuttl/webhooks/scale/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
## Description
|
||||
|
||||
This test verifies the resource validation webhook is configured correctly when a policy targets all `Scale` resource.
|
||||
It should be equivalent to using `*/scale`
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy targeting `Scale`
|
||||
- Assert policy gets ready
|
||||
1. - Assert that the resource validation webhook is configured correctly
|
9
test/conformance/kuttl/webhooks/scale/policy-assert.yaml
Normal file
9
test/conformance/kuttl/webhooks/scale/policy-assert.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
22
test/conformance/kuttl/webhooks/scale/policy.yaml
Normal file
22
test/conformance/kuttl/webhooks/scale/policy.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Scale
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
34
test/conformance/kuttl/webhooks/scale/webhooks.yaml
Normal file
34
test/conformance/kuttl/webhooks/scale/webhooks.yaml
Normal file
|
@ -0,0 +1,34 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- replicationcontrollers/scale
|
||||
scope: '*'
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- deployments/scale
|
||||
- replicasets/scale
|
||||
- statefulsets/scale
|
||||
scope: '*'
|
6
test/conformance/kuttl/webhooks/wildcard/01-policy.yaml
Normal file
6
test/conformance/kuttl/webhooks/wildcard/01-policy.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- policy.yaml
|
||||
assert:
|
||||
- policy-assert.yaml
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
assert:
|
||||
- webhooks.yaml
|
9
test/conformance/kuttl/webhooks/wildcard/README.md
Normal file
9
test/conformance/kuttl/webhooks/wildcard/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies the resource validation webhook is configured correctly when a policy targets all `*` resource.
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy targeting `*`
|
||||
- Assert policy gets ready
|
||||
1. - Assert that the resource validation webhook is configured correctly
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
22
test/conformance/kuttl/webhooks/wildcard/policy.yaml
Normal file
22
test/conformance/kuttl/webhooks/wildcard/policy.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*'
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
35
test/conformance/kuttl/webhooks/wildcard/webhooks.yaml
Normal file
35
test/conformance/kuttl/webhooks/wildcard/webhooks.yaml
Normal file
|
@ -0,0 +1,35 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- failurePolicy: Ignore
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- '*/*'
|
||||
scope: '*'
|
||||
- failurePolicy: Fail
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
- CONNECT
|
||||
resources:
|
||||
- '*/*'
|
||||
scope: '*'
|
Loading…
Add table
Reference in a new issue