Skip to content
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

Refactor object template status generation #143

Merged

Conversation

mprahl
Copy link
Member

@mprahl mprahl commented Jun 15, 2023

The code is ready for review but I will need to update some E2E tests.

This refactoring takes away the responsibility of individual methods of updating the status and instead they return one or more status events. The handleObjectTemplates method then generates status events using the entire context of results from the object-template to be able to consolidate messages and include different messages per namespace.

Relates:
https://issues.redhat.com/browse/ACM-5175

@mprahl
Copy link
Member Author

mprahl commented Jun 15, 2023

/hold

@openshift-ci openshift-ci bot requested a review from gparvin June 15, 2023 16:19
@mprahl mprahl force-pushed the refactor-status branch 9 times, most recently from e4b0360 to ff294aa Compare June 20, 2023 17:40
@mprahl mprahl changed the title WIP: Refactor object template status generation Refactor object template status generation Jun 20, 2023
@mprahl
Copy link
Member Author

mprahl commented Jun 20, 2023

/unhold this is ready for review

Copy link
Member

@JustinKuli JustinKuli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes haven't really "clicked" for me yet... I only have some initial comments, I need to look at it again.

Comment on lines 543 to 547
var namesStr string

if len(namespaceToEvent[ns].result.objectNames) > 0 {
namesStr = " ["

for i, name := range namespaceToEvent[ns].result.objectNames {
namesStr += name
if i != len(namespaceToEvent[ns].result.objectNames)-1 {
namesStr += ", "
}
}

namesStr += "]"
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var namesStr string
if len(namespaceToEvent[ns].result.objectNames) > 0 {
namesStr = " ["
for i, name := range namespaceToEvent[ns].result.objectNames {
namesStr += name
if i != len(namespaceToEvent[ns].result.objectNames)-1 {
namesStr += ", "
}
}
namesStr += "]"
}
namesStr := ""
if len(namespaceToEvent[ns].result.objectNames) > 0 {
namesStr = " [" + strings.Join(namespaceToEvent[ns].result.objectNames, ", ") + "]"
}

I know we have this same kind of loop somewhere else, but I think we should use the stdlib for this.

r.Recorder.Event(policy, eventType, fmt.Sprintf(eventFmtStr, policy.GetName(), objDetails.name),
convertPolicyStatusToString(policy))
result = objectResult{
[]string{objName},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when this objName is empty? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It ends up not being used because it's an error case, so it falls in the default switch case. Here is an example of the status:

      conditions:
        - lastTransitionTime: "2023-06-21T12:24:28Z"
          message: namespaced object of kind Role has no namespace specified from the policy namespaceSelector nor the object metadata
          reason: K8s missing namespace
          status: "True"
          type: violation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a unit test for this situation to illustrate this. I hope this helps clarify it.

Comment on lines 1633 to 1662
type objectResult struct {
objectNames []string
namespace string
events []objectResultEvent
}

type objectResultEvent struct {
compliant bool
reason string
message string
}

type objectResultEventWithCtx struct {
result objectResult
event objectResultEvent
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these names are pretty confusing. I don't have anything better though...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I tried renaming them. Let me know if the new names are better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

Comment on lines 1682 to 1686
result = objectResult{
objectNames: []string{obj.name},
namespace: obj.namespace,
events: append(result.events, objectResultEvent{completed, reason, msg}),
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just append to the events here (like 2 lines down) ?

Suggested change
result = objectResult{
objectNames: []string{obj.name},
namespace: obj.namespace,
events: append(result.events, objectResultEvent{completed, reason, msg}),
}
result.events = append(result.events, objectResultEvent{completed, reason, msg})

Comment on lines 1187 to 1188
// Determine the applicable batch. For example, if there are 3 batches but 2 events, we need
// to skip the first batch and append to the second and third batches.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely need more explanation for this comment. Why skip any batches? Why specifically the first?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the comment with a real example. Does this help?

@mprahl mprahl force-pushed the refactor-status branch 6 times, most recently from f8ca607 to 7989040 Compare June 22, 2023 12:06
@mprahl mprahl requested a review from JustinKuli June 22, 2023 13:28
Signed-off-by: mprahl <mprahl@users.noreply.github.com>
@mprahl mprahl requested a review from dhaiducek June 22, 2023 15:15
@@ -205,7 +205,7 @@ install-resources:

.PHONY: e2e-test
e2e-test: e2e-dependencies
$(GINKGO) -v --fail-fast $(E2E_TEST_ARGS) test/e2e
$(GINKGO) -v --timeout=2h --fail-fast $(E2E_TEST_ARGS) test/e2e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this. I fixed flaky e2e test issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once #145 is merged, I'll rebase this PR and remove the increased timeout.

@yiraeChristineKim
Copy link
Contributor

Would you refactor the lines from 88 to 91 in configurationpolicy_utils. currentObject.Object.APIVersion == relatedObject.Object.APIVersion && currentObject.Object.Kind == relatedObject.Object.Kind && currentObject.Object.Metadata.Name == relatedObject.Object.Metadata.Name && currentObject.Object.Metadata.Namespace == relatedObject.Object.Metadata.Namespace. we can just compare the objects, such as relatedObject.Object vs relatedObject.Object


for namespace, eventWithCtx := range namespaceToEvent {
// If a noncompliant event is encountered, then reset the maps to only include noncompliant events.
if compliant && !eventWithCtx.event.compliant {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this part. Amazing logic flow!

// createMergedStatus updates the status field for a configurationpolicy with remediationAction=inform
// based on how many compliant/noncompliant objects are found when processing the templates in the configurationpolicy
// Or multiple namespaces and enforce use this
func createMergedStatus(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lovely remove this part

@mprahl
Copy link
Member Author

mprahl commented Jun 26, 2023

currentObject.Object.APIVersion ==

@yiraeChristineKim I think since that change is unrelated to this PR, I'd prefer it in a separate PR.

This refactoring takes away the responsibility of individual methods of
updating the status and instead they return one or more status events.
The handleObjectTemplates method then generates status events using the
entire context of results from the object-template to be able to
consolidate messages and include different messages per namespace.

Relates:
https://issues.redhat.com/browse/ACM-5175

Signed-off-by: mprahl <mprahl@users.noreply.github.com>
@mprahl
Copy link
Member Author

mprahl commented Jun 26, 2023

/hold for reviews

Copy link
Member

@dhaiducek dhaiducek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple questions about whether passing a pointer is necessary, but overall a great update. The batching pulls together similar actions nicely (though I'll be honest some of the more elaborate batching logic I kind of took your word it worked 😆 )

Comment on lines 1866 to +1868
policy *policyv1.ConfigurationPolicy,
index int,
) (mapping *meta.RESTMapping, updateNeeded bool) {
) (mapping *meta.RESTMapping, result *objectTmplEvalResult) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The policy argument no longer needs to be a pointer

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pointer is nice in that it prevents the policy data from being copied in memory, however, it does mean the function could unintentionally modify the policy.

What do you think @yiraeChristineKim and @JustinKuli?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If our controller is out of memory, I agree to use a pointer. Otherwise, mappingErrResult is only used
line 1159, so I vote for removing Pointer

mappingErrResult != nil {
   			nsToResults[ns] = *mappingErrResult

   			continue
   		}

Copy link
Contributor

@yiraeChristineKim yiraeChristineKim Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no, getmapping(helper) is used only here... let's go with pointer to save memory

@@ -2931,7 +2867,7 @@ func (r *ConfigurationPolicyReconciler) sendComplianceEvent(instance *policyv1.C
}

// convertPolicyStatusToString to be able to pass the status as event
func convertPolicyStatusToString(plc *policyv1.ConfigurationPolicy) (results string) {
func convertPolicyStatusToString(plc *policyv1.ConfigurationPolicy) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'm not sure the plc argument needs to be a pointer?

@openshift-ci
Copy link

openshift-ci bot commented Jun 27, 2023

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dhaiducek, 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:
  • OWNERS [dhaiducek,mprahl,yiraeChristineKim]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@mprahl
Copy link
Member Author

mprahl commented Jun 27, 2023

/unhold

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants