mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 10:28:36 +00:00
feat: webhook labels (#9015)
Signed-off-by: Zadkiel Aharonian <hello@zadkiel.fr> Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
parent
fd45cc5eee
commit
5e96b26a48
9 changed files with 108 additions and 18 deletions
|
@ -273,6 +273,7 @@ The chart values are organised per component.
|
|||
| config.resourceFilters | list | See [values.yaml](values.yaml) | Resource types to be skipped by the Kyverno policy engine. Make sure to surround each entry in quotes so that it doesn't get parsed as a nested YAML list. These are joined together without spaces, run through `tpl`, and the result is set in the config map. |
|
||||
| config.webhooks | list | `[]` | Defines the `namespaceSelector` in the webhook configurations. Note that it takes a list of `namespaceSelector` and/or `objectSelector` in the JSON format, and only the first element will be forwarded to the webhook configurations. The Kyverno namespace is excluded if `excludeKyvernoNamespace` is `true` (default) |
|
||||
| config.webhookAnnotations | object | `{}` | Defines annotations to set on webhook configurations. |
|
||||
| config.webhookLabels | object | `{}` | Defines labels to set on webhook configurations. |
|
||||
| config.matchConditions | list | `[]` | Defines match conditions to set on webhook configurations (requires Kubernetes 1.27+). |
|
||||
| config.excludeKyvernoNamespace | bool | `true` | Exclude Kyverno namespace Determines if default Kyverno namespace exclusion is enabled for webhooks and resourceFilters |
|
||||
| config.resourceFiltersExcludeNamespaces | list | `[]` | resourceFilter namespace exclude Namespaces to exclude from the default resourceFilters |
|
||||
|
|
|
@ -42,6 +42,9 @@ data:
|
|||
{{- with .Values.config.webhookAnnotations }}
|
||||
webhookAnnotations: {{ toJson . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.config.webhookLabels }}
|
||||
webhookLabels: {{ toJson . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.config.matchConditions }}
|
||||
matchConditions: {{ toJson . | quote }}
|
||||
{{- end }}
|
||||
|
|
|
@ -232,6 +232,11 @@ config:
|
|||
# Example to disable admission enforcer on AKS:
|
||||
# 'admissions.enforcer/disabled': 'true'
|
||||
|
||||
# -- Defines labels to set on webhook configurations.
|
||||
webhookLabels: {}
|
||||
# Example to adopt webhook resources in ArgoCD:
|
||||
# 'argocd.argoproj.io/instance': 'kyverno'
|
||||
|
||||
# -- Defines match conditions to set on webhook configurations (requires Kubernetes 1.27+).
|
||||
matchConditions: []
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ const (
|
|||
generateSuccessEvents = "generateSuccessEvents"
|
||||
webhooks = "webhooks"
|
||||
webhookAnnotations = "webhookAnnotations"
|
||||
webhookLabels = "webhookLabels"
|
||||
matchConditions = "matchConditions"
|
||||
)
|
||||
|
||||
|
@ -162,6 +163,8 @@ type Configuration interface {
|
|||
GetWebhooks() []WebhookConfig
|
||||
// GetWebhookAnnotations returns annotations to set on webhook configs
|
||||
GetWebhookAnnotations() map[string]string
|
||||
// GetWebhookLabels returns labels to set on webhook configs
|
||||
GetWebhookLabels() map[string]string
|
||||
// GetMatchConditions returns match conditions to set on webhook configs
|
||||
GetMatchConditions() []admissionregistrationv1.MatchCondition
|
||||
// Load loads configuration from a configmap
|
||||
|
@ -181,6 +184,7 @@ type configuration struct {
|
|||
generateSuccessEvents bool
|
||||
webhooks []WebhookConfig
|
||||
webhookAnnotations map[string]string
|
||||
webhookLabels map[string]string
|
||||
matchConditions []admissionregistrationv1.MatchCondition
|
||||
mux sync.RWMutex
|
||||
callbacks []func()
|
||||
|
@ -300,6 +304,12 @@ func (cd *configuration) GetWebhookAnnotations() map[string]string {
|
|||
return cd.webhookAnnotations
|
||||
}
|
||||
|
||||
func (cd *configuration) GetWebhookLabels() map[string]string {
|
||||
cd.mux.RLock()
|
||||
defer cd.mux.RUnlock()
|
||||
return cd.webhookLabels
|
||||
}
|
||||
|
||||
func (cd *configuration) GetMatchConditions() []admissionregistrationv1.MatchCondition {
|
||||
cd.mux.RLock()
|
||||
defer cd.mux.RUnlock()
|
||||
|
@ -332,6 +342,7 @@ func (cd *configuration) load(cm *corev1.ConfigMap) {
|
|||
cd.generateSuccessEvents = false
|
||||
cd.webhooks = nil
|
||||
cd.webhookAnnotations = nil
|
||||
cd.webhookLabels = nil
|
||||
cd.matchConditions = nil
|
||||
// load filters
|
||||
cd.filters = parseKinds(data[resourceFilters])
|
||||
|
@ -437,6 +448,20 @@ func (cd *configuration) load(cm *corev1.ConfigMap) {
|
|||
logger.Info("webhookAnnotations configured")
|
||||
}
|
||||
}
|
||||
// load webhook annotations
|
||||
webhookLabels, ok := data[webhookLabels]
|
||||
if !ok {
|
||||
logger.Info("webhookLabels not set")
|
||||
} else {
|
||||
logger := logger.WithValues("webhookLabels", webhookLabels)
|
||||
webhookLabels, err := parseWebhookLabels(webhookLabels)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to parse webhook labels")
|
||||
} else {
|
||||
cd.webhookLabels = webhookLabels
|
||||
logger.Info("webhookLabels configured")
|
||||
}
|
||||
}
|
||||
// load match conditions
|
||||
matchConditions, ok := data[matchConditions]
|
||||
if !ok {
|
||||
|
@ -465,6 +490,7 @@ func (cd *configuration) unload() {
|
|||
cd.generateSuccessEvents = false
|
||||
cd.webhooks = nil
|
||||
cd.webhookAnnotations = nil
|
||||
cd.webhookLabels = nil
|
||||
logger.Info("configuration unloaded")
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,14 @@ func parseWebhookAnnotations(in string) (map[string]string, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func parseWebhookLabels(in string) (map[string]string, error) {
|
||||
var out map[string]string
|
||||
if err := json.Unmarshal([]byte(in), &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseMatchConditions(in string) ([]admissionregistrationv1.MatchCondition, error) {
|
||||
var out []admissionregistrationv1.MatchCondition
|
||||
if err := json.Unmarshal([]byte(in), &out); err != nil {
|
||||
|
|
|
@ -258,6 +258,43 @@ func Test_parseWebhookAnnotations(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_parseWebhookLabels(t *testing.T) {
|
||||
type args struct {
|
||||
in string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want map[string]string
|
||||
wantErr bool
|
||||
}{{
|
||||
args: args{"hello"},
|
||||
wantErr: true,
|
||||
}, {
|
||||
args: args{""},
|
||||
wantErr: true,
|
||||
}, {
|
||||
args: args{"null"},
|
||||
}, {
|
||||
args: args{`{"a": "b"}`},
|
||||
want: map[string]string{
|
||||
"a": "b",
|
||||
},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseWebhookLabels(tt.args.in)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseWebhookLabels() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parseWebhookLabels() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseBucketBoundariesConfig(t *testing.T) {
|
||||
var emptyBoundaries []float64
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/logging"
|
||||
"github.com/kyverno/kyverno/pkg/tls"
|
||||
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
|
||||
"golang.org/x/exp/maps"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
@ -164,12 +165,16 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, _,
|
|||
return err
|
||||
}
|
||||
|
||||
func objectMeta(name string, annotations map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
|
||||
func objectMeta(name string, annotations map[string]string, labels map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
|
||||
desiredLabels := make(map[string]string)
|
||||
defaultLabels := map[string]string{
|
||||
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
|
||||
}
|
||||
maps.Copy(desiredLabels, labels)
|
||||
maps.Copy(desiredLabels, defaultLabels)
|
||||
return metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: map[string]string{
|
||||
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
|
||||
},
|
||||
Name: name,
|
||||
Labels: desiredLabels,
|
||||
Annotations: annotations,
|
||||
OwnerReferences: owner,
|
||||
}
|
||||
|
@ -177,7 +182,7 @@ func objectMeta(name string, annotations map[string]string, owner ...metav1.Owne
|
|||
|
||||
func (c *controller) build(cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
|
||||
return &admissionregistrationv1.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(c.webhookName, cfg.GetWebhookAnnotations()),
|
||||
ObjectMeta: objectMeta(c.webhookName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels()),
|
||||
Webhooks: []admissionregistrationv1.ValidatingWebhook{{
|
||||
Name: fmt.Sprintf("%s.%s.svc", config.KyvernoServiceName(), config.KyvernoNamespace()),
|
||||
ClientConfig: c.clientConfig(caBundle),
|
||||
|
|
|
@ -512,7 +512,7 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam
|
|||
|
||||
func (c *controller) buildVerifyMutatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
|
||||
return &admissionregistrationv1.MutatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.VerifyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
|
||||
ObjectMeta: objectMeta(config.VerifyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.MutatingWebhook{{
|
||||
Name: config.VerifyMutatingWebhookName,
|
||||
ClientConfig: c.clientConfig(caBundle, config.VerifyMutatingWebhookServicePath),
|
||||
|
@ -539,7 +539,7 @@ func (c *controller) buildVerifyMutatingWebhookConfiguration(_ context.Context,
|
|||
|
||||
func (c *controller) buildPolicyMutatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
|
||||
return &admissionregistrationv1.MutatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.PolicyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
|
||||
ObjectMeta: objectMeta(config.PolicyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.MutatingWebhook{{
|
||||
Name: config.PolicyMutatingWebhookName,
|
||||
ClientConfig: c.clientConfig(caBundle, config.PolicyMutatingWebhookServicePath),
|
||||
|
@ -562,7 +562,7 @@ func (c *controller) buildPolicyMutatingWebhookConfiguration(_ context.Context,
|
|||
|
||||
func (c *controller) buildPolicyValidatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
|
||||
return &admissionregistrationv1.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.PolicyValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
|
||||
ObjectMeta: objectMeta(config.PolicyValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.ValidatingWebhook{{
|
||||
Name: config.PolicyValidatingWebhookName,
|
||||
ClientConfig: c.clientConfig(caBundle, config.PolicyValidatingWebhookServicePath),
|
||||
|
@ -584,7 +584,7 @@ func (c *controller) buildPolicyValidatingWebhookConfiguration(_ context.Context
|
|||
|
||||
func (c *controller) buildDefaultResourceMutatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
|
||||
return &admissionregistrationv1.MutatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
|
||||
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.MutatingWebhook{{
|
||||
Name: config.MutatingWebhookName + "-ignore",
|
||||
ClientConfig: c.clientConfig(caBundle, config.MutatingWebhookServicePath+"/ignore"),
|
||||
|
@ -630,7 +630,7 @@ func (c *controller) buildDefaultResourceMutatingWebhookConfiguration(_ context.
|
|||
|
||||
func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
|
||||
result := admissionregistrationv1.MutatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
|
||||
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.MutatingWebhook{},
|
||||
}
|
||||
if c.watchdogCheck() {
|
||||
|
@ -708,7 +708,7 @@ func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(_ contex
|
|||
sideEffects = &noneOnDryRun
|
||||
}
|
||||
return &admissionregistrationv1.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
|
||||
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.ValidatingWebhook{{
|
||||
Name: config.ValidatingWebhookName + "-ignore",
|
||||
ClientConfig: c.clientConfig(caBundle, config.ValidatingWebhookServicePath+"/ignore"),
|
||||
|
@ -756,7 +756,7 @@ func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(_ contex
|
|||
|
||||
func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
|
||||
result := admissionregistrationv1.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
|
||||
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.ValidatingWebhook{},
|
||||
}
|
||||
if c.watchdogCheck() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/kyverno/kyverno/api/kyverno"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"golang.org/x/exp/maps"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -85,12 +86,16 @@ func (wh *webhook) isEmpty() bool {
|
|||
return len(wh.rules) == 0
|
||||
}
|
||||
|
||||
func objectMeta(name string, annotations map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
|
||||
func objectMeta(name string, annotations map[string]string, labels map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
|
||||
desiredLabels := make(map[string]string)
|
||||
defaultLabels := map[string]string{
|
||||
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
|
||||
}
|
||||
maps.Copy(desiredLabels, labels)
|
||||
maps.Copy(desiredLabels, defaultLabels)
|
||||
return metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: map[string]string{
|
||||
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
|
||||
},
|
||||
Name: name,
|
||||
Labels: desiredLabels,
|
||||
Annotations: annotations,
|
||||
OwnerReferences: owner,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue