1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 02:45:06 +00:00

refactor: use abstract policy interface in webhookconfig (#3466)

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>

Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-03-25 15:43:47 +01:00 committed by GitHub
parent e303dddf86
commit 4efcabffb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 102 deletions

View file

@ -31,6 +31,11 @@ type ClusterPolicy struct {
Status PolicyStatus `json:"status,omitempty" yaml:"status,omitempty"`
}
// GetSpec returns the policy spec
func (p *ClusterPolicy) GetSpec() Spec {
return p.Spec
}
// GetRules returns the policy rules
func (p *ClusterPolicy) GetRules() []Rule {
return p.Spec.GetRules()

View file

@ -32,6 +32,11 @@ type Policy struct {
Status PolicyStatus `json:"status,omitempty" yaml:"status,omitempty"`
}
// GetSpec returns the policy spec
func (p *Policy) GetSpec() Spec {
return p.Spec
}
// GetRules returns the policy rules
func (p *Policy) GetRules() []Rule {
return p.Spec.GetRules()

View file

@ -118,7 +118,7 @@ func GetSupportedControllers(spec *kyverno.Spec, log logr.Logger) []string {
}
// GetRequestedControllers returns the requested autogen controllers based on object annotations.
func GetRequestedControllers(meta metav1.ObjectMeta) []string {
func GetRequestedControllers(meta *metav1.ObjectMeta) []string {
annotations := meta.GetAnnotations()
if annotations == nil {
return nil
@ -135,7 +135,7 @@ func GetRequestedControllers(meta metav1.ObjectMeta) []string {
// GetControllers computes the autogen controllers that should be applied to a policy.
// It returns the requested, supported and effective controllers (intersection of requested and supported ones).
func GetControllers(meta metav1.ObjectMeta, spec *kyverno.Spec, log logr.Logger) ([]string, []string, []string) {
func GetControllers(meta *metav1.ObjectMeta, spec *kyverno.Spec, log logr.Logger) ([]string, []string, []string) {
// compute supported and requested controllers
supported := GetSupportedControllers(spec, log)
requested := GetRequestedControllers(meta)

View file

@ -241,7 +241,7 @@ func Test_GetRequestedControllers(t *testing.T) {
}
for _, test := range testCases {
controllers := GetRequestedControllers(test.meta)
controllers := GetRequestedControllers(&test.meta)
assert.DeepEqual(t, test.expectedControllers, controllers)
}
}

View file

@ -36,6 +36,12 @@ import (
var DefaultWebhookTimeout int64 = 10
// policy abstracts the concrete policy type (Policy vs ClusterPolicy)
type policy interface {
metav1.Object
GetSpec() kyverno.Spec
}
// webhookConfigManager manges the webhook configuration dynamically
// it is NOT multi-thread safe
type webhookConfigManager struct {
@ -139,13 +145,11 @@ func (m *webhookConfigManager) handleErr(err error, key interface{}) {
m.queue.Forget(key)
return
}
if m.queue.NumRequeues(key) < 3 {
logger.Error(err, "failed to sync policy", "key", key)
m.queue.AddRateLimited(key)
return
}
utilruntime.HandleError(err)
logger.V(2).Info("dropping policy out of queue", "key", key)
m.queue.Forget(key)
@ -153,26 +157,22 @@ func (m *webhookConfigManager) handleErr(err error, key interface{}) {
func (m *webhookConfigManager) addClusterPolicy(obj interface{}) {
p := obj.(*kyverno.ClusterPolicy)
if hasWildcard(p) {
if hasWildcard(&p.Spec) {
atomic.AddInt64(&m.wildcardPolicy, int64(1))
}
m.enqueue(p)
}
func (m *webhookConfigManager) updateClusterPolicy(old, cur interface{}) {
oldP := old.(*kyverno.ClusterPolicy)
curP := cur.(*kyverno.ClusterPolicy)
oldP, curP := old.(*kyverno.ClusterPolicy), cur.(*kyverno.ClusterPolicy)
if reflect.DeepEqual(oldP.Spec, curP.Spec) {
return
}
if hasWildcard(oldP) && !hasWildcard(curP) {
if hasWildcard(&oldP.Spec) && !hasWildcard(&curP.Spec) {
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
} else if !hasWildcard(oldP) && hasWildcard(curP) {
} else if !hasWildcard(&oldP.Spec) && hasWildcard(&curP.Spec) {
atomic.AddInt64(&m.wildcardPolicy, int64(1))
}
m.enqueue(curP)
}
@ -191,8 +191,7 @@ func (m *webhookConfigManager) deleteClusterPolicy(obj interface{}) {
}
m.log.V(4).Info("Recovered deleted ClusterPolicy '%s' from tombstone", "name", p.GetName())
}
if hasWildcard(p) {
if hasWildcard(&p.Spec) {
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
}
m.enqueue(p)
@ -200,30 +199,23 @@ func (m *webhookConfigManager) deleteClusterPolicy(obj interface{}) {
func (m *webhookConfigManager) addPolicy(obj interface{}) {
p := obj.(*kyverno.Policy)
if hasWildcard(p) {
if hasWildcard(&p.Spec) {
atomic.AddInt64(&m.wildcardPolicy, int64(1))
}
pol := kyverno.ClusterPolicy(*p)
m.enqueue(&pol)
m.enqueue(p)
}
func (m *webhookConfigManager) updatePolicy(old, cur interface{}) {
oldP := old.(*kyverno.Policy)
curP := cur.(*kyverno.Policy)
oldP, curP := old.(*kyverno.Policy), cur.(*kyverno.Policy)
if reflect.DeepEqual(oldP.Spec, curP.Spec) {
return
}
if hasWildcard(oldP) && !hasWildcard(curP) {
if hasWildcard(&oldP.Spec) && !hasWildcard(&curP.Spec) {
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
} else if !hasWildcard(oldP) && hasWildcard(curP) {
} else if !hasWildcard(&oldP.Spec) && hasWildcard(&curP.Spec) {
atomic.AddInt64(&m.wildcardPolicy, int64(1))
}
pol := kyverno.ClusterPolicy(*curP)
m.enqueue(&pol)
m.enqueue(curP)
}
func (m *webhookConfigManager) deletePolicy(obj interface{}) {
@ -239,15 +231,12 @@ func (m *webhookConfigManager) deletePolicy(obj interface{}) {
utilruntime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type"))
return
}
m.log.V(4).Info("Recovered deleted ClusterPolicy '%s' from tombstone", "name", p.GetName())
m.log.V(4).Info("Recovered deleted Policy '%s/%s' from tombstone", "name", p.GetNamespace(), p.GetName())
}
if hasWildcard(p) {
if hasWildcard(&p.Spec) {
atomic.AddInt64(&m.wildcardPolicy, ^int64(0))
}
pol := kyverno.ClusterPolicy(*p)
m.enqueue(&pol)
m.enqueue(p)
}
func (m *webhookConfigManager) deleteWebhook(obj interface{}) {
@ -263,19 +252,17 @@ func (m *webhookConfigManager) deleteWebhook(obj interface{}) {
func (m *webhookConfigManager) enqueueAllPolicies() {
logger := m.log.WithName("enqueueAllPolicies")
policies, err := m.listAllPolicies()
if err != nil {
logger.Error(err, "unabled to list policies")
}
for _, policy := range policies {
m.enqueue(policy)
logger.V(4).Info("added policy to the queue", "namespace", policy.GetNamespace(), "name", policy.GetName())
}
}
func (m *webhookConfigManager) enqueue(policy *kyverno.ClusterPolicy) {
func (m *webhookConfigManager) enqueue(policy interface{}) {
logger := m.log
key, err := cache.MetaNamespaceKeyFunc(policy)
if err != nil {
@ -330,7 +317,6 @@ func (m *webhookConfigManager) processNextWorkItem() bool {
defer m.queue.Done(key)
err := m.sync(key.(string))
m.handleErr(err, key)
return true
}
@ -341,13 +327,11 @@ func (m *webhookConfigManager) sync(key string) error {
defer func() {
logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime).String())
}()
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
logger.Info("invalid resource key", "key", key)
return nil
}
return m.reconcileWebhook(namespace, name)
}
@ -378,7 +362,7 @@ func (m *webhookConfigManager) reconcileWebhook(namespace, name string) error {
}
}
if err := m.updateStatus(policy, ready); err != nil {
if err := m.updateStatus(namespace, name, ready); err != nil {
return errors.Wrapf(err, "failed to update policy status %s/%s", namespace, name)
}
@ -388,39 +372,30 @@ func (m *webhookConfigManager) reconcileWebhook(namespace, name string) error {
return nil
}
func (m *webhookConfigManager) getPolicy(namespace, name string) (*kyverno.ClusterPolicy, error) {
func (m *webhookConfigManager) getPolicy(namespace, name string) (policy, error) {
if namespace == "" {
return m.pLister.Get(name)
} else {
return m.npLister.Policies(namespace).Get(name)
}
nsPolicy, err := m.npLister.Policies(namespace).Get(name)
if err == nil && nsPolicy != nil {
p := kyverno.ClusterPolicy(*nsPolicy)
return &p, err
}
return nil, err
}
func (m *webhookConfigManager) listAllPolicies() ([]*kyverno.ClusterPolicy, error) {
policies := []*kyverno.ClusterPolicy{}
func (m *webhookConfigManager) listAllPolicies() ([]policy, error) {
policies := []policy{}
polList, err := m.npLister.Policies(metav1.NamespaceAll).List(labels.Everything())
if err != nil {
return nil, errors.Wrapf(err, "failed to list Policy")
}
for _, pol := range polList {
p := kyverno.ClusterPolicy(*pol)
policies = append(policies, &p)
for _, p := range polList {
policies = append(policies, p)
}
cpolList, err := m.pLister.List(labels.Everything())
if err != nil {
return nil, errors.Wrapf(err, "failed to list ClusterPolicy")
}
policies = append(policies, cpolList...)
for _, p := range cpolList {
policies = append(policies, p)
}
return policies, nil
}
@ -463,19 +438,20 @@ func (m *webhookConfigManager) buildWebhooks(namespace string) (res []*webhook,
}
for _, p := range policies {
if p.HasValidate() || p.HasGenerate() {
if p.Spec.FailurePolicy != nil && *p.Spec.FailurePolicy == kyverno.Ignore {
m.mergeWebhook(validateIgnore, p, true)
spec := p.GetSpec()
if spec.HasValidate() || spec.HasGenerate() {
if spec.FailurePolicy != nil && *spec.FailurePolicy == kyverno.Ignore {
m.mergeWebhook(validateIgnore, &spec, true)
} else {
m.mergeWebhook(validateFail, p, true)
m.mergeWebhook(validateFail, &spec, true)
}
}
if p.HasMutate() || p.HasVerifyImages() {
if p.Spec.FailurePolicy != nil && *p.Spec.FailurePolicy == kyverno.Ignore {
m.mergeWebhook(mutateIgnore, p, false)
if spec.HasMutate() || spec.HasVerifyImages() {
if spec.FailurePolicy != nil && *spec.FailurePolicy == kyverno.Ignore {
m.mergeWebhook(mutateIgnore, &spec, false)
} else {
m.mergeWebhook(mutateFail, p, false)
m.mergeWebhook(mutateFail, &spec, false)
}
}
}
@ -722,29 +698,45 @@ func (m *webhookConfigManager) compareAndUpdateWebhook(webhookKind, webhookName
return nil
}
func (m *webhookConfigManager) updateStatus(policy *kyverno.ClusterPolicy, status bool) error {
policyCopy := policy.DeepCopy()
requested, supported, activated := autogen.GetControllers(policy.ObjectMeta, &policy.Spec, m.log)
policyCopy.Status.SetReady(status)
policyCopy.Status.Autogen.Requested = requested
policyCopy.Status.Autogen.Supported = supported
policyCopy.Status.Autogen.Activated = activated
policyCopy.Status.Rules = policy.Spec.Rules
if reflect.DeepEqual(policyCopy.Status, policy.Status) {
return nil
func (m *webhookConfigManager) updateStatus(namespace, name string, ready bool) error {
update := func(meta *metav1.ObjectMeta, spec *kyverno.Spec, status *kyverno.PolicyStatus) bool {
copy := status.DeepCopy()
requested, supported, activated := autogen.GetControllers(meta, spec, m.log)
status.SetReady(ready)
status.Autogen.Requested = requested
status.Autogen.Supported = supported
status.Autogen.Activated = activated
status.Rules = spec.Rules
return !reflect.DeepEqual(status, copy)
}
if policy.GetNamespace() == "" {
_, err := m.kyvernoClient.KyvernoV1().ClusterPolicies().UpdateStatus(context.TODO(), policyCopy, metav1.UpdateOptions{})
return err
if namespace == "" {
p, err := m.pLister.Get(name)
if err != nil {
return err
}
if update(&p.ObjectMeta, &p.Spec, &p.Status) {
if _, err := m.kyvernoClient.KyvernoV1().ClusterPolicies().UpdateStatus(context.TODO(), p, metav1.UpdateOptions{}); err != nil {
return err
}
}
} else {
p, err := m.npLister.Policies(namespace).Get(name)
if err != nil {
return err
}
if update(&p.ObjectMeta, &p.Spec, &p.Status) {
if _, err := m.kyvernoClient.KyvernoV1().Policies(namespace).UpdateStatus(context.TODO(), p, metav1.UpdateOptions{}); err != nil {
return err
}
}
}
_, err := m.kyvernoClient.KyvernoV1().Policies(policyCopy.GetNamespace()).UpdateStatus(context.TODO(), (*kyverno.Policy)(policyCopy), metav1.UpdateOptions{})
return err
return nil
}
// mergeWebhook merges the matching kinds of the policy to webhook.rule
func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy *kyverno.ClusterPolicy, updateValidate bool) {
func (m *webhookConfigManager) mergeWebhook(dst *webhook, spec *kyverno.Spec, updateValidate bool) {
matchedGVK := make([]string, 0)
for _, rule := range policy.GetRules() {
for _, rule := range spec.GetRules() {
// matching kinds in generate policies need to be added to both webhook
if rule.HasGenerate() {
matchedGVK = append(matchedGVK, rule.MatchKinds()...)
@ -826,9 +818,9 @@ func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy *kyverno.Cluste
dst.rule[apiVersions] = removeDuplicates(versions)
dst.rule[resources] = removeDuplicates(rsrcs)
if policy.Spec.WebhookTimeoutSeconds != nil {
if dst.maxWebhookTimeout < int64(*policy.Spec.WebhookTimeoutSeconds) {
dst.maxWebhookTimeout = int64(*policy.Spec.WebhookTimeoutSeconds)
if spec.WebhookTimeoutSeconds != nil {
if dst.maxWebhookTimeout < int64(*spec.WebhookTimeoutSeconds) {
dst.maxWebhookTimeout = int64(*spec.WebhookTimeoutSeconds)
}
}
}
@ -857,20 +849,10 @@ func webhookKey(webhookKind, failurePolicy string) string {
return strings.Join([]string{webhookKind, failurePolicy}, "/")
}
func hasWildcard(policy interface{}) bool {
if p, ok := policy.(*kyverno.ClusterPolicy); ok {
for _, rule := range p.GetRules() {
if kinds := rule.MatchKinds(); utils.ContainsString(kinds, "*") {
return true
}
}
}
if p, ok := policy.(*kyverno.Policy); ok {
for _, rule := range p.GetRules() {
if kinds := rule.MatchKinds(); utils.ContainsString(kinds, "*") {
return true
}
func hasWildcard(spec *kyverno.Spec) bool {
for _, rule := range spec.GetRules() {
if kinds := rule.MatchKinds(); utils.ContainsString(kinds, "*") {
return true
}
}
return false