-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Critical Bug]unexpectedly deleted when pruneObjectBehavior is None #142
[Critical Bug]unexpectedly deleted when pruneObjectBehavior is None #142
Conversation
666e0b8
to
e61fe12
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than the extra file, I think this looks right! Feel free to ignore my suggestion, I might have gotten a bit carried away...
test/e2e/__debug_bin
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably don't want this
// should compare only object | ||
for _, r := range arr { | ||
func containRelated(related []policyv1.RelatedObject, input policyv1.RelatedObject) bool { | ||
// should compare name, APIVersion, Kind and namepcace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// should compare name, APIVersion, Kind and namepcace | |
// should compare name, APIVersion, Kind and namespace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good
func (r *ConfigurationPolicyReconciler) cleanUpChildObjects(plc policyv1.ConfigurationPolicy, | ||
related []policyv1.RelatedObject, | ||
) []string { | ||
deletionFailures := []string{} | ||
|
||
// When spec is updated and new related objects are created | ||
if len(related) != 0 { | ||
log.Info("preparing to delete detached objects...") | ||
|
||
oldRelated := plc.Status.RelatedObjects | ||
var objShouldRemoved []policyv1.RelatedObject | ||
|
||
for _, oldR := range oldRelated { | ||
exist := containRelated(related, oldR) | ||
|
||
if !exist { | ||
objShouldRemoved = append(objShouldRemoved, oldR) | ||
} | ||
} | ||
|
||
plc.Status.RelatedObjects = objShouldRemoved | ||
} | ||
|
||
for _, object := range plc.Status.RelatedObjects { | ||
// set up client for object deletion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking out loud here in case this is cleaner (it is to me at least)... what do you think of this?
func (r *ConfigurationPolicyReconciler) cleanUpChildObjects(plc policyv1.ConfigurationPolicy,
newRelated []policyv1.RelatedObject,
) []string {
deletionFailures := []string{}
objsToDelete := []policyv1.RelatedObject{}
log.Info("preparing to delete detached objects...")
if len(newRelated) == 0 { // delete all
objsToDelete = plc.Status.RelatedObjects
} else { // When spec is updated and new related objects are created
oldRelated := plc.Status.RelatedObjects
for _, oldR := range oldRelated {
if !containRelated(newRelated, oldR) {
objsToDelete = append(objsToDelete, oldR)
}
}
}
for _, object := range objsToDelete {
// set up client for object deletion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the two cases can just work the same, because if the newRelated
is empty, then containRelated
will just always be false.
func (r *ConfigurationPolicyReconciler) cleanUpChildObjects(plc policyv1.ConfigurationPolicy,
newRelated []policyv1.RelatedObject,
) []string {
deletionFailures := []string{}
objsToDelete := []policyv1.RelatedObject{}
log.Info("preparing to delete detached objects...")
oldRelated := plc.Status.RelatedObjects
for _, oldR := range oldRelated {
if !containRelated(newRelated, oldR) {
objsToDelete = append(objsToDelete, oldR)
}
}
for _, object := range objsToDelete {
// set up client for object deletion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also logic is same but change is variable name right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when newRelated = nil we have to fall in loop, unnecessary isn't it?
e61fe12
to
ca93716
Compare
var objShouldRemoved []policyv1.RelatedObject | ||
|
||
for _, oldR := range objsToDelete { | ||
exist := containRelated(newRelated, oldR) | ||
|
||
if !exist { | ||
objShouldRemoved = append(objShouldRemoved, oldR) | ||
} | ||
} | ||
|
||
objsToDelete = objShouldRemoved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional simplification
var objShouldRemoved []policyv1.RelatedObject | |
for _, oldR := range objsToDelete { | |
exist := containRelated(newRelated, oldR) | |
if !exist { | |
objShouldRemoved = append(objShouldRemoved, oldR) | |
} | |
} | |
objsToDelete = objShouldRemoved | |
objsToDelete = []policyv1.RelatedObject{} | |
for _, oldR := range plc.Status.RelatedObjects { | |
if !containRelated(newRelated, oldR) { | |
objsToDelete = append(objsToDelete, oldR) | |
} | |
} |
// At here, relatedObjects is always [] | ||
failures := r.cleanUpChildObjects(plc, relatedObjects) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is clearer?
// At here, relatedObjects is always [] | |
failures := r.cleanUpChildObjects(plc, relatedObjects) | |
failures := r.cleanUpChildObjects(plc, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good
@@ -3,6 +3,7 @@ kind: ConfigurationPolicy | |||
metadata: | |||
name: tmplt-policy-secret-duplicate | |||
spec: | |||
pruneObjectBehavior: DeleteAll |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pruneObjectBehavior : none
template error always fall to needsDelete = false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is more targeted test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had requested this :)
If this test doesn't have pruneObjectBehavior
set, then the template error definitely shouldn't cause anything to be removed (because now, things are only removed when that is set). The test is meant to answer the question "Will objects be removed when there is a templating error, and I generally want objects to be pruned?" with "No, objects will not be removed".
@@ -571,6 +591,8 @@ func (r *ConfigurationPolicyReconciler) cleanUpChildObjects(plc policyv1.Configu | |||
object.Properties.UID == uid { | |||
needsDelete = true | |||
} | |||
} else { // None or not | |||
needsDelete = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is happening too late in the method since the code above is performing API queries only for the return values to never get used.
My suggestion is to add something to this effect in the start of the method:
if !strings.EqualFold(string(plc.Spec.RemediationAction), "enforce") {
return deletionFailures
}
if !(string(plc.Spec.PruneObjectBehavior) == "DeleteAll" || string(plc.Spec.PruneObjectBehavior) == "DeleteIfCreated") {
return deletionFailures
}
Afterwards, you can also remove the if statement of if strings.EqualFold(string(plc.Spec.RemediationAction), "enforce") {
on line 581 of the diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good but just a performance concern and a few optional improvements.
ca93716
to
26c1934
Compare
26c1934
to
cf76c20
Compare
|
||
// When spec is updated and new related objects are created | ||
if len(newRelated) != 0 { | ||
log.Info("preparing to delete detached objects...") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional:
log.Info("preparing to delete detached objects...") | |
log.Info("Preparing to delete detached objects...") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this log message only be shown if objsToDelete
has entries?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
um.... you right. let me delete it
@@ -767,7 +794,8 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi | |||
if plc.ObjectMeta.DeletionTimestamp != nil { | |||
log.V(1).Info("Config policy has been deleted, handling child objects") | |||
|
|||
failures := r.cleanUpChildObjects(plc) | |||
// At here, relatedObjects is always [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// At here, relatedObjects is always [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor comments
Signed-off-by: Yi Rae Kim <yikim@redhat.com>
cf76c20
to
cc4a248
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/hold for review from Justin
/unohold |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: JustinKuli, mprahl, yiraeChristineKim The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/unhold |
Description of problem:
When a ConfigurationPolicy stops managing an object, it is always deleted regardless of pruneObjectBehavior being set to
None
.Below are a few examples of when a ConfigurationPolicy would stop managing an object:
The ConfigurationPolicy is updated to manage a different object.
The ConfigurationPolicy's namespaceSelector is changed.
The ConfigurationPolicy's templating resolves differently based on changing conditions to no longer manage an object.
Version-Release number of selected component (if applicable):
How reproducible:
Very reproducable
Steps to Reproduce:
Create a ConfigurationPolicy with pruneObjectBehavior set to "None" (default) that creates/updates a ConfigMap called "test"
Update the ConfigurationPolicy to create/update a ConfigMap called "not test"
Actual results:
After following the reproducing steps, the ConfigMap called "test" is deleted.
Expected results:
After following the reproducing steps, the ConfigMap called "test" should not be deleted.
Additional info:
This will cause unexpected data loss to customers.
Ref: https://issues.redhat.com/browse/ACM-5939