1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

fix(status): status comparison is wrong (#11203)

* fix(status): status comparison is wrong

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* fix(status): retry status update after first failure

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

---------

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>
Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
Khaled Emara 2024-09-24 18:17:38 +03:00 committed by GitHub
parent 2d601a0830
commit 9541608182
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 109 additions and 49 deletions

View file

@ -214,8 +214,9 @@ func convertRule(rule kyvernoRule, kind string) (*kyvernov1.Rule, error) {
}
out := kyvernov1.Rule{
Name: rule.Name,
VerifyImages: rule.VerifyImages,
Name: rule.Name,
VerifyImages: rule.VerifyImages,
SkipBackgroundRequests: rule.SkipBackgroundRequests,
}
if rule.MatchResources != nil {
out.MatchResources = *rule.MatchResources

View file

@ -21,14 +21,15 @@ import (
// https://github.com/kyverno/kyverno/issues/568
type kyvernoRule struct {
Name string `json:"name"`
MatchResources *kyvernov1.MatchResources `json:"match"`
ExcludeResources *kyvernov1.MatchResources `json:"exclude,omitempty"`
Context *[]kyvernov1.ContextEntry `json:"context,omitempty"`
AnyAllConditions *kyvernov1.ConditionsWrapper `json:"preconditions,omitempty"`
Mutation *kyvernov1.Mutation `json:"mutate,omitempty"`
Validation *kyvernov1.Validation `json:"validate,omitempty"`
VerifyImages []kyvernov1.ImageVerification `json:"verifyImages,omitempty"`
Name string `json:"name"`
MatchResources *kyvernov1.MatchResources `json:"match"`
ExcludeResources *kyvernov1.MatchResources `json:"exclude,omitempty"`
Context *[]kyvernov1.ContextEntry `json:"context,omitempty"`
AnyAllConditions *kyvernov1.ConditionsWrapper `json:"preconditions,omitempty"`
Mutation *kyvernov1.Mutation `json:"mutate,omitempty"`
Validation *kyvernov1.Validation `json:"validate,omitempty"`
VerifyImages []kyvernov1.ImageVerification `json:"verifyImages,omitempty"`
SkipBackgroundRequests *bool `json:"skipBackgroundRequests,omitempty"`
}
func createRule(rule *kyvernov1.Rule) *kyvernoRule {
@ -36,8 +37,9 @@ func createRule(rule *kyvernov1.Rule) *kyvernoRule {
return nil
}
jsonFriendlyStruct := kyvernoRule{
Name: rule.Name,
VerifyImages: rule.VerifyImages,
Name: rule.Name,
VerifyImages: rule.VerifyImages,
SkipBackgroundRequests: rule.SkipBackgroundRequests,
}
if !datautils.DeepEqual(rule.MatchResources, kyvernov1.MatchResources{}) {
jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy()
@ -134,9 +136,10 @@ func generateRule(name string, rule *kyvernov1.Rule, tplKey, shift string, kinds
if rule.Validation != nil {
if target := rule.Validation.GetPattern(); target != nil {
newValidate := &kyvernov1.Validation{
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "pattern"),
FailureAction: rule.Validation.FailureAction,
FailureActionOverrides: rule.Validation.FailureActionOverrides,
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "pattern"),
FailureAction: rule.Validation.FailureAction,
FailureActionOverrides: rule.Validation.FailureActionOverrides,
AllowExistingViolations: rule.Validation.AllowExistingViolations,
}
newValidate.SetPattern(
map[string]interface{}{

View file

@ -23,6 +23,7 @@ import (
"github.com/kyverno/kyverno/pkg/controllers"
"github.com/kyverno/kyverno/pkg/tls"
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
@ -45,6 +46,7 @@ import (
corev1listers "k8s.io/client-go/listers/core/v1"
rbacv1listers "k8s.io/client-go/listers/rbac/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/retry"
"k8s.io/client-go/util/workqueue"
)
@ -576,30 +578,74 @@ func (c *controller) updatePolicyStatuses(ctx context.Context) error {
}
for _, policy := range policies {
if policy.GetNamespace() == "" {
_, err = controllerutils.UpdateStatus(
err := controllerutils.UpdateStatus(
ctx,
policy.(*kyvernov1.ClusterPolicy),
c.kyvernoClient.KyvernoV1().ClusterPolicies(),
func(policy *kyvernov1.ClusterPolicy) error {
return updateStatusFunc(policy)
},
func(a *kyvernov1.ClusterPolicy, b *kyvernov1.ClusterPolicy) bool {
return datautils.DeepEqual(a.Status, b.Status)
},
)
if err != nil {
logger.Error(err, "failed to update clusterpolicy status", "policy", policy.GetName())
continue
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
objNew, err := c.kyvernoClient.KyvernoV1().ClusterPolicies().Get(ctx, policy.GetName(), metav1.GetOptions{})
if err != nil {
return err
}
return controllerutils.UpdateStatus(
ctx,
objNew,
c.kyvernoClient.KyvernoV1().ClusterPolicies(),
func(policy *kyvernov1.ClusterPolicy) error {
return updateStatusFunc(policy)
},
func(a *kyvernov1.ClusterPolicy, b *kyvernov1.ClusterPolicy) bool {
return datautils.DeepEqual(a.Status, b.Status)
},
)
})
if retryErr != nil {
logger.Error(err, "failed to update clusterpolicy status", "policy", policy.GetName())
continue
}
}
} else {
_, err = controllerutils.UpdateStatus(
err := controllerutils.UpdateStatus(
ctx,
policy.(*kyvernov1.Policy),
c.kyvernoClient.KyvernoV1().Policies(policy.GetNamespace()),
func(policy *kyvernov1.Policy) error {
return updateStatusFunc(policy)
},
func(a *kyvernov1.Policy, b *kyvernov1.Policy) bool {
return datautils.DeepEqual(a.Status, b.Status)
},
)
if err != nil {
logger.Error(err, "failed to update policy status", "namespace", policy.GetNamespace(), "policy", policy.GetName())
continue
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
objNew, err := c.kyvernoClient.KyvernoV1().Policies(policy.GetNamespace()).Get(ctx, policy.GetName(), metav1.GetOptions{})
if err != nil {
return err
}
return controllerutils.UpdateStatus(
ctx,
objNew,
c.kyvernoClient.KyvernoV1().Policies(policy.GetNamespace()),
func(policy *kyvernov1.Policy) error {
return updateStatusFunc(policy)
},
func(a *kyvernov1.Policy, b *kyvernov1.Policy) bool {
return datautils.DeepEqual(a.Status, b.Status)
},
)
})
if retryErr != nil {
logger.Error(err, "failed to update policy status", "namespace", policy.GetNamespace(), "policy", policy.GetName())
continue
}
}
}
}

View file

@ -73,7 +73,7 @@ func New(
}, err))
if shouldUpdateStatus {
if updateErr := updateStatus(ctx, gce.Name, kyvernoClient, false, entryevent.ReasonAPICallFailure); updateErr != nil {
if updateErr := updateStatus(ctx, gce, kyvernoClient, false, entryevent.ReasonAPICallFailure); updateErr != nil {
logger.Error(updateErr, "failed to update status")
}
}
@ -83,7 +83,7 @@ func New(
logger.V(4).Info("api call success", "data", data)
if shouldUpdateStatus {
if updateErr := updateStatus(ctx, gce.Name, kyvernoClient, true, "APICallSuccess"); updateErr != nil {
if updateErr := updateStatus(ctx, gce, kyvernoClient, true, "APICallSuccess"); updateErr != nil {
logger.Error(updateErr, "failed to update status")
}
}
@ -147,25 +147,23 @@ func doCall(ctx context.Context, caller apicall.Executor, call kyvernov1.APICall
return result, retryError
}
func updateStatus(ctx context.Context, gceName string, kyvernoClient versioned.Interface, ready bool, reason string) error {
func updateStatus(ctx context.Context, gce *kyvernov2alpha1.GlobalContextEntry, kyvernoClient versioned.Interface, ready bool, reason string) error {
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
latestGCE, getErr := kyvernoClient.KyvernoV2alpha1().GlobalContextEntries().Get(ctx, gceName, metav1.GetOptions{})
latestGCE, getErr := kyvernoClient.KyvernoV2alpha1().GlobalContextEntries().Get(ctx, gce.GetName(), metav1.GetOptions{})
if getErr != nil {
return getErr
}
_, updateErr := controllerutils.UpdateStatus(ctx, latestGCE, kyvernoClient.KyvernoV2alpha1().GlobalContextEntries(), func(latest *kyvernov2alpha1.GlobalContextEntry) error {
return controllerutils.UpdateStatus(ctx, latestGCE, kyvernoClient.KyvernoV2alpha1().GlobalContextEntries(), func(latest *kyvernov2alpha1.GlobalContextEntry) error {
if latest == nil {
return fmt.Errorf("failed to update status: %s", latestGCE.Name)
return fmt.Errorf("failed to update status: %s", gce.GetName())
}
latest.Status.SetReady(ready, reason)
if ready {
latest.Status.UpdateRefreshTime()
}
return nil
})
return updateErr
}, nil)
})
return retryErr

View file

@ -19,6 +19,7 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/dynamic/dynamicinformer"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/retry"
)
type entry struct {
@ -135,12 +136,20 @@ func (e *entry) Stop() {
}
func updateStatus(ctx context.Context, gce *kyvernov2alpha1.GlobalContextEntry, kyvernoClient versioned.Interface, ready bool, reason string) error {
_, err := controllerutils.UpdateStatus(ctx, gce, kyvernoClient.KyvernoV2alpha1().GlobalContextEntries(), func(latest *kyvernov2alpha1.GlobalContextEntry) error {
if latest == nil {
return fmt.Errorf("failed to update status: %s", gce.Name)
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
latestGCE, getErr := kyvernoClient.KyvernoV2alpha1().GlobalContextEntries().Get(ctx, gce.GetName(), metav1.GetOptions{})
if getErr != nil {
return getErr
}
latest.Status.SetReady(ready, reason)
return nil
updateErr := controllerutils.UpdateStatus(ctx, latestGCE, kyvernoClient.KyvernoV2alpha1().GlobalContextEntries(), func(latest *kyvernov2alpha1.GlobalContextEntry) error {
if latest == nil {
return fmt.Errorf("failed to update status: %s", gce.GetName())
}
latest.Status.SetReady(ready, reason)
return nil
}, nil)
return updateErr
})
return err
return retryErr
}

View file

@ -150,22 +150,25 @@ func Update[T interface {
func UpdateStatus[T interface {
metav1.Object
DeepCopy[T]
}, S ObjectStatusClient[T]](ctx context.Context, obj T, setter S, build func(T) error,
) (T, error) {
var objNew T
objNew, err := setter.Get(ctx, obj.GetName(), metav1.GetOptions{})
if err != nil {
return objNew, err
}
mutated := objNew.DeepCopy()
}, S ObjectStatusClient[T]](
ctx context.Context,
obj T,
setter S,
build func(T) error,
cmp func(T, T) bool,
) error {
mutated := obj.DeepCopy()
if err := build(mutated); err != nil {
var d T
return d, err
return err
} else {
if datautils.DeepEqual(obj, mutated) {
return mutated, nil
if cmp == nil {
cmp = datautils.DeepEqual[T]
}
if cmp(obj, mutated) {
return nil
} else {
return setter.UpdateStatus(ctx, mutated, metav1.UpdateOptions{})
_, err := setter.UpdateStatus(ctx, mutated, metav1.UpdateOptions{})
return err
}
}
}