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:
parent
e303dddf86
commit
4efcabffb5
5 changed files with 94 additions and 102 deletions
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue