mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
refactor: webhookconfig package (part 3) (#3834)
* refactor: webhookconfig package (part 1) Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com> * refactor: webhook config package (part 2) Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com> * refactor: webhookconfig package (part 3) Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
This commit is contained in:
parent
069d625786
commit
27e7b2d326
4 changed files with 145 additions and 467 deletions
|
@ -13,7 +13,8 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/tls"
|
"github.com/kyverno/kyverno/pkg/tls"
|
||||||
admregapi "k8s.io/api/admissionregistration/v1"
|
admregapi "k8s.io/api/admissionregistration/v1"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/rbac/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
@ -84,22 +85,16 @@ func extractCA(config *rest.Config) (result []byte) {
|
||||||
return config.TLSClientConfig.CAData
|
return config.TLSClientConfig.CAData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wrc *Register) constructOwner() metav1.OwnerReference {
|
func getHealthyPodsIP(pods []corev1.Pod) (ips []string, errs []error) {
|
||||||
logger := wrc.log
|
for _, pod := range pods {
|
||||||
kubeClusterRoleName, err := wrc.GetKubePolicyClusterRoleName()
|
if pod.Status.Phase == "Running" {
|
||||||
if err != nil {
|
ips = append(ips, pod.Status.PodIP)
|
||||||
logger.Error(err, "failed to get cluster role")
|
|
||||||
return metav1.OwnerReference{}
|
|
||||||
}
|
}
|
||||||
return metav1.OwnerReference{
|
|
||||||
APIVersion: config.ClusterRoleAPIVersion,
|
|
||||||
Kind: config.ClusterRoleKind,
|
|
||||||
Name: kubeClusterRoleName.GetName(),
|
|
||||||
UID: kubeClusterRoleName.GetUID(),
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wrc *Register) GetKubePolicyClusterRoleName() (*corev1.ClusterRole, error) {
|
func (wrc *Register) GetKubePolicyClusterRoleName() (*rbacv1.ClusterRole, error) {
|
||||||
selector := &metav1.LabelSelector{
|
selector := &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{
|
MatchLabels: map[string]string{
|
||||||
"app.kubernetes.io/name": "kyverno",
|
"app.kubernetes.io/name": "kyverno",
|
||||||
|
@ -127,6 +122,21 @@ func (wrc *Register) GetKubePolicyDeployment() (*appsv1.Deployment, error) {
|
||||||
return deploy, nil
|
return deploy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wrc *Register) constructOwner() metav1.OwnerReference {
|
||||||
|
logger := wrc.log
|
||||||
|
kubeClusterRoleName, err := wrc.GetKubePolicyClusterRoleName()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err, "failed to get cluster role")
|
||||||
|
return metav1.OwnerReference{}
|
||||||
|
}
|
||||||
|
return metav1.OwnerReference{
|
||||||
|
APIVersion: config.ClusterRoleAPIVersion,
|
||||||
|
Kind: config.ClusterRoleKind,
|
||||||
|
Name: kubeClusterRoleName.GetName(),
|
||||||
|
UID: kubeClusterRoleName.GetUID(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// webhook utils
|
// webhook utils
|
||||||
|
|
||||||
func generateRules(rule admregapi.Rule, operationTypes []admregapi.OperationType) []admregapi.RuleWithOperations {
|
func generateRules(rule admregapi.Rule, operationTypes []admregapi.OperationType) []admregapi.RuleWithOperations {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package webhookconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -15,7 +14,6 @@ import (
|
||||||
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
||||||
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
||||||
kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
|
kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
|
||||||
"github.com/kyverno/kyverno/pkg/common"
|
|
||||||
"github.com/kyverno/kyverno/pkg/config"
|
"github.com/kyverno/kyverno/pkg/config"
|
||||||
client "github.com/kyverno/kyverno/pkg/dclient"
|
client "github.com/kyverno/kyverno/pkg/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/utils"
|
"github.com/kyverno/kyverno/pkg/utils"
|
||||||
|
@ -24,43 +22,44 @@ import (
|
||||||
admregapi "k8s.io/api/admissionregistration/v1"
|
admregapi "k8s.io/api/admissionregistration/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
adminformers "k8s.io/client-go/informers/admissionregistration/v1"
|
adminformers "k8s.io/client-go/informers/admissionregistration/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
admlisters "k8s.io/client-go/listers/admissionregistration/v1"
|
admlisters "k8s.io/client-go/listers/admissionregistration/v1"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DefaultWebhookTimeout int64 = 10
|
var DefaultWebhookTimeout int32 = 10
|
||||||
|
|
||||||
// webhookConfigManager manges the webhook configuration dynamically
|
// webhookConfigManager manges the webhook configuration dynamically
|
||||||
// it is NOT multi-thread safe
|
// it is NOT multi-thread safe
|
||||||
type webhookConfigManager struct {
|
type webhookConfigManager struct {
|
||||||
client client.Interface
|
// clients
|
||||||
|
discoveryClient client.IDiscovery
|
||||||
|
kubeClient kubernetes.Interface
|
||||||
kyvernoClient kyvernoclient.Interface
|
kyvernoClient kyvernoclient.Interface
|
||||||
|
|
||||||
|
// informers
|
||||||
pInformer kyvernoinformer.ClusterPolicyInformer
|
pInformer kyvernoinformer.ClusterPolicyInformer
|
||||||
npInformer kyvernoinformer.PolicyInformer
|
npInformer kyvernoinformer.PolicyInformer
|
||||||
|
|
||||||
// pLister can list/get policy from the shared informer's store
|
|
||||||
pLister kyvernolister.ClusterPolicyLister
|
|
||||||
|
|
||||||
// npLister can list/get namespace policy from the shared informer's store
|
|
||||||
npLister kyvernolister.PolicyLister
|
|
||||||
|
|
||||||
mutateInformer adminformers.MutatingWebhookConfigurationInformer
|
mutateInformer adminformers.MutatingWebhookConfigurationInformer
|
||||||
validateInformer adminformers.ValidatingWebhookConfigurationInformer
|
validateInformer adminformers.ValidatingWebhookConfigurationInformer
|
||||||
|
|
||||||
|
// listers
|
||||||
|
pLister kyvernolister.ClusterPolicyLister
|
||||||
|
npLister kyvernolister.PolicyLister
|
||||||
mutateLister admlisters.MutatingWebhookConfigurationLister
|
mutateLister admlisters.MutatingWebhookConfigurationLister
|
||||||
validateLister admlisters.ValidatingWebhookConfigurationLister
|
validateLister admlisters.ValidatingWebhookConfigurationLister
|
||||||
|
|
||||||
|
// queue
|
||||||
queue workqueue.RateLimitingInterface
|
queue workqueue.RateLimitingInterface
|
||||||
|
|
||||||
// serverIP used to get the name of debug webhooks
|
// serverIP used to get the name of debug webhooks
|
||||||
serverIP string
|
serverIP string
|
||||||
|
|
||||||
autoUpdateWebhooks bool
|
autoUpdateWebhooks bool
|
||||||
|
|
||||||
// wildcardPolicy indicates the number of policies that matches all kinds (*) defined
|
// wildcardPolicy indicates the number of policies that matches all kinds (*) defined
|
||||||
|
@ -78,7 +77,8 @@ type manage interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWebhookConfigManager(
|
func newWebhookConfigManager(
|
||||||
client client.Interface,
|
discoveryClient client.IDiscovery,
|
||||||
|
kubeClient kubernetes.Interface,
|
||||||
kyvernoClient kyvernoclient.Interface,
|
kyvernoClient kyvernoclient.Interface,
|
||||||
pInformer kyvernoinformer.ClusterPolicyInformer,
|
pInformer kyvernoinformer.ClusterPolicyInformer,
|
||||||
npInformer kyvernoinformer.PolicyInformer,
|
npInformer kyvernoinformer.PolicyInformer,
|
||||||
|
@ -91,10 +91,17 @@ func newWebhookConfigManager(
|
||||||
log logr.Logger) manage {
|
log logr.Logger) manage {
|
||||||
|
|
||||||
m := &webhookConfigManager{
|
m := &webhookConfigManager{
|
||||||
client: client,
|
discoveryClient: discoveryClient,
|
||||||
kyvernoClient: kyvernoClient,
|
kyvernoClient: kyvernoClient,
|
||||||
|
kubeClient: kubeClient,
|
||||||
pInformer: pInformer,
|
pInformer: pInformer,
|
||||||
npInformer: npInformer,
|
npInformer: npInformer,
|
||||||
|
mutateInformer: mwcInformer,
|
||||||
|
validateInformer: vwcInformer,
|
||||||
|
pLister: pInformer.Lister(),
|
||||||
|
npLister: npInformer.Lister(),
|
||||||
|
mutateLister: mwcInformer.Lister(),
|
||||||
|
validateLister: vwcInformer.Lister(),
|
||||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "configmanager"),
|
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "configmanager"),
|
||||||
wildcardPolicy: 0,
|
wildcardPolicy: 0,
|
||||||
serverIP: serverIP,
|
serverIP: serverIP,
|
||||||
|
@ -104,13 +111,6 @@ func newWebhookConfigManager(
|
||||||
log: log,
|
log: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
m.pLister = pInformer.Lister()
|
|
||||||
m.npLister = npInformer.Lister()
|
|
||||||
m.mutateInformer = mwcInformer
|
|
||||||
m.mutateLister = mwcInformer.Lister()
|
|
||||||
m.validateInformer = vwcInformer
|
|
||||||
m.validateLister = vwcInformer.Lister()
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,24 +399,6 @@ func (m *webhookConfigManager) listAllPolicies() ([]kyverno.PolicyInterface, err
|
||||||
return policies, nil
|
return policies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
apiGroups string = "apiGroups"
|
|
||||||
apiVersions string = "apiVersions"
|
|
||||||
resources string = "resources"
|
|
||||||
)
|
|
||||||
|
|
||||||
// webhook is the instance that aggregates the GVK of existing policies
|
|
||||||
// based on kind, failurePolicy and webhookTimeout
|
|
||||||
type webhook struct {
|
|
||||||
kind string
|
|
||||||
maxWebhookTimeout int64
|
|
||||||
failurePolicy kyverno.FailurePolicyType
|
|
||||||
|
|
||||||
// rule represents the same rule struct of the webhook using a map object
|
|
||||||
// https://github.com/kubernetes/api/blob/master/admissionregistration/v1/types.go#L25
|
|
||||||
rule map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *webhookConfigManager) buildWebhooks(namespace string) (res []*webhook, err error) {
|
func (m *webhookConfigManager) buildWebhooks(namespace string) (res []*webhook, err error) {
|
||||||
mutateIgnore := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Ignore)
|
mutateIgnore := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Ignore)
|
||||||
mutateFail := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Fail)
|
mutateFail := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Fail)
|
||||||
|
@ -463,19 +445,18 @@ func (m *webhookConfigManager) buildWebhooks(namespace string) (res []*webhook,
|
||||||
func (m *webhookConfigManager) updateWebhookConfig(webhooks []*webhook) error {
|
func (m *webhookConfigManager) updateWebhookConfig(webhooks []*webhook) error {
|
||||||
logger := m.log.WithName("updateWebhookConfig")
|
logger := m.log.WithName("updateWebhookConfig")
|
||||||
|
|
||||||
webhooksMap := make(map[string]interface{}, len(webhooks))
|
webhooksMap := map[string]*webhook{}
|
||||||
for _, w := range webhooks {
|
for _, w := range webhooks {
|
||||||
key := webhookKey(w.kind, string(w.failurePolicy))
|
webhooksMap[webhookKey(w.kind, string(w.failurePolicy))] = w
|
||||||
webhooksMap[key] = w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errs []string
|
var errs []string
|
||||||
if err := m.compareAndUpdateWebhook(kindMutating, getResourceMutatingWebhookConfigName(m.serverIP), webhooksMap); err != nil {
|
if err := m.updateMutatingWebhookConfiguration(getResourceMutatingWebhookConfigName(m.serverIP), webhooksMap); err != nil {
|
||||||
logger.V(4).Info("failed to update mutatingwebhookconfigurations", "error", err.Error())
|
logger.V(4).Info("failed to update mutatingwebhookconfigurations", "error", err.Error())
|
||||||
errs = append(errs, err.Error())
|
errs = append(errs, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := m.compareAndUpdateWebhook(kindValidating, getResourceValidatingWebhookConfigName(m.serverIP), webhooksMap); err != nil {
|
if err := m.updateValidatingWebhookConfiguration(getResourceValidatingWebhookConfigName(m.serverIP), webhooksMap); err != nil {
|
||||||
logger.V(4).Info("failed to update validatingwebhookconfigurations", "error", err.Error())
|
logger.V(4).Info("failed to update validatingwebhookconfigurations", "error", err.Error())
|
||||||
errs = append(errs, err.Error())
|
errs = append(errs, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -487,223 +468,57 @@ func (m *webhookConfigManager) updateWebhookConfig(webhooks []*webhook) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *webhookConfigManager) getWebhook(webhookKind, webhookName string) (resourceWebhook *unstructured.Unstructured, err error) {
|
func (m *webhookConfigManager) updateMutatingWebhookConfiguration(webhookName string, webhooksMap map[string]*webhook) error {
|
||||||
get := func() error {
|
logger := m.log.WithName("updateMutatingWebhookConfiduration").WithValues("name", webhookName)
|
||||||
resourceWebhook = &unstructured.Unstructured{}
|
resourceWebhook, err := m.mutateLister.Get(webhookName)
|
||||||
err = nil
|
|
||||||
|
|
||||||
var rawResc []byte
|
|
||||||
|
|
||||||
switch webhookKind {
|
|
||||||
case kindMutating:
|
|
||||||
resourceWebhookTyped, err := m.mutateLister.Get(webhookName)
|
|
||||||
if err != nil && !apierrors.IsNotFound(err) {
|
if err != nil && !apierrors.IsNotFound(err) {
|
||||||
return errors.Wrapf(err, "unable to get %s/%s", webhookKind, webhookName)
|
return errors.Wrapf(err, "unable to get %s/%s", kindMutating, webhookName)
|
||||||
} else if apierrors.IsNotFound(err) {
|
} else if apierrors.IsNotFound(err) {
|
||||||
m.createDefaultWebhook <- webhookKind
|
m.createDefaultWebhook <- kindMutating
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resourceWebhookTyped.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "admissionregistration.k8s.io/v1", Kind: kindMutating})
|
for i := range resourceWebhook.Webhooks {
|
||||||
rawResc, err = json.Marshal(resourceWebhookTyped)
|
newWebhook := webhooksMap[webhookKey(kindMutating, string(*resourceWebhook.Webhooks[i].FailurePolicy))]
|
||||||
if err != nil {
|
if newWebhook == nil || newWebhook.isEmpty() {
|
||||||
return err
|
resourceWebhook.Webhooks[i].Rules = []admregapi.RuleWithOperations{}
|
||||||
}
|
} else {
|
||||||
case kindValidating:
|
resourceWebhook.Webhooks[i].TimeoutSeconds = &newWebhook.maxWebhookTimeout
|
||||||
resourceWebhookTyped, err := m.validateLister.Get(webhookName)
|
resourceWebhook.Webhooks[i].Rules = []admregapi.RuleWithOperations{
|
||||||
if err != nil && !apierrors.IsNotFound(err) {
|
newWebhook.buildRuleWithOperations(admregapi.Create, admregapi.Update, admregapi.Delete),
|
||||||
return errors.Wrapf(err, "unable to get %s/%s", webhookKind, webhookName)
|
|
||||||
} else if apierrors.IsNotFound(err) {
|
|
||||||
m.createDefaultWebhook <- webhookKind
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resourceWebhookTyped.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "admissionregistration.k8s.io/v1", Kind: kindValidating})
|
|
||||||
rawResc, err = json.Marshal(resourceWebhookTyped)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown webhook kind: must be '%v' or '%v'", kindMutating, kindValidating)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawResc, &resourceWebhook.Object)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := "getWebhook: unable to get webhook configuration"
|
|
||||||
retryGetWebhook := common.RetryFunc(time.Second, 10*time.Second, get, msg, m.log)
|
|
||||||
if err := retryGetWebhook(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return resourceWebhook, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// webhookRulesEqual compares webhook rules between
|
|
||||||
// the representation returned by the API server,
|
|
||||||
// and the internal representation that is generated.
|
|
||||||
//
|
|
||||||
// The two representations are slightly different,
|
|
||||||
// so this function handles those differences.
|
|
||||||
func webhookRulesEqual(apiRules []interface{}, internalRules []interface{}) (bool, error) {
|
|
||||||
// Handle edge case when both are empty.
|
|
||||||
// API representation is a nil slice,
|
|
||||||
// internal representation is one rule
|
|
||||||
// but with no selectors.
|
|
||||||
if len(apiRules) == 0 && len(internalRules) == 1 {
|
|
||||||
if len(internalRules[0].(map[string]interface{})) == 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle edge case when internal is empty but API has one rule.
|
|
||||||
// internal representation is one rule but with no selectors.
|
|
||||||
if len(apiRules) == 1 && len(internalRules) == 1 {
|
|
||||||
if len(internalRules[0].(map[string]interface{})) == 0 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both *should* be length 1, but as long
|
|
||||||
// as they are equal the next loop works.
|
|
||||||
if len(apiRules) != len(internalRules) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range internalRules {
|
|
||||||
internal, ok := internalRules[i].(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return false, errors.New("type conversion of internal rules failed")
|
|
||||||
}
|
|
||||||
api, ok := apiRules[i].(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return false, errors.New("type conversion of API rules failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range over the fields of internal, as the
|
|
||||||
// API rule has extra fields (operations, scope)
|
|
||||||
// that can't be checked on the internal rules.
|
|
||||||
for field := range internal {
|
|
||||||
// Convert the API rules values to []string.
|
|
||||||
apiValues, _, err := unstructured.NestedStringSlice(api, field)
|
|
||||||
if err != nil {
|
|
||||||
return false, errors.Wrapf(err, "error getting string slice for API rules field %s", field)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal type is already []string.
|
|
||||||
internalValues := internal[field]
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(internalValues, apiValues) {
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if _, err := m.kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Update(context.TODO(), resourceWebhook, metav1.UpdateOptions{}); err != nil {
|
||||||
return true, nil
|
return errors.Wrapf(err, "unable to update: %s", resourceWebhook.GetName())
|
||||||
}
|
|
||||||
|
|
||||||
func (m *webhookConfigManager) compareAndUpdateWebhook(webhookKind, webhookName string, webhooksMap map[string]interface{}) error {
|
|
||||||
logger := m.log.WithName("compareAndUpdateWebhook").WithValues("kind", webhookKind, "name", webhookName)
|
|
||||||
resourceWebhook, err := m.getWebhook(webhookKind, webhookName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webhooksUntyped, _, err := unstructured.NestedSlice(resourceWebhook.UnstructuredContent(), "webhooks")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to fetch tag webhooks for %s/%s", webhookKind, webhookName)
|
|
||||||
}
|
|
||||||
|
|
||||||
newWebooks := make([]interface{}, len(webhooksUntyped))
|
|
||||||
copy(newWebooks, webhooksUntyped)
|
|
||||||
var changed bool
|
|
||||||
for i, webhookUntyed := range webhooksUntyped {
|
|
||||||
existingWebhook, ok := webhookUntyed.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
logger.Error(errors.New("type mismatched"), "expected map[string]interface{}, got %T", webhooksUntyped)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
failurePolicy, _, err := unstructured.NestedString(existingWebhook, "failurePolicy")
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(errors.New("type mismatched"), "expected string, got %T", failurePolicy)
|
|
||||||
continue
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
rules, _, err := unstructured.NestedSlice(existingWebhook, "rules")
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err, "type mismatched, expected []interface{}, got %T", rules)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
newWebhook := webhooksMap[webhookKey(webhookKind, failurePolicy)]
|
|
||||||
w, ok := newWebhook.(*webhook)
|
|
||||||
if !ok {
|
|
||||||
logger.Error(errors.New("type mismatched"), "expected *webhook, got %T", newWebooks)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rulesEqual, err := webhookRulesEqual(rules, []interface{}{w.rule})
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err, "failed to compare webhook rules")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rulesEqual {
|
|
||||||
changed = true
|
|
||||||
|
|
||||||
tmpRules, ok := newWebooks[i].(map[string]interface{})["rules"].([]interface{})
|
|
||||||
if !ok {
|
|
||||||
// init operations
|
|
||||||
ops := []string{string(admregapi.Create), string(admregapi.Update), string(admregapi.Delete), string(admregapi.Connect)}
|
|
||||||
if webhookKind == kindMutating {
|
|
||||||
ops = []string{string(admregapi.Create), string(admregapi.Update), string(admregapi.Delete)}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpRules = []interface{}{map[string]interface{}{}}
|
|
||||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), ops, "operations"); err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, apiGroups)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.rule == nil || reflect.DeepEqual(w.rule, map[string]interface{}{}) {
|
|
||||||
// zero kyverno policy with the current failurePolicy, reset webhook rules to empty
|
|
||||||
newWebooks[i].(map[string]interface{})["rules"] = []interface{}{}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), w.rule[apiGroups].([]string), apiGroups); err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, apiGroups)
|
|
||||||
}
|
|
||||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), w.rule[apiVersions].([]string), apiVersions); err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, apiVersions)
|
|
||||||
}
|
|
||||||
if err = unstructured.SetNestedStringSlice(tmpRules[0].(map[string]interface{}), w.rule[resources].([]string), resources); err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to set webhooks[%d].rules[0].%s", i, resources)
|
|
||||||
}
|
|
||||||
|
|
||||||
newWebooks[i].(map[string]interface{})["rules"] = tmpRules
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = unstructured.SetNestedField(newWebooks[i].(map[string]interface{}), w.maxWebhookTimeout, "timeoutSeconds"); err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to set webhooks[%d].timeoutSeconds to %v", i, w.maxWebhookTimeout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if changed {
|
|
||||||
logger.V(4).Info("webhook configuration has been changed, updating")
|
|
||||||
if err := unstructured.SetNestedSlice(resourceWebhook.UnstructuredContent(), newWebooks, "webhooks"); err != nil {
|
|
||||||
return errors.Wrap(err, "unable to set new webhooks")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := m.client.UpdateResource(resourceWebhook.GetAPIVersion(), resourceWebhook.GetKind(), "", resourceWebhook, false); err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to update %s/%s: %s", resourceWebhook.GetAPIVersion(), resourceWebhook.GetKind(), resourceWebhook.GetName())
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.V(4).Info("successfully updated the webhook configuration")
|
logger.V(4).Info("successfully updated the webhook configuration")
|
||||||
}
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *webhookConfigManager) updateValidatingWebhookConfiguration(webhookName string, webhooksMap map[string]*webhook) error {
|
||||||
|
logger := m.log.WithName("updateMutatingWebhookConfiduration").WithValues("name", webhookName)
|
||||||
|
resourceWebhook, err := m.validateLister.Get(webhookName)
|
||||||
|
if err != nil && !apierrors.IsNotFound(err) {
|
||||||
|
return errors.Wrapf(err, "unable to get %s/%s", kindValidating, webhookName)
|
||||||
|
} else if apierrors.IsNotFound(err) {
|
||||||
|
m.createDefaultWebhook <- kindValidating
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range resourceWebhook.Webhooks {
|
||||||
|
newWebhook := webhooksMap[webhookKey(kindValidating, string(*resourceWebhook.Webhooks[i].FailurePolicy))]
|
||||||
|
if newWebhook == nil || newWebhook.isEmpty() {
|
||||||
|
resourceWebhook.Webhooks[i].Rules = []admregapi.RuleWithOperations{}
|
||||||
|
} else {
|
||||||
|
resourceWebhook.Webhooks[i].TimeoutSeconds = &newWebhook.maxWebhookTimeout
|
||||||
|
resourceWebhook.Webhooks[i].Rules = []admregapi.RuleWithOperations{
|
||||||
|
newWebhook.buildRuleWithOperations(admregapi.Create, admregapi.Update, admregapi.Delete, admregapi.Connect),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := m.kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Update(context.TODO(), resourceWebhook, metav1.UpdateOptions{}); err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to update: %s", resourceWebhook.GetName())
|
||||||
|
}
|
||||||
|
logger.V(4).Info("successfully updated the webhook configuration")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,6 +561,32 @@ func (m *webhookConfigManager) updateStatus(namespace, name string, ready bool)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// webhook is the instance that aggregates the GVK of existing policies
|
||||||
|
// based on kind, failurePolicy and webhookTimeout
|
||||||
|
type webhook struct {
|
||||||
|
kind string
|
||||||
|
maxWebhookTimeout int32
|
||||||
|
failurePolicy kyverno.FailurePolicyType
|
||||||
|
groups sets.String
|
||||||
|
versions sets.String
|
||||||
|
resources sets.String
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wh *webhook) buildRuleWithOperations(ops ...admregapi.OperationType) admregapi.RuleWithOperations {
|
||||||
|
return admregapi.RuleWithOperations{
|
||||||
|
Rule: admregapi.Rule{
|
||||||
|
APIGroups: wh.groups.List(),
|
||||||
|
APIVersions: wh.versions.List(),
|
||||||
|
Resources: wh.resources.List(),
|
||||||
|
},
|
||||||
|
Operations: ops,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wh *webhook) isEmpty() bool {
|
||||||
|
return wh.groups.Len() == 0 || wh.versions.Len() == 0 || wh.resources.Len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
// mergeWebhook merges the matching kinds of the policy to webhook.rule
|
// mergeWebhook merges the matching kinds of the policy to webhook.rule
|
||||||
func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy kyverno.PolicyInterface, updateValidate bool) {
|
func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy kyverno.PolicyInterface, updateValidate bool) {
|
||||||
matchedGVK := make([]string, 0)
|
matchedGVK := make([]string, 0)
|
||||||
|
@ -789,7 +630,7 @@ func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy kyverno.PolicyI
|
||||||
case "ServiceProxyOptions":
|
case "ServiceProxyOptions":
|
||||||
gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services/proxy"})
|
gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services/proxy"})
|
||||||
default:
|
default:
|
||||||
_, gvr, err := m.client.Discovery().FindResource(gv, k)
|
_, gvr, err := m.discoveryClient.FindResource(gv, k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.log.Error(err, "unable to convert GVK to GVR", "GVK", gvk)
|
m.log.Error(err, "unable to convert GVK to GVR", "GVK", gvk)
|
||||||
continue
|
continue
|
||||||
|
@ -803,70 +644,35 @@ func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy kyverno.PolicyI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var groups, versions, rsrcs []string
|
|
||||||
if val, ok := dst.rule[apiGroups]; ok {
|
|
||||||
groups = make([]string, len(val.([]string)))
|
|
||||||
copy(groups, val.([]string))
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, ok := dst.rule[apiVersions]; ok {
|
|
||||||
versions = make([]string, len(val.([]string)))
|
|
||||||
copy(versions, val.([]string))
|
|
||||||
}
|
|
||||||
if val, ok := dst.rule[resources]; ok {
|
|
||||||
rsrcs = make([]string, len(val.([]string)))
|
|
||||||
copy(rsrcs, val.([]string))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, gvr := range gvrList {
|
for _, gvr := range gvrList {
|
||||||
groups = append(groups, gvr.Group)
|
dst.groups.Insert(gvr.Group)
|
||||||
versions = append(versions, gvr.Version)
|
dst.versions.Insert(gvr.Version)
|
||||||
rsrcs = append(rsrcs, gvr.Resource)
|
dst.resources.Insert(gvr.Resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.ContainsString(rsrcs, "pods") {
|
if dst.resources.Has("pods") {
|
||||||
rsrcs = append(rsrcs, "pods/ephemeralcontainers")
|
dst.resources.Insert("pods/ephemeralcontainers")
|
||||||
}
|
}
|
||||||
|
if dst.resources.Has("services") {
|
||||||
if utils.ContainsString(rsrcs, "services") {
|
dst.resources.Insert("services/status")
|
||||||
rsrcs = append(rsrcs, "services/status")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(groups) > 0 {
|
|
||||||
dst.rule[apiGroups] = removeDuplicates(groups)
|
|
||||||
}
|
|
||||||
if len(versions) > 0 {
|
|
||||||
dst.rule[apiVersions] = removeDuplicates(versions)
|
|
||||||
}
|
|
||||||
if len(rsrcs) > 0 {
|
|
||||||
dst.rule[resources] = removeDuplicates(rsrcs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spec := policy.GetSpec()
|
spec := policy.GetSpec()
|
||||||
if spec.WebhookTimeoutSeconds != nil {
|
if spec.WebhookTimeoutSeconds != nil {
|
||||||
if dst.maxWebhookTimeout < int64(*spec.WebhookTimeoutSeconds) {
|
if dst.maxWebhookTimeout < *spec.WebhookTimeoutSeconds {
|
||||||
dst.maxWebhookTimeout = int64(*spec.WebhookTimeoutSeconds)
|
dst.maxWebhookTimeout = *spec.WebhookTimeoutSeconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeDuplicates(items []string) (res []string) {
|
func newWebhook(kind string, timeout int32, failurePolicy kyverno.FailurePolicyType) *webhook {
|
||||||
set := make(map[string]int)
|
|
||||||
for _, item := range items {
|
|
||||||
if _, ok := set[item]; !ok {
|
|
||||||
set[item] = 1
|
|
||||||
res = append(res, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWebhook(kind string, timeout int64, failurePolicy kyverno.FailurePolicyType) *webhook {
|
|
||||||
return &webhook{
|
return &webhook{
|
||||||
kind: kind,
|
kind: kind,
|
||||||
maxWebhookTimeout: timeout,
|
maxWebhookTimeout: timeout,
|
||||||
failurePolicy: failurePolicy,
|
failurePolicy: failurePolicy,
|
||||||
rule: make(map[string]interface{}),
|
groups: sets.NewString(),
|
||||||
|
versions: sets.NewString(),
|
||||||
|
resources: sets.NewString(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,7 +690,7 @@ func hasWildcard(spec *kyverno.Spec) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setWildcardConfig(w *webhook) {
|
func setWildcardConfig(w *webhook) {
|
||||||
w.rule[apiGroups] = []string{"*"}
|
w.groups = sets.NewString("*")
|
||||||
w.rule[apiVersions] = []string{"*"}
|
w.versions = sets.NewString("*")
|
||||||
w.rule[resources] = []string{"*/*"}
|
w.resources = sets.NewString("*/*")
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,141 +3,14 @@ package webhookconfig
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func Test_webhook_isEmpty(t *testing.T) {
|
||||||
emptyInternalRules []interface{}
|
empty := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Ignore)
|
||||||
emptyAPIRules []interface{}
|
assert.Equal(t, empty.isEmpty(), true)
|
||||||
|
notEmpty := newWebhook(kindMutating, DefaultWebhookTimeout, kyverno.Ignore)
|
||||||
configmapsInternalRules []interface{}
|
setWildcardConfig(notEmpty)
|
||||||
configmapsAPIRules []interface{}
|
assert.Equal(t, notEmpty.isEmpty(), false)
|
||||||
|
|
||||||
configmapsSecretsInternalRules []interface{}
|
|
||||||
configmapsSecretsAPIRules []interface{}
|
|
||||||
|
|
||||||
secretsConfigmapsInternalRules []interface{}
|
|
||||||
secretsConfigmapsAPIRules []interface{}
|
|
||||||
|
|
||||||
badAPIRules []interface{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// No rules.
|
|
||||||
// Internal representation is a rule with no selectors.
|
|
||||||
// API server representation is no rule (nil).
|
|
||||||
emptyInternalRules = []interface{}{map[string]interface{}{}}
|
|
||||||
emptyAPIRules = nil
|
|
||||||
|
|
||||||
// Rule selecting configmaps.
|
|
||||||
// API server representation matches the internal
|
|
||||||
// representation but has extra fields "operations" and "scope",
|
|
||||||
// and is of interface types instead of strings.
|
|
||||||
configmapsInternalRules = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiGroups": []string{""},
|
|
||||||
"apiVersions": []string{"v1"},
|
|
||||||
"resources": []string{"configmaps"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
configmapsAPIRules = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiGroups": []interface{}{""},
|
|
||||||
"apiVersions": []interface{}{"v1"},
|
|
||||||
"resources": []interface{}{"configmaps"},
|
|
||||||
"operations": []interface{}{"CREATE", "UPDATE", "DELETE", "CONNECT"},
|
|
||||||
"scope": "*",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rule selecting configmaps and secrets.
|
|
||||||
// API server representation matches the internal
|
|
||||||
// representation but has extra fields "operations" and "scope",
|
|
||||||
// and is of interface types instead of strings.
|
|
||||||
configmapsSecretsInternalRules = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiGroups": []string{""},
|
|
||||||
"apiVersions": []string{"v1"},
|
|
||||||
"resources": []string{"configmaps", "secrets"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
configmapsSecretsAPIRules = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiGroups": []interface{}{""},
|
|
||||||
"apiVersions": []interface{}{"v1"},
|
|
||||||
"resources": []interface{}{"configmaps", "secrets"},
|
|
||||||
"operations": []interface{}{"CREATE", "UPDATE", "DELETE", "CONNECT"},
|
|
||||||
"scope": "*",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as previous but reversing the order of configmaps and secrets.
|
|
||||||
secretsConfigmapsInternalRules = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiGroups": []string{""},
|
|
||||||
"apiVersions": []string{"v1"},
|
|
||||||
"resources": []string{"secrets", "configmaps"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
secretsConfigmapsAPIRules = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiGroups": []interface{}{""},
|
|
||||||
"apiVersions": []interface{}{"v1"},
|
|
||||||
"resources": []interface{}{"secrets", "configmaps"},
|
|
||||||
"operations": []interface{}{"CREATE", "UPDATE", "DELETE", "CONNECT"},
|
|
||||||
"scope": "*",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// API rules with missing fields.
|
|
||||||
badAPIRules = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiGroups": []interface{}{""},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRulesEqual(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
internal []interface{}
|
|
||||||
apiserver []interface{}
|
|
||||||
equal bool
|
|
||||||
shouldErr bool
|
|
||||||
}{
|
|
||||||
// Both empty. Should be equal.
|
|
||||||
{"empty-equal", emptyInternalRules, emptyAPIRules, true, false},
|
|
||||||
|
|
||||||
// Both rules select configmaps. Should be equal.
|
|
||||||
{"configmaps-equal", configmapsInternalRules, configmapsAPIRules, true, false},
|
|
||||||
|
|
||||||
// Both rules select configmaps and secrets. Should be equal.
|
|
||||||
{"cm-secrets-equal", configmapsSecretsInternalRules, configmapsSecretsAPIRules, true, false},
|
|
||||||
|
|
||||||
// Both rules select secrets and configmaps (reversed compared to previous). Should be equal.
|
|
||||||
{"secrets-cm-equal", secretsConfigmapsInternalRules, secretsConfigmapsAPIRules, true, false},
|
|
||||||
|
|
||||||
// Internal empty, API has one rule. Not equal.
|
|
||||||
{"internal-empty-api-single", emptyInternalRules, configmapsSecretsAPIRules, false, false},
|
|
||||||
|
|
||||||
// Internal is updated from nothing to configmaps. Not equal.
|
|
||||||
{"add-configmaps", configmapsInternalRules, emptyAPIRules, false, false},
|
|
||||||
|
|
||||||
// Internal is updated from configmaps to configmaps and secrets. Not equal.
|
|
||||||
{"add-secrets", configmapsSecretsInternalRules, configmapsAPIRules, false, false},
|
|
||||||
|
|
||||||
// Order of configmaps and secrets is switched. Not equal.
|
|
||||||
{"order-switched", configmapsSecretsInternalRules, secretsConfigmapsAPIRules, false, false},
|
|
||||||
|
|
||||||
// Malformed API rules, if modified by user or something like that. Not equal.
|
|
||||||
{"bad-api-rules", configmapsInternalRules, badAPIRules, false, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
equal, err := webhookRulesEqual(test.apiserver, test.internal)
|
|
||||||
assert.Equal(t, err != nil, test.shouldErr)
|
|
||||||
assert.Equal(t, equal, test.equal)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/utils"
|
"github.com/kyverno/kyverno/pkg/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
admregapi "k8s.io/api/admissionregistration/v1"
|
admregapi "k8s.io/api/admissionregistration/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
adminformers "k8s.io/client-go/informers/admissionregistration/v1"
|
adminformers "k8s.io/client-go/informers/admissionregistration/v1"
|
||||||
|
@ -97,7 +96,7 @@ func NewRegister(
|
||||||
stopCh: stopCh,
|
stopCh: stopCh,
|
||||||
}
|
}
|
||||||
|
|
||||||
register.manage = newWebhookConfigManager(client, kyvernoClient, pInformer, npInformer, mwcInformer, vwcInformer, serverIP, register.autoUpdateWebhooks, register.createDefaultWebhook, stopCh, log.WithName("WebhookConfigManager"))
|
register.manage = newWebhookConfigManager(client.Discovery(), kubeClient, kyvernoClient, pInformer, npInformer, mwcInformer, vwcInformer, serverIP, register.autoUpdateWebhooks, register.createDefaultWebhook, stopCh, log.WithName("WebhookConfigManager"))
|
||||||
|
|
||||||
return register
|
return register
|
||||||
}
|
}
|
||||||
|
@ -364,16 +363,6 @@ func (wrc *Register) checkEndpoint() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHealthyPodsIP(pods []corev1.Pod) (ips []string, errs []error) {
|
|
||||||
for _, pod := range pods {
|
|
||||||
if pod.Status.Phase != "Running" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ips = append(ips, pod.Status.PodIP)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wrc *Register) updateResourceValidatingWebhookConfiguration(webhookCfg config.WebhookConfig) error {
|
func (wrc *Register) updateResourceValidatingWebhookConfiguration(webhookCfg config.WebhookConfig) error {
|
||||||
resource, err := wrc.vwcLister.Get(getResourceValidatingWebhookConfigName(wrc.serverIP))
|
resource, err := wrc.vwcLister.Get(getResourceValidatingWebhookConfigName(wrc.serverIP))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue