mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
feat: delete webhook configurations after kyverno is uninstalled (#10782)
* feat: delete webhook configurations after kyverno is uninstalled Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: optionally add permissions Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * fix: linter Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * fix: disable finalizers in latest manifest Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * fix: move webhook cleanup to webhooks controller Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: add finalizers on deployment Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * fix: refactor Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: add roles to cleanupcontroller Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: add cleanup to generic controllers Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: add webhook cleanup in generic controllers Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: remove unnecessary clusterrole and clusterrole bindings Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: make this behaviour opt-in Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: reconcile webhook setup on deployment change Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * fix: update codegen and remove unused vars Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: add finalizers to chart Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> --------- Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> Co-authored-by: Jim Bugwadia <jim@nirmata.com> Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
parent
416b7d2f8b
commit
c0d6eaddb3
22 changed files with 880 additions and 72 deletions
|
@ -702,6 +702,7 @@ The chart values are organised per component.
|
|||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| webhooksCleanup.enabled | bool | `true` | Create a helm pre-delete hook to cleanup webhooks. |
|
||||
| webhooksCleanup.autoDeleteWebhooks.enabled | bool | `false` | Allow webhooks controller to delete webhooks using finalizers |
|
||||
| webhooksCleanup.image.registry | string | `nil` | Image registry |
|
||||
| webhooksCleanup.image.repository | string | `"bitnami/kubectl"` | Image repository |
|
||||
| webhooksCleanup.image.tag | string | `"1.30.2"` | Image tag Defaults to `latest` if omitted |
|
||||
|
|
|
@ -16,6 +16,14 @@ apiVersion: rbac.authorization.k8s.io/v1
|
|||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ template "kyverno.admission-controller.roleName" . }}:core
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/webhooks
|
||||
- kyverno.io/exceptionwebhooks
|
||||
- kyverno.io/globalcontextwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.admission-controller.labels" . | nindent 4 }}
|
||||
rules:
|
||||
|
@ -139,6 +147,31 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- clusterroles
|
||||
- clusterrolebindings
|
||||
resourceNames:
|
||||
- {{ template "kyverno.admission-controller.roleName" . }}
|
||||
- {{ template "kyverno.admission-controller.roleName" . }}:core
|
||||
- {{ template "kyverno.admission-controller.roleName" . }}:temporary
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- clusterroles
|
||||
- clusterrolebindings
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.admissionController.rbac.coreClusterRole.extraResources }}
|
||||
{{- toYaml . | nindent 2 }}
|
||||
{{- end }}
|
||||
|
@ -153,4 +186,4 @@ metadata:
|
|||
rules:
|
||||
{{- toYaml . | nindent 2 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
@ -4,6 +4,14 @@ kind: Deployment
|
|||
metadata:
|
||||
name: {{ template "kyverno.admission-controller.name" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/webhooks
|
||||
- kyverno.io/exceptionwebhooks
|
||||
- kyverno.io/globalcontextwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.admission-controller.labels" . | nindent 4 }}
|
||||
{{- with .Values.admissionController.annotations }}
|
||||
|
@ -105,6 +113,8 @@ spec:
|
|||
env:
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: {{ template "kyverno.admission-controller.serviceAccountName" . }}
|
||||
- name: KYVERNO_ROLE_NAME
|
||||
value: {{ template "kyverno.admission-controller.roleName" . }}
|
||||
- name: INIT_CONFIG
|
||||
value: {{ template "kyverno.config.configMapName" . }}
|
||||
- name: METRICS_CONFIG
|
||||
|
@ -138,6 +148,9 @@ spec:
|
|||
- --reportsServiceAccountName=system:serviceaccount:{{ include "kyverno.namespace" . }}:{{ include "kyverno.reports-controller.serviceAccountName" . }}
|
||||
- --servicePort={{ .Values.admissionController.service.port }}
|
||||
- --webhookServerPort={{ .Values.admissionController.webhookServer.port }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
- --autoDeleteWebhooks
|
||||
{{- end }}
|
||||
{{- if .Values.admissionController.tracing.enabled }}
|
||||
- --enableTracing
|
||||
- --tracingAddress={{ .Values.admissionController.tracing.address }}
|
||||
|
@ -220,6 +233,8 @@ spec:
|
|||
fieldPath: metadata.name
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: {{ template "kyverno.admission-controller.serviceAccountName" . }}
|
||||
- name: KYVERNO_ROLE_NAME
|
||||
value: {{ template "kyverno.admission-controller.roleName" . }}
|
||||
- name: KYVERNO_SVC
|
||||
value: {{ template "kyverno.admission-controller.serviceName" . }}
|
||||
- name: TUF_ROOT
|
||||
|
|
|
@ -4,6 +4,14 @@ kind: Role
|
|||
metadata:
|
||||
name: {{ template "kyverno.admission-controller.roleName" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/webhooks
|
||||
- kyverno.io/exceptionwebhooks
|
||||
- kyverno.io/globalcontextwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.admission-controller.labels" . | nindent 4 }}
|
||||
rules:
|
||||
|
@ -11,10 +19,12 @@ rules:
|
|||
- ''
|
||||
resources:
|
||||
- secrets
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- patch
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
|
@ -39,6 +49,29 @@ rules:
|
|||
- get
|
||||
- patch
|
||||
- update
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- roles
|
||||
- rolebindings
|
||||
resourceNames:
|
||||
- {{ template "kyverno.admission-controller.roleName" . }}
|
||||
- {{ template "kyverno.admission-controller.roleName" . }}:temporary
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- roles
|
||||
- rolebindings
|
||||
verbs:
|
||||
- create
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
# Allow update of Kyverno deployment annotations
|
||||
- apiGroups:
|
||||
- apps
|
||||
|
|
|
@ -4,6 +4,14 @@ apiVersion: rbac.authorization.k8s.io/v1
|
|||
metadata:
|
||||
name: {{ template "kyverno.admission-controller.roleName" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/webhooks
|
||||
- kyverno.io/exceptionwebhooks
|
||||
- kyverno.io/globalcontextwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.admission-controller.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
|
|
|
@ -4,6 +4,14 @@ kind: ServiceAccount
|
|||
metadata:
|
||||
name: {{ template "kyverno.admission-controller.serviceAccountName" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/webhooks
|
||||
- kyverno.io/exceptionwebhooks
|
||||
- kyverno.io/globalcontextwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.admission-controller.labels" . | nindent 4 }}
|
||||
{{- with .Values.admissionController.rbac.serviceAccount.annotations }}
|
||||
|
|
|
@ -17,6 +17,13 @@ apiVersion: rbac.authorization.k8s.io/v1
|
|||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ template "kyverno.cleanup-controller.roleName" . }}:core
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/policywebhooks
|
||||
- kyverno.io/ttlwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.cleanup-controller.labels" . | nindent 4 }}
|
||||
rules:
|
||||
|
@ -97,6 +104,31 @@ rules:
|
|||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- clusterroles
|
||||
- clusterrolebindings
|
||||
resourceNames:
|
||||
- {{ template "kyverno.cleanup-controller.roleName" . }}
|
||||
- {{ template "kyverno.cleanup-controller.roleName" . }}:core
|
||||
- {{ template "kyverno.cleanup-controller.roleName" . }}:temporary
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- clusterroles
|
||||
- clusterrolebindings
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.cleanupController.rbac.clusterRole.extraResources }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
@ -109,4 +141,4 @@ rules:
|
|||
{{- toYaml . | nindent 2 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
@ -5,6 +5,13 @@ kind: Deployment
|
|||
metadata:
|
||||
name: {{ template "kyverno.cleanup-controller.name" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/policywebhooks
|
||||
- kyverno.io/ttlwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.cleanup-controller.labels" . | nindent 4 }}
|
||||
{{- with .Values.cleanupController.annotations }}
|
||||
|
@ -101,6 +108,9 @@ spec:
|
|||
- --servicePort={{ .Values.cleanupController.service.port }}
|
||||
- --cleanupServerPort={{ .Values.cleanupController.server.port }}
|
||||
- --webhookServerPort={{ .Values.cleanupController.webhookServer.port }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
- --autoDeleteWebhooks
|
||||
{{- end }}
|
||||
{{- if .Values.cleanupController.tracing.enabled }}
|
||||
- --enableTracing
|
||||
- --tracingAddress={{ .Values.cleanupController.tracing.address }}
|
||||
|
@ -150,6 +160,8 @@ spec:
|
|||
fieldPath: metadata.name
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: {{ template "kyverno.cleanup-controller.serviceAccountName" . }}
|
||||
- name: KYVERNO_ROLE_NAME
|
||||
value: {{ template "kyverno.cleanup-controller.roleName" . }}
|
||||
- name: KYVERNO_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
|
|
|
@ -4,6 +4,13 @@ apiVersion: rbac.authorization.k8s.io/v1
|
|||
kind: Role
|
||||
metadata:
|
||||
name: {{ template "kyverno.cleanup-controller.roleName" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/policywebhooks
|
||||
- kyverno.io/ttlwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.cleanup-controller.labels" . | nindent 4 }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
|
@ -27,6 +34,22 @@ rules:
|
|||
resourceNames:
|
||||
- {{ template "kyverno.cleanup-controller.name" . }}.{{ template "kyverno.namespace" . }}.svc.kyverno-tls-ca
|
||||
- {{ template "kyverno.cleanup-controller.name" . }}.{{ template "kyverno.namespace" . }}.svc.kyverno-tls-pair
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
resourceNames:
|
||||
- {{ template "kyverno.cleanup-controller.serviceAccountName" . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
|
@ -55,5 +78,42 @@ rules:
|
|||
- update
|
||||
resourceNames:
|
||||
- kyverno-cleanup-controller
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- roles
|
||||
- rolebindings
|
||||
resourceNames:
|
||||
- {{ template "kyverno.cleanup-controller.roleName" . }}
|
||||
- {{ template "kyverno.cleanup-controller.roleName" . }}:temporary
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- roles
|
||||
- rolebindings
|
||||
verbs:
|
||||
- create
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
- patch
|
||||
- update
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
|
|
@ -4,6 +4,13 @@ kind: RoleBinding
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: {{ template "kyverno.cleanup-controller.roleName" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/policywebhooks
|
||||
- kyverno.io/ttlwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.cleanup-controller.labels" . | nindent 4 }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
|
|
|
@ -5,6 +5,13 @@ kind: ServiceAccount
|
|||
metadata:
|
||||
name: {{ template "kyverno.cleanup-controller.serviceAccountName" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
{{- if .Values.webhooksCleanup.autoDeleteWebhooks.enabled }}
|
||||
{{- if not .Values.templating.enabled }}
|
||||
finalizers:
|
||||
- kyverno.io/policywebhooks
|
||||
- kyverno.io/ttlwebhooks
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "kyverno.cleanup-controller.labels" . | nindent 4 }}
|
||||
{{- with .Values.cleanupController.rbac.serviceAccount.annotations }}
|
||||
|
|
|
@ -469,6 +469,10 @@ webhooksCleanup:
|
|||
# -- Create a helm pre-delete hook to cleanup webhooks.
|
||||
enabled: true
|
||||
|
||||
autoDeleteWebhooks:
|
||||
# -- Allow webhooks controller to delete webhooks using finalizers
|
||||
enabled: false
|
||||
|
||||
image:
|
||||
# -- (string) Image registry
|
||||
registry: ~
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
genericwebhookcontroller "github.com/kyverno/kyverno/pkg/controllers/generic/webhook"
|
||||
globalcontextcontroller "github.com/kyverno/kyverno/pkg/controllers/globalcontext"
|
||||
ttlcontroller "github.com/kyverno/kyverno/pkg/controllers/ttl"
|
||||
webhookcontroller "github.com/kyverno/kyverno/pkg/controllers/webhook"
|
||||
"github.com/kyverno/kyverno/pkg/event"
|
||||
"github.com/kyverno/kyverno/pkg/globalcontext/store"
|
||||
"github.com/kyverno/kyverno/pkg/informers"
|
||||
|
@ -29,6 +30,7 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/tls"
|
||||
"github.com/kyverno/kyverno/pkg/toggle"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
|
||||
"github.com/kyverno/kyverno/pkg/webhooks"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -38,10 +40,12 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
resyncPeriod = 15 * time.Minute
|
||||
webhookWorkers = 2
|
||||
policyWebhookControllerName = "policy-webhook-controller"
|
||||
ttlWebhookControllerName = "ttl-webhook-controller"
|
||||
resyncPeriod = 15 * time.Minute
|
||||
webhookWorkers = 2
|
||||
policyWebhookControllerName = "policy-webhook-controller"
|
||||
ttlWebhookControllerName = "ttl-webhook-controller"
|
||||
policyWebhookControllerFinalizerName = "kyverno.io/policywebhooks"
|
||||
ttlWebhookControllerFinalizerName = "kyverno.io/ttlwebhooks"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -78,6 +82,7 @@ func main() {
|
|||
interval time.Duration
|
||||
renewBefore time.Duration
|
||||
maxAPICallResponseLength int64
|
||||
autoDeleteWebhooks bool
|
||||
)
|
||||
flagset := flag.NewFlagSet("cleanup-controller", flag.ExitOnError)
|
||||
flagset.BoolVar(&dumpPayload, "dumpPayload", false, "Set this flag to activate/deactivate debug mode.")
|
||||
|
@ -91,6 +96,7 @@ func main() {
|
|||
flagset.StringVar(&tlsSecretName, "tlsSecretName", "", "Name of the secret containing TLS pair.")
|
||||
flagset.DurationVar(&renewBefore, "renewBefore", 15*24*time.Hour, "The certificate renewal time before expiration")
|
||||
flagset.Int64Var(&maxAPICallResponseLength, "maxAPICallResponseLength", 2*1000*1000, "Maximum allowed response size from API Calls. A value of 0 bypasses checks (not recommended).")
|
||||
flagset.BoolVar(&autoDeleteWebhooks, "autoDeleteWebhooks", false, "Set this flag to 'true' to enable autodeletion of webhook configurations using finalizers (requires extra permissions).")
|
||||
// config
|
||||
appConfig := internal.NewConfiguration(
|
||||
internal.WithProfiling(),
|
||||
|
@ -129,7 +135,8 @@ func main() {
|
|||
// certificates informers
|
||||
caSecret := informers.NewSecretInformer(setup.KubeClient, config.KyvernoNamespace(), caSecretName, resyncPeriod)
|
||||
tlsSecret := informers.NewSecretInformer(setup.KubeClient, config.KyvernoNamespace(), tlsSecretName, resyncPeriod)
|
||||
if !informers.StartInformersAndWaitForCacheSync(ctx, setup.Logger, caSecret, tlsSecret) {
|
||||
kyvernoDeployment := informers.NewDeploymentInformer(setup.KubeClient, config.KyvernoNamespace(), config.KyvernoDeploymentName(), resyncPeriod)
|
||||
if !informers.StartInformersAndWaitForCacheSync(ctx, setup.Logger, caSecret, tlsSecret, kyvernoDeployment) {
|
||||
setup.Logger.Error(errors.New("failed to wait for cache sync"), "failed to wait for cache sync")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -180,6 +187,12 @@ func main() {
|
|||
if !internal.StartInformersAndWaitForCacheSync(ctx, setup.Logger, kubeInformer, kyvernoInformer) {
|
||||
os.Exit(1)
|
||||
}
|
||||
runtime := runtimeutils.NewRuntime(
|
||||
setup.Logger.WithName("runtime-checks"),
|
||||
serverIP,
|
||||
kyvernoDeployment,
|
||||
nil,
|
||||
)
|
||||
// setup leader election
|
||||
le, err := leaderelection.New(
|
||||
setup.Logger.WithName("leader-election"),
|
||||
|
@ -229,6 +242,7 @@ func main() {
|
|||
setup.KubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
|
||||
kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
|
||||
caSecret,
|
||||
kyvernoDeployment,
|
||||
config.CleanupValidatingWebhookConfigurationName,
|
||||
config.CleanupValidatingWebhookServicePath,
|
||||
serverIP,
|
||||
|
@ -255,6 +269,10 @@ func main() {
|
|||
genericwebhookcontroller.None,
|
||||
setup.Configuration,
|
||||
caSecretName,
|
||||
runtime,
|
||||
autoDeleteWebhooks,
|
||||
webhookcontroller.WebhookCleanupSetup(setup.KubeClient, policyWebhookControllerFinalizerName),
|
||||
webhookcontroller.WebhookCleanupHandler(setup.KubeClient, policyWebhookControllerFinalizerName),
|
||||
),
|
||||
webhookWorkers,
|
||||
)
|
||||
|
@ -265,6 +283,7 @@ func main() {
|
|||
setup.KubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
|
||||
kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
|
||||
caSecret,
|
||||
kyvernoDeployment,
|
||||
config.TtlValidatingWebhookConfigurationName,
|
||||
config.TtlValidatingWebhookServicePath,
|
||||
serverIP,
|
||||
|
@ -295,6 +314,10 @@ func main() {
|
|||
genericwebhookcontroller.None,
|
||||
setup.Configuration,
|
||||
caSecretName,
|
||||
runtime,
|
||||
autoDeleteWebhooks,
|
||||
webhookcontroller.WebhookCleanupSetup(setup.KubeClient, ttlWebhookControllerFinalizerName),
|
||||
webhookcontroller.WebhookCleanupHandler(setup.KubeClient, ttlWebhookControllerFinalizerName),
|
||||
),
|
||||
webhookWorkers,
|
||||
)
|
||||
|
|
|
@ -51,15 +51,19 @@ import (
|
|||
corev1 "k8s.io/api/core/v1"
|
||||
apiserver "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
appsv1informers "k8s.io/client-go/informers/apps/v1"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
kyamlopenapi "sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
const (
|
||||
resyncPeriod = 15 * time.Minute
|
||||
exceptionWebhookControllerName = "exception-webhook-controller"
|
||||
gctxWebhookControllerName = "global-context-webhook-controller"
|
||||
resyncPeriod = 15 * time.Minute
|
||||
exceptionWebhookControllerName = "exception-webhook-controller"
|
||||
gctxWebhookControllerName = "global-context-webhook-controller"
|
||||
webhookControllerFinalizerName = "kyverno.io/webhooks"
|
||||
exceptionControllerFinalizerName = "kyverno.io/exceptionwebhooks"
|
||||
gctxControllerFinalizerName = "kyverno.io/globalcontextwebhooks"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -107,11 +111,13 @@ func createrLeaderControllers(
|
|||
serverIP string,
|
||||
webhookTimeout int,
|
||||
autoUpdateWebhooks bool,
|
||||
autoDeleteWebhooks bool,
|
||||
kubeInformer kubeinformers.SharedInformerFactory,
|
||||
kubeKyvernoInformer kubeinformers.SharedInformerFactory,
|
||||
kyvernoInformer kyvernoinformer.SharedInformerFactory,
|
||||
caInformer corev1informers.SecretInformer,
|
||||
tlsInformer corev1informers.SecretInformer,
|
||||
deploymentInformer appsv1informers.DeploymentInformer,
|
||||
kubeClient kubernetes.Interface,
|
||||
kyvernoClient versioned.Interface,
|
||||
dynamicClient dclient.Interface,
|
||||
|
@ -141,6 +147,7 @@ func createrLeaderControllers(
|
|||
kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
|
||||
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
|
||||
kyvernoInformer.Kyverno().V1().Policies(),
|
||||
deploymentInformer,
|
||||
caInformer,
|
||||
kubeKyvernoInformer.Coordination().V1().Leases(),
|
||||
kubeInformer.Rbac().V1().ClusterRoles(),
|
||||
|
@ -150,16 +157,20 @@ func createrLeaderControllers(
|
|||
servicePort,
|
||||
webhookServerPort,
|
||||
autoUpdateWebhooks,
|
||||
autoDeleteWebhooks,
|
||||
admissionReports,
|
||||
runtime,
|
||||
configuration,
|
||||
caSecretName,
|
||||
webhookcontroller.WebhookCleanupSetup(kubeClient, webhookControllerFinalizerName),
|
||||
webhookcontroller.WebhookCleanupHandler(kubeClient, webhookControllerFinalizerName),
|
||||
)
|
||||
exceptionWebhookController := genericwebhookcontroller.NewController(
|
||||
exceptionWebhookControllerName,
|
||||
kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
|
||||
kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
|
||||
caInformer,
|
||||
deploymentInformer,
|
||||
config.ExceptionValidatingWebhookConfigurationName,
|
||||
config.ExceptionValidatingWebhookServicePath,
|
||||
serverIP,
|
||||
|
@ -181,12 +192,17 @@ func createrLeaderControllers(
|
|||
genericwebhookcontroller.None,
|
||||
configuration,
|
||||
caSecretName,
|
||||
runtime,
|
||||
autoDeleteWebhooks,
|
||||
webhookcontroller.WebhookCleanupSetup(kubeClient, exceptionControllerFinalizerName),
|
||||
webhookcontroller.WebhookCleanupHandler(kubeClient, exceptionControllerFinalizerName),
|
||||
)
|
||||
gctxWebhookController := genericwebhookcontroller.NewController(
|
||||
gctxWebhookControllerName,
|
||||
kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
|
||||
kubeInformer.Admissionregistration().V1().ValidatingWebhookConfigurations(),
|
||||
caInformer,
|
||||
deploymentInformer,
|
||||
config.GlobalContextValidatingWebhookConfigurationName,
|
||||
config.GlobalContextValidatingWebhookServicePath,
|
||||
serverIP,
|
||||
|
@ -208,6 +224,10 @@ func createrLeaderControllers(
|
|||
genericwebhookcontroller.None,
|
||||
configuration,
|
||||
caSecretName,
|
||||
runtime,
|
||||
autoDeleteWebhooks,
|
||||
webhookcontroller.WebhookCleanupSetup(kubeClient, gctxControllerFinalizerName),
|
||||
webhookcontroller.WebhookCleanupHandler(kubeClient, gctxControllerFinalizerName),
|
||||
)
|
||||
leaderControllers = append(leaderControllers, internal.NewController(certmanager.ControllerName, certManager, certmanager.Workers))
|
||||
leaderControllers = append(leaderControllers, internal.NewController(webhookcontroller.ControllerName, webhookController, webhookcontroller.Workers))
|
||||
|
@ -241,6 +261,7 @@ func main() {
|
|||
maxQueuedEvents int
|
||||
omitEvents string
|
||||
autoUpdateWebhooks bool
|
||||
autoDeleteWebhooks bool
|
||||
webhookRegistrationTimeout time.Duration
|
||||
admissionReports bool
|
||||
dumpPayload bool
|
||||
|
@ -261,6 +282,7 @@ func main() {
|
|||
flagset.StringVar(&omitEvents, "omitEvents", "", "Set this flag to a comma sperated list of PolicyViolation, PolicyApplied, PolicyError, PolicySkipped to disable events, e.g. --omitEvents=PolicyApplied,PolicyViolation")
|
||||
flagset.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.")
|
||||
flagset.BoolVar(&autoUpdateWebhooks, "autoUpdateWebhooks", true, "Set this flag to 'false' to disable auto-configuration of the webhook.")
|
||||
flagset.BoolVar(&autoDeleteWebhooks, "autoDeleteWebhooks", false, "Set this flag to 'true' to enable autodeletion of webhook configurations using finalizers (requires extra permissions).")
|
||||
flagset.DurationVar(&webhookRegistrationTimeout, "webhookRegistrationTimeout", 120*time.Second, "Timeout for webhook registration, e.g., 30s, 1m, 5m.")
|
||||
flagset.Func(toggle.ProtectManagedResourcesFlagName, toggle.ProtectManagedResourcesDescription, toggle.ProtectManagedResources.Parse)
|
||||
flagset.Func(toggle.ForceFailurePolicyIgnoreFlagName, toggle.ForceFailurePolicyIgnoreDescription, toggle.ForceFailurePolicyIgnore.Parse)
|
||||
|
@ -324,7 +346,8 @@ func main() {
|
|||
}
|
||||
caSecret := informers.NewSecretInformer(setup.KubeClient, config.KyvernoNamespace(), caSecretName, resyncPeriod)
|
||||
tlsSecret := informers.NewSecretInformer(setup.KubeClient, config.KyvernoNamespace(), tlsSecretName, resyncPeriod)
|
||||
if !informers.StartInformersAndWaitForCacheSync(signalCtx, setup.Logger, caSecret, tlsSecret) {
|
||||
kyvernoDeployment := informers.NewDeploymentInformer(setup.KubeClient, config.KyvernoNamespace(), config.KyvernoDeploymentName(), resyncPeriod)
|
||||
if !informers.StartInformersAndWaitForCacheSync(signalCtx, setup.Logger, caSecret, tlsSecret, kyvernoDeployment) {
|
||||
setup.Logger.Error(errors.New("failed to wait for cache sync"), "failed to wait for cache sync")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -342,6 +365,7 @@ func main() {
|
|||
kubeInformer := kubeinformers.NewSharedInformerFactory(setup.KubeClient, resyncPeriod)
|
||||
kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(setup.KubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace()))
|
||||
kyvernoInformer := kyvernoinformer.NewSharedInformerFactory(setup.KyvernoClient, resyncPeriod)
|
||||
|
||||
certRenewer := tls.NewCertRenewer(
|
||||
setup.KubeClient.CoreV1().Secrets(config.KyvernoNamespace()),
|
||||
tls.CertRenewalInterval,
|
||||
|
@ -463,11 +487,13 @@ func main() {
|
|||
serverIP,
|
||||
webhookTimeout,
|
||||
autoUpdateWebhooks,
|
||||
autoDeleteWebhooks,
|
||||
kubeInformer,
|
||||
kubeKyvernoInformer,
|
||||
kyvernoInformer,
|
||||
caSecret,
|
||||
tlsSecret,
|
||||
kyvernoDeployment,
|
||||
setup.KubeClient,
|
||||
setup.KyvernoClient,
|
||||
setup.KyvernoDynamicClient,
|
||||
|
|
|
@ -48730,10 +48730,12 @@ rules:
|
|||
- ''
|
||||
resources:
|
||||
- secrets
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- patch
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
|
@ -48874,6 +48876,14 @@ rules:
|
|||
- update
|
||||
resourceNames:
|
||||
- kyverno-cleanup-controller
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
|
@ -49205,6 +49215,8 @@ spec:
|
|||
env:
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: kyverno-admission-controller
|
||||
- name: KYVERNO_ROLE_NAME
|
||||
value: kyverno:admission-controller
|
||||
- name: INIT_CONFIG
|
||||
value: kyverno
|
||||
- name: METRICS_CONFIG
|
||||
|
@ -49291,6 +49303,8 @@ spec:
|
|||
fieldPath: metadata.name
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: kyverno-admission-controller
|
||||
- name: KYVERNO_ROLE_NAME
|
||||
value: kyverno:admission-controller
|
||||
- name: KYVERNO_SVC
|
||||
value: kyverno-svc
|
||||
- name: TUF_ROOT
|
||||
|
@ -49522,6 +49536,8 @@ spec:
|
|||
fieldPath: metadata.name
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: kyverno-cleanup-controller
|
||||
- name: KYVERNO_ROLE_NAME
|
||||
value: kyverno:cleanup-controller
|
||||
- name: KYVERNO_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
|
|
|
@ -106,6 +106,8 @@ var (
|
|||
kyvernoNamespace = osutils.GetEnvWithFallback("KYVERNO_NAMESPACE", "kyverno")
|
||||
// kyvernoServiceAccountName is the Kyverno service account name
|
||||
kyvernoServiceAccountName = osutils.GetEnvWithFallback("KYVERNO_SERVICEACCOUNT_NAME", "kyverno")
|
||||
// kyvernoRoleName is the Kyverno rbac name
|
||||
kyvernoRoleName = osutils.GetEnvWithFallback("KYVERNO_ROLE_NAME", "kyverno")
|
||||
// kyvernoDeploymentName is the Kyverno deployment name
|
||||
kyvernoDeploymentName = osutils.GetEnvWithFallback("KYVERNO_DEPLOYMENT", "kyverno")
|
||||
// kyvernoServiceName is the Kyverno service name
|
||||
|
@ -132,6 +134,10 @@ func KyvernoServiceAccountName() string {
|
|||
return kyvernoServiceAccountName
|
||||
}
|
||||
|
||||
func KyvernoRoleName() string {
|
||||
return kyvernoRoleName
|
||||
}
|
||||
|
||||
func KyvernoDeploymentName() string {
|
||||
return kyvernoDeploymentName
|
||||
}
|
||||
|
|
|
@ -12,14 +12,18 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/logging"
|
||||
"github.com/kyverno/kyverno/pkg/tls"
|
||||
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
|
||||
runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
|
||||
"golang.org/x/exp/maps"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
admissionregistrationv1informers "k8s.io/client-go/informers/admissionregistration/v1"
|
||||
appsv1informers "k8s.io/client-go/informers/apps/v1"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
admissionregistrationv1listers "k8s.io/client-go/listers/admissionregistration/v1"
|
||||
appsv1listers "k8s.io/client-go/listers/apps/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
@ -42,25 +46,31 @@ type controller struct {
|
|||
vwcClient controllerutils.ObjectClient[*admissionregistrationv1.ValidatingWebhookConfiguration]
|
||||
|
||||
// listers
|
||||
vwcLister admissionregistrationv1listers.ValidatingWebhookConfigurationLister
|
||||
secretLister corev1listers.SecretNamespaceLister
|
||||
vwcLister admissionregistrationv1listers.ValidatingWebhookConfigurationLister
|
||||
secretLister corev1listers.SecretNamespaceLister
|
||||
deploymentLister appsv1listers.DeploymentNamespaceLister
|
||||
|
||||
// queue
|
||||
queue workqueue.TypedRateLimitingInterface[any]
|
||||
|
||||
// config
|
||||
controllerName string
|
||||
logger logr.Logger
|
||||
webhookName string
|
||||
path string
|
||||
server string
|
||||
servicePort int32
|
||||
rules []admissionregistrationv1.RuleWithOperations
|
||||
failurePolicy *admissionregistrationv1.FailurePolicyType
|
||||
sideEffects *admissionregistrationv1.SideEffectClass
|
||||
configuration config.Configuration
|
||||
labelSelector *metav1.LabelSelector
|
||||
caSecretName string
|
||||
controllerName string
|
||||
logger logr.Logger
|
||||
webhookName string
|
||||
path string
|
||||
server string
|
||||
servicePort int32
|
||||
rules []admissionregistrationv1.RuleWithOperations
|
||||
failurePolicy *admissionregistrationv1.FailurePolicyType
|
||||
sideEffects *admissionregistrationv1.SideEffectClass
|
||||
runtime runtimeutils.Runtime
|
||||
configuration config.Configuration
|
||||
labelSelector *metav1.LabelSelector
|
||||
caSecretName string
|
||||
webhooksDeleted bool
|
||||
autoDeleteWebhooks bool
|
||||
webhookCleanupSetup func(context.Context, logr.Logger) error
|
||||
postWebhookCleanup func(context.Context, logr.Logger) error
|
||||
}
|
||||
|
||||
func NewController(
|
||||
|
@ -68,6 +78,7 @@ func NewController(
|
|||
vwcClient controllerutils.ObjectClient[*admissionregistrationv1.ValidatingWebhookConfiguration],
|
||||
vwcInformer admissionregistrationv1informers.ValidatingWebhookConfigurationInformer,
|
||||
secretInformer corev1informers.SecretInformer,
|
||||
deploymentInformer appsv1informers.DeploymentInformer,
|
||||
webhookName string,
|
||||
path string,
|
||||
server string,
|
||||
|
@ -79,25 +90,34 @@ func NewController(
|
|||
sideEffects *admissionregistrationv1.SideEffectClass,
|
||||
configuration config.Configuration,
|
||||
caSecretName string,
|
||||
runtime runtimeutils.Runtime,
|
||||
autoDeleteWebhooks bool,
|
||||
webhookCleanupSetup func(context.Context, logr.Logger) error,
|
||||
postWebhookCleanup func(context.Context, logr.Logger) error,
|
||||
) controllers.Controller {
|
||||
queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any](), controllerName)
|
||||
c := controller{
|
||||
vwcClient: vwcClient,
|
||||
vwcLister: vwcInformer.Lister(),
|
||||
secretLister: secretInformer.Lister().Secrets(config.KyvernoNamespace()),
|
||||
queue: queue,
|
||||
controllerName: controllerName,
|
||||
logger: logging.ControllerLogger(controllerName),
|
||||
webhookName: webhookName,
|
||||
path: path,
|
||||
server: server,
|
||||
servicePort: servicePort,
|
||||
rules: rules,
|
||||
failurePolicy: failurePolicy,
|
||||
sideEffects: sideEffects,
|
||||
configuration: configuration,
|
||||
labelSelector: labelSelector,
|
||||
caSecretName: caSecretName,
|
||||
vwcClient: vwcClient,
|
||||
vwcLister: vwcInformer.Lister(),
|
||||
secretLister: secretInformer.Lister().Secrets(config.KyvernoNamespace()),
|
||||
deploymentLister: deploymentInformer.Lister().Deployments(config.KyvernoNamespace()),
|
||||
queue: queue,
|
||||
controllerName: controllerName,
|
||||
logger: logging.ControllerLogger(controllerName),
|
||||
webhookName: webhookName,
|
||||
path: path,
|
||||
server: server,
|
||||
servicePort: servicePort,
|
||||
rules: rules,
|
||||
failurePolicy: failurePolicy,
|
||||
sideEffects: sideEffects,
|
||||
configuration: configuration,
|
||||
labelSelector: labelSelector,
|
||||
caSecretName: caSecretName,
|
||||
runtime: runtime,
|
||||
autoDeleteWebhooks: autoDeleteWebhooks,
|
||||
webhookCleanupSetup: webhookCleanupSetup,
|
||||
postWebhookCleanup: postWebhookCleanup,
|
||||
}
|
||||
if _, _, err := controllerutils.AddDefaultEventHandlers(c.logger, vwcInformer.Informer(), queue); err != nil {
|
||||
c.logger.Error(err, "failed to register event handlers")
|
||||
|
@ -122,11 +142,34 @@ func NewController(
|
|||
); err != nil {
|
||||
c.logger.Error(err, "failed to register event handlers")
|
||||
}
|
||||
if autoDeleteWebhooks {
|
||||
if _, err := controllerutils.AddEventHandlersT(
|
||||
deploymentInformer.Informer(),
|
||||
func(obj *appsv1.Deployment) {
|
||||
},
|
||||
func(_, obj *appsv1.Deployment) {
|
||||
if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == config.KyvernoDeploymentName() {
|
||||
c.enqueueCleanupAfter(1 * time.Second)
|
||||
}
|
||||
},
|
||||
func(obj *appsv1.Deployment) {
|
||||
if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == config.KyvernoDeploymentName() {
|
||||
c.enqueueCleanup()
|
||||
}
|
||||
},
|
||||
); err != nil {
|
||||
c.logger.Error(err, "failed to register event handlers")
|
||||
}
|
||||
}
|
||||
|
||||
configuration.OnChanged(c.enqueue)
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *controller) Run(ctx context.Context, workers int) {
|
||||
if err := c.webhookCleanupSetup(ctx, c.logger); err != nil {
|
||||
c.logger.Error(err, "failed to setup webhook cleanup")
|
||||
}
|
||||
c.enqueue()
|
||||
controllerutils.Run(ctx, c.logger, c.controllerName, time.Second, c.queue, workers, maxRetries, c.reconcile)
|
||||
}
|
||||
|
@ -135,7 +178,22 @@ func (c *controller) enqueue() {
|
|||
c.queue.Add(c.webhookName)
|
||||
}
|
||||
|
||||
func (c *controller) enqueueCleanup() {
|
||||
c.queue.Add(config.KyvernoDeploymentName())
|
||||
}
|
||||
|
||||
func (c *controller) enqueueCleanupAfter(duration time.Duration) {
|
||||
c.queue.AddAfter(config.KyvernoDeploymentName(), duration)
|
||||
}
|
||||
|
||||
func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, _, _ string) error {
|
||||
if c.autoDeleteWebhooks && c.runtime.IsGoingDown() {
|
||||
return c.reconcileWebhookDeletion(ctx)
|
||||
}
|
||||
|
||||
if c.autoDeleteWebhooks && key == config.KyvernoDeploymentName() {
|
||||
return c.reconcileWebhookDeletion(ctx)
|
||||
}
|
||||
if key != c.webhookName {
|
||||
return nil
|
||||
}
|
||||
|
@ -165,6 +223,37 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, _,
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *controller) reconcileWebhookDeletion(ctx context.Context) error {
|
||||
if c.autoDeleteWebhooks {
|
||||
if c.runtime.IsGoingDown() {
|
||||
if c.webhooksDeleted {
|
||||
return nil
|
||||
}
|
||||
c.webhooksDeleted = true
|
||||
if err := c.vwcClient.Delete(ctx, c.webhookName, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
|
||||
c.logger.Error(err, "failed to clean up validating webhook configuration", "label", kyverno.LabelWebhookManagedBy)
|
||||
return err
|
||||
} else if err == nil {
|
||||
c.logger.Info("successfully deleted validating webhook configurations", "label", kyverno.LabelWebhookManagedBy)
|
||||
}
|
||||
|
||||
if err := c.postWebhookCleanup(ctx, c.logger); err != nil {
|
||||
c.logger.Error(err, "failed to clean up temporary rbac")
|
||||
return err
|
||||
} else {
|
||||
c.logger.Info("successfully deleted temporary rbac")
|
||||
}
|
||||
} else {
|
||||
if err := c.webhookCleanupSetup(ctx, c.logger); err != nil {
|
||||
c.logger.Error(err, "failed to reconcile webhook cleanup setup")
|
||||
return err
|
||||
}
|
||||
c.logger.Info("reconciled webhook cleanup setup")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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{
|
||||
|
|
282
pkg/controllers/webhook/cleanup.go
Normal file
282
pkg/controllers/webhook/cleanup.go
Normal file
|
@ -0,0 +1,282 @@
|
|||
package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/util/retry"
|
||||
)
|
||||
|
||||
// WebhookCleanupSetup creates temporary rbac owned by kyverno resources, these roles and cluster roles get automatically deleted when kyverno is uninstalled
|
||||
// It creates the following resources:
|
||||
// 1. Creates a temporary cluster role binding to give permission to delete kyverno's cluster role and set its owner ref to aggregated cluster role itself.
|
||||
// 2. Creates a temporary role and role binding with permissions to delete a service account, roles and role bindings with owner ref set to the service account.
|
||||
func WebhookCleanupSetup(
|
||||
kubeClient kubernetes.Interface,
|
||||
finalizer string,
|
||||
) func(context.Context, logr.Logger) error {
|
||||
return func(ctx context.Context, logger logr.Logger) error {
|
||||
name := config.KyvernoRoleName()
|
||||
coreName := name + ":core"
|
||||
tempRbacName := name + ":temporary"
|
||||
|
||||
// create temporary rbac
|
||||
cr, err := kubeClient.RbacV1().ClusterRoles().Get(ctx, coreName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to get cluster role binding")
|
||||
return err
|
||||
}
|
||||
|
||||
coreClusterRoleBinding := &rbacv1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: coreName,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "rbac.authorization.k8s.io/v1",
|
||||
Kind: "ClusterRole",
|
||||
Name: cr.Name,
|
||||
UID: cr.UID,
|
||||
},
|
||||
},
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
Name: config.KyvernoServiceAccountName(),
|
||||
Namespace: config.KyvernoNamespace(),
|
||||
APIGroup: "",
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "ClusterRole",
|
||||
Name: coreName,
|
||||
},
|
||||
}
|
||||
|
||||
if crb, err := kubeClient.RbacV1().ClusterRoleBindings().Create(ctx, coreClusterRoleBinding, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
logger.Error(err, "failed to create temporary clusterrolebinding", "name", crb.Name)
|
||||
return err
|
||||
} else if !apierrors.IsAlreadyExists(err) {
|
||||
logger.V(4).Info("temporary clusterrolebinding created", "clusterrolebinding", crb.Name)
|
||||
}
|
||||
|
||||
// create temporary rbac
|
||||
sa, err := kubeClient.CoreV1().ServiceAccounts(config.KyvernoNamespace()).Get(ctx, config.KyvernoServiceAccountName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to get service account")
|
||||
return err
|
||||
}
|
||||
|
||||
role := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: tempRbacName,
|
||||
Namespace: config.KyvernoNamespace(),
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "v1",
|
||||
Kind: "ServiceAccount",
|
||||
Name: sa.Name,
|
||||
UID: sa.UID,
|
||||
},
|
||||
},
|
||||
},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"serviceaccounts"},
|
||||
ResourceNames: []string{config.KyvernoServiceAccountName()},
|
||||
Verbs: []string{"get", "update", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
Resources: []string{"rolebindings", "roles"},
|
||||
ResourceNames: []string{name},
|
||||
Verbs: []string{"get", "update"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"apps"},
|
||||
Resources: []string{"deployments"},
|
||||
ResourceNames: []string{config.KyvernoDeploymentName()},
|
||||
Verbs: []string{"get", "update"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if r, err := kubeClient.RbacV1().Roles(config.KyvernoNamespace()).Create(ctx, role, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
logger.Error(err, "failed to create temporary role", "name", r.Name)
|
||||
return err
|
||||
} else if !apierrors.IsAlreadyExists(err) {
|
||||
logger.V(4).Info("temporary role created in kyverno namespace", "role", r.Name, "namespace", r.Namespace)
|
||||
}
|
||||
|
||||
roleBinding := &rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: tempRbacName,
|
||||
Namespace: config.KyvernoNamespace(),
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "v1",
|
||||
Kind: "ServiceAccount",
|
||||
Name: sa.Name,
|
||||
UID: sa.UID,
|
||||
},
|
||||
},
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
Name: config.KyvernoServiceAccountName(),
|
||||
Namespace: config.KyvernoNamespace(),
|
||||
APIGroup: "",
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "Role",
|
||||
Name: tempRbacName,
|
||||
},
|
||||
}
|
||||
|
||||
if rb, err := kubeClient.RbacV1().RoleBindings(config.KyvernoNamespace()).Create(ctx, roleBinding, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
logger.Error(err, "failed to create temporary rolebinding", "name", rb.Name)
|
||||
return err
|
||||
} else if !apierrors.IsAlreadyExists(err) {
|
||||
logger.V(4).Info("temporary rolebinding created in kyverno namespace", "rolebinding", rb.Name, "namespace", rb.Namespace)
|
||||
}
|
||||
|
||||
// Add finalizers
|
||||
if err := AddFinalizers(ctx, kubeClient.RbacV1().ClusterRoles(), coreName, finalizer); err != nil && !apierrors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to add finalizer to clusterrole", "name", coreName)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AddFinalizers(ctx, kubeClient.RbacV1().RoleBindings(config.KyvernoNamespace()), name, finalizer); err != nil {
|
||||
logger.Error(err, "failed to add finalizer to rolebindings", "name", name, "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AddFinalizers(ctx, kubeClient.RbacV1().Roles(config.KyvernoNamespace()), name, finalizer); err != nil {
|
||||
logger.Error(err, "failed to add finalizer to role", "name", name, "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AddFinalizers(ctx, kubeClient.AppsV1().Deployments(config.KyvernoNamespace()), config.KyvernoDeploymentName(), finalizer); err != nil {
|
||||
logger.Error(err, "failed to add finalizer to deployment", "name", config.KyvernoDeploymentName(), "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AddFinalizers(ctx, kubeClient.CoreV1().ServiceAccounts(config.KyvernoNamespace()), config.KyvernoServiceAccountName(), finalizer); err != nil {
|
||||
logger.Error(err, "failed to add finalizer to serviceaccount", "name", config.KyvernoServiceAccountName(), "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WebhookCleanupHandler is run after webhook configuration cleanup is performed to delete roles and service account.
|
||||
// Admission controller cluster and namespaced roles and role bindings have finalizers to block their deletion until admission controller terminates.
|
||||
// This handler removes the finalizers on roles and service account after they are used to cleanup webhook cfg.
|
||||
// It does the following:
|
||||
//
|
||||
// Deletes the cluster scoped rbac in order:
|
||||
// a. Removes finalizers from controller cluster role binding
|
||||
// b. Removes finalizers from controller core cluster role
|
||||
// c. Removes finalizers from controller aggregated cluster role
|
||||
// d. Temporary cluster role and cluster role binding created by WebhookCleanupSetup gets garbage collected after (c) automatically
|
||||
//
|
||||
// Deletes the namespace scoped rbac in order:
|
||||
// a. Removes finalizers from controller role binding.
|
||||
// b. Removes finalizers from controller role.
|
||||
// c. Removes finalizers from controller service account
|
||||
// d. Temporary role and role binding created by WebhookCleanupSetup gets garbage collected after (c) automatically
|
||||
func WebhookCleanupHandler(
|
||||
kubeClient kubernetes.Interface,
|
||||
finalizer string,
|
||||
) func(context.Context, logr.Logger) error {
|
||||
return func(ctx context.Context, logger logr.Logger) error {
|
||||
name := config.KyvernoRoleName()
|
||||
coreName := name + ":core"
|
||||
|
||||
// cleanup cluster scoped rbac
|
||||
if err := DeleteFinalizers(ctx, kubeClient.RbacV1().ClusterRoles(), coreName, finalizer); err != nil {
|
||||
logger.Error(err, "failed to delete finalizer from clusterrole", "name", coreName)
|
||||
return err
|
||||
}
|
||||
|
||||
// cleanup namespace scoped rbac
|
||||
if err := DeleteFinalizers(ctx, kubeClient.RbacV1().RoleBindings(config.KyvernoNamespace()), name, finalizer); err != nil {
|
||||
logger.Error(err, "failed to delete finalizer from rolebindings", "name", name, "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := DeleteFinalizers(ctx, kubeClient.RbacV1().Roles(config.KyvernoNamespace()), name, finalizer); err != nil {
|
||||
logger.Error(err, "failed to delete finalizer from role", "name", name, "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := DeleteFinalizers(ctx, kubeClient.AppsV1().Deployments(config.KyvernoNamespace()), config.KyvernoDeploymentName(), finalizer); err != nil {
|
||||
logger.Error(err, "failed to delete finalizer from deployment", "name", config.KyvernoDeploymentName(), "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := DeleteFinalizers(ctx, kubeClient.CoreV1().ServiceAccounts(config.KyvernoNamespace()), config.KyvernoServiceAccountName(), finalizer); err != nil {
|
||||
logger.Error(err, "failed to delete finalizer from serviceaccount", "name", config.KyvernoServiceAccountName(), "namespace", config.KyvernoNamespace())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteFinalizers[T metav1.Object](ctx context.Context, client controllerutils.ObjectClient[T], name, finalizer string) error {
|
||||
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
obj, err := client.Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
finalizers := make([]string, 0)
|
||||
for _, f := range obj.GetFinalizers() {
|
||||
if f != finalizer {
|
||||
finalizers = append(finalizers, f)
|
||||
}
|
||||
}
|
||||
|
||||
obj.SetFinalizers(finalizers)
|
||||
_, err = client.Update(ctx, obj, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func AddFinalizers[T metav1.Object](ctx context.Context, client controllerutils.ObjectClient[T], name, finalizer string) error {
|
||||
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
obj, err := client.Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finalizers := obj.GetFinalizers()
|
||||
for _, f := range finalizers {
|
||||
if f == finalizer {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
finalizers = append(finalizers, finalizer)
|
||||
obj.SetFinalizers(finalizers)
|
||||
|
||||
_, err = client.Update(ctx, obj, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -26,6 +26,7 @@ import (
|
|||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
coordinationv1 "k8s.io/api/coordination/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
@ -34,10 +35,12 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
admissionregistrationv1informers "k8s.io/client-go/informers/admissionregistration/v1"
|
||||
appsv1informers "k8s.io/client-go/informers/apps/v1"
|
||||
coordinationv1informers "k8s.io/client-go/informers/coordination/v1"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
rbacv1informers "k8s.io/client-go/informers/rbac/v1"
|
||||
admissionregistrationv1listers "k8s.io/client-go/listers/admissionregistration/v1"
|
||||
appsv1listers "k8s.io/client-go/listers/apps/v1"
|
||||
coordinationv1listers "k8s.io/client-go/listers/coordination/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
rbacv1listers "k8s.io/client-go/listers/rbac/v1"
|
||||
|
@ -91,6 +94,7 @@ type controller struct {
|
|||
vwcLister admissionregistrationv1listers.ValidatingWebhookConfigurationLister
|
||||
cpolLister kyvernov1listers.ClusterPolicyLister
|
||||
polLister kyvernov1listers.PolicyLister
|
||||
deploymentLister appsv1listers.DeploymentLister
|
||||
secretLister corev1listers.SecretLister
|
||||
leaseLister coordinationv1listers.LeaseLister
|
||||
clusterroleLister rbacv1listers.ClusterRoleLister
|
||||
|
@ -100,14 +104,18 @@ type controller struct {
|
|||
queue workqueue.TypedRateLimitingInterface[any]
|
||||
|
||||
// config
|
||||
server string
|
||||
defaultTimeout int32
|
||||
servicePort int32
|
||||
autoUpdateWebhooks bool
|
||||
admissionReports bool
|
||||
runtime runtimeutils.Runtime
|
||||
configuration config.Configuration
|
||||
caSecretName string
|
||||
server string
|
||||
defaultTimeout int32
|
||||
servicePort int32
|
||||
autoUpdateWebhooks bool
|
||||
autoDeleteWebhooks bool
|
||||
admissionReports bool
|
||||
runtime runtimeutils.Runtime
|
||||
configuration config.Configuration
|
||||
caSecretName string
|
||||
webhooksDeleted bool
|
||||
webhookCleanupSetup func(context.Context, logr.Logger) error
|
||||
postWebhookCleanup func(context.Context, logr.Logger) error
|
||||
|
||||
// state
|
||||
lock sync.Mutex
|
||||
|
@ -124,6 +132,7 @@ func NewController(
|
|||
vwcInformer admissionregistrationv1informers.ValidatingWebhookConfigurationInformer,
|
||||
cpolInformer kyvernov1informers.ClusterPolicyInformer,
|
||||
polInformer kyvernov1informers.PolicyInformer,
|
||||
deploymentInformer appsv1informers.DeploymentInformer,
|
||||
secretInformer corev1informers.SecretInformer,
|
||||
leaseInformer coordinationv1informers.LeaseInformer,
|
||||
clusterroleInformer rbacv1informers.ClusterRoleInformer,
|
||||
|
@ -133,35 +142,42 @@ func NewController(
|
|||
servicePort int32,
|
||||
webhookServerPort int32,
|
||||
autoUpdateWebhooks bool,
|
||||
autoDeleteWebhooks bool,
|
||||
admissionReports bool,
|
||||
runtime runtimeutils.Runtime,
|
||||
configuration config.Configuration,
|
||||
caSecretName string,
|
||||
webhookCleanupSetup func(context.Context, logr.Logger) error,
|
||||
postWebhookCleanup func(context.Context, logr.Logger) error,
|
||||
) controllers.Controller {
|
||||
queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any](), ControllerName)
|
||||
c := controller{
|
||||
discoveryClient: discoveryClient,
|
||||
mwcClient: mwcClient,
|
||||
vwcClient: vwcClient,
|
||||
leaseClient: leaseClient,
|
||||
kyvernoClient: kyvernoClient,
|
||||
mwcLister: mwcInformer.Lister(),
|
||||
vwcLister: vwcInformer.Lister(),
|
||||
cpolLister: cpolInformer.Lister(),
|
||||
polLister: polInformer.Lister(),
|
||||
secretLister: secretInformer.Lister(),
|
||||
leaseLister: leaseInformer.Lister(),
|
||||
clusterroleLister: clusterroleInformer.Lister(),
|
||||
gctxentryLister: gctxentryInformer.Lister(),
|
||||
queue: queue,
|
||||
server: server,
|
||||
defaultTimeout: defaultTimeout,
|
||||
servicePort: servicePort,
|
||||
autoUpdateWebhooks: autoUpdateWebhooks,
|
||||
admissionReports: admissionReports,
|
||||
runtime: runtime,
|
||||
configuration: configuration,
|
||||
caSecretName: caSecretName,
|
||||
discoveryClient: discoveryClient,
|
||||
mwcClient: mwcClient,
|
||||
vwcClient: vwcClient,
|
||||
leaseClient: leaseClient,
|
||||
kyvernoClient: kyvernoClient,
|
||||
mwcLister: mwcInformer.Lister(),
|
||||
vwcLister: vwcInformer.Lister(),
|
||||
cpolLister: cpolInformer.Lister(),
|
||||
polLister: polInformer.Lister(),
|
||||
deploymentLister: deploymentInformer.Lister(),
|
||||
secretLister: secretInformer.Lister(),
|
||||
leaseLister: leaseInformer.Lister(),
|
||||
clusterroleLister: clusterroleInformer.Lister(),
|
||||
gctxentryLister: gctxentryInformer.Lister(),
|
||||
queue: queue,
|
||||
server: server,
|
||||
defaultTimeout: defaultTimeout,
|
||||
servicePort: servicePort,
|
||||
autoUpdateWebhooks: autoUpdateWebhooks,
|
||||
autoDeleteWebhooks: autoDeleteWebhooks,
|
||||
admissionReports: admissionReports,
|
||||
runtime: runtime,
|
||||
configuration: configuration,
|
||||
caSecretName: caSecretName,
|
||||
webhookCleanupSetup: webhookCleanupSetup,
|
||||
postWebhookCleanup: postWebhookCleanup,
|
||||
policyState: map[string]sets.Set[string]{
|
||||
config.MutatingWebhookConfigurationName: sets.New[string](),
|
||||
config.ValidatingWebhookConfigurationName: sets.New[string](),
|
||||
|
@ -193,6 +209,25 @@ func NewController(
|
|||
); err != nil {
|
||||
logger.Error(err, "failed to register event handlers")
|
||||
}
|
||||
if autoDeleteWebhooks {
|
||||
if _, err := controllerutils.AddEventHandlersT(
|
||||
deploymentInformer.Informer(),
|
||||
func(obj *appsv1.Deployment) {
|
||||
},
|
||||
func(_, obj *appsv1.Deployment) {
|
||||
if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == config.KyvernoDeploymentName() {
|
||||
c.enqueueCleanupAfter(1 * time.Second)
|
||||
}
|
||||
},
|
||||
func(obj *appsv1.Deployment) {
|
||||
if obj.GetNamespace() == config.KyvernoNamespace() && obj.GetName() == config.KyvernoDeploymentName() {
|
||||
c.enqueueCleanup()
|
||||
}
|
||||
},
|
||||
); err != nil {
|
||||
logger.Error(err, "failed to register event handlers")
|
||||
}
|
||||
}
|
||||
if _, err := controllerutils.AddEventHandlers(
|
||||
cpolInformer.Informer(),
|
||||
func(interface{}) { c.enqueueResourceWebhooks(0) },
|
||||
|
@ -214,6 +249,9 @@ func NewController(
|
|||
}
|
||||
|
||||
func (c *controller) Run(ctx context.Context, workers int) {
|
||||
if err := c.webhookCleanupSetup(ctx, logger); err != nil {
|
||||
logger.Error(err, "failed to setup webhook cleanup")
|
||||
}
|
||||
// add our known webhooks to the queue
|
||||
c.enqueueAll()
|
||||
controllerutils.Run(ctx, logger, ControllerName, time.Second, c.queue, workers, maxRetries, c.reconcile, c.watchdog)
|
||||
|
@ -286,6 +324,14 @@ func (c *controller) enqueueAll() {
|
|||
c.enqueueVerifyWebhook()
|
||||
}
|
||||
|
||||
func (c *controller) enqueueCleanup() {
|
||||
c.queue.Add(config.KyvernoDeploymentName())
|
||||
}
|
||||
|
||||
func (c *controller) enqueueCleanupAfter(duration time.Duration) {
|
||||
c.queue.AddAfter(config.KyvernoDeploymentName(), duration)
|
||||
}
|
||||
|
||||
func (c *controller) enqueuePolicyWebhooks() {
|
||||
c.queue.Add(config.PolicyValidatingWebhookConfigurationName)
|
||||
c.queue.Add(config.PolicyMutatingWebhookConfigurationName)
|
||||
|
@ -363,6 +409,47 @@ func (c *controller) reconcileVerifyMutatingWebhookConfiguration(ctx context.Con
|
|||
return c.reconcileMutatingWebhookConfiguration(ctx, true, c.buildVerifyMutatingWebhookConfiguration)
|
||||
}
|
||||
|
||||
func (c *controller) reconcileWebhookDeletion(ctx context.Context) error {
|
||||
if c.autoUpdateWebhooks {
|
||||
if c.runtime.IsGoingDown() {
|
||||
if c.webhooksDeleted {
|
||||
return nil
|
||||
}
|
||||
c.webhooksDeleted = true
|
||||
if err := c.vwcClient.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{
|
||||
LabelSelector: kyverno.LabelWebhookManagedBy,
|
||||
}); err != nil && !apierrors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to clean up validating webhook configuration", "label", kyverno.LabelWebhookManagedBy)
|
||||
return err
|
||||
} else if err == nil {
|
||||
logger.Info("successfully deleted validating webhook configurations", "label", kyverno.LabelWebhookManagedBy)
|
||||
}
|
||||
if err := c.mwcClient.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{
|
||||
LabelSelector: kyverno.LabelWebhookManagedBy,
|
||||
}); err != nil && !apierrors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to clean up mutating webhook configuration", "label", kyverno.LabelWebhookManagedBy)
|
||||
return err
|
||||
} else if err == nil {
|
||||
logger.Info("successfully deleted mutating webhook configurations", "label", kyverno.LabelWebhookManagedBy)
|
||||
}
|
||||
|
||||
if err := c.postWebhookCleanup(ctx, logger); err != nil {
|
||||
logger.Error(err, "failed to clean up temporary rbac")
|
||||
return err
|
||||
} else {
|
||||
logger.Info("successfully deleted temporary rbac")
|
||||
}
|
||||
} else {
|
||||
if err := c.webhookCleanupSetup(ctx, logger); err != nil {
|
||||
logger.Error(err, "failed to reconcile webhook cleanup setup")
|
||||
return err
|
||||
}
|
||||
logger.Info("reconciled webhook cleanup setup")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) reconcileValidatingWebhookConfiguration(ctx context.Context, autoUpdateWebhooks bool, build func(context.Context, config.Configuration, []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error)) error {
|
||||
caData, err := tls.ReadRootCASecret(c.caSecretName, config.KyvernoNamespace(), c.secretLister.Secrets(config.KyvernoNamespace()))
|
||||
if err != nil {
|
||||
|
@ -526,6 +613,10 @@ func (c *controller) updatePolicyStatuses(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, namespace, name string) error {
|
||||
if c.autoDeleteWebhooks && c.runtime.IsGoingDown() {
|
||||
return c.reconcileWebhookDeletion(ctx)
|
||||
}
|
||||
|
||||
switch name {
|
||||
case config.MutatingWebhookConfigurationName:
|
||||
if c.runtime.IsRollingUpdate() {
|
||||
|
@ -555,6 +646,8 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam
|
|||
return c.reconcilePolicyMutatingWebhookConfiguration(ctx)
|
||||
case config.VerifyMutatingWebhookConfigurationName:
|
||||
return c.reconcileVerifyMutatingWebhookConfiguration(ctx)
|
||||
case config.KyvernoDeploymentName():
|
||||
return c.reconcileWebhookDeletion(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
46
pkg/informers/deployment.go
Normal file
46
pkg/informers/deployment.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package informers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
appsv1informers "k8s.io/client-go/informers/apps/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
appsv1listers "k8s.io/client-go/listers/apps/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
type deploymentInformer struct {
|
||||
informer cache.SharedIndexInformer
|
||||
lister appsv1listers.DeploymentLister
|
||||
}
|
||||
|
||||
func NewDeploymentInformer(
|
||||
client kubernetes.Interface,
|
||||
namespace string,
|
||||
name string,
|
||||
resyncPeriod time.Duration,
|
||||
) appsv1informers.DeploymentInformer {
|
||||
indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
|
||||
options := func(lo *metav1.ListOptions) {
|
||||
lo.FieldSelector = fields.OneTermEqualSelector(metav1.ObjectNameField, name).String()
|
||||
}
|
||||
informer := appsv1informers.NewFilteredDeploymentInformer(
|
||||
client,
|
||||
namespace,
|
||||
resyncPeriod,
|
||||
indexers,
|
||||
options,
|
||||
)
|
||||
lister := appsv1listers.NewDeploymentLister(informer.GetIndexer())
|
||||
return &deploymentInformer{informer, lister}
|
||||
}
|
||||
|
||||
func (i *deploymentInformer) Informer() cache.SharedIndexInformer {
|
||||
return i.informer
|
||||
}
|
||||
|
||||
func (i *deploymentInformer) Lister() appsv1listers.DeploymentLister {
|
||||
return i.lister
|
||||
}
|
|
@ -35,8 +35,9 @@ type ObjectClient[T metav1.Object] interface {
|
|||
CreateClient[T]
|
||||
GetClient[T]
|
||||
UpdateClient[T]
|
||||
DeleteClient
|
||||
PatchClient[T]
|
||||
DeleteClient
|
||||
DeleteCollectionClient
|
||||
}
|
||||
|
||||
type DeleteCollectionClient interface {
|
||||
|
|
|
@ -245,6 +245,8 @@ func (s *server) cleanup(ctx context.Context) {
|
|||
deleteLease := func(name string) {
|
||||
if err := s.leaseClient.Delete(ctx, name, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to clean up lease", "name", name)
|
||||
} else if err == nil {
|
||||
logger.Info("successfully deleted leases", "label", kyverno.LabelWebhookManagedBy)
|
||||
}
|
||||
}
|
||||
deleteVwc := func() {
|
||||
|
@ -252,6 +254,8 @@ func (s *server) cleanup(ctx context.Context) {
|
|||
LabelSelector: kyverno.LabelWebhookManagedBy,
|
||||
}); err != nil && !apierrors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to clean up validating webhook configuration", "label", kyverno.LabelWebhookManagedBy)
|
||||
} else if err == nil {
|
||||
logger.Info("successfully deleted validating webhook configurations", "label", kyverno.LabelWebhookManagedBy)
|
||||
}
|
||||
}
|
||||
deleteMwc := func() {
|
||||
|
@ -259,6 +263,8 @@ func (s *server) cleanup(ctx context.Context) {
|
|||
LabelSelector: kyverno.LabelWebhookManagedBy,
|
||||
}); err != nil && !apierrors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to clean up mutating webhook configuration", "label", kyverno.LabelWebhookManagedBy)
|
||||
} else if err == nil {
|
||||
logger.Info("successfully deleted mutating webhook configurations", "label", kyverno.LabelWebhookManagedBy)
|
||||
}
|
||||
}
|
||||
deleteLease("kyvernopre-lock")
|
||||
|
|
Loading…
Reference in a new issue