refactor(engine): remove direct db dependency from actions logic#6305
refactor(engine): remove direct db dependency from actions logic#6305sachin9058 wants to merge 1 commit intomindersec:mainfrom
Conversation
|
Open to feedback if a different approach is preferred. |
589de8f to
8e9d9d1
Compare
evankanderson
left a comment
There was a problem hiding this comment.
This looks like it is making good progress. A couple small comments (your LLM is still rewriting a lot of comments it doesn't need to), but this is looking promising.
| var prev *PreviousEval | ||
| if row := params.GetEvalStatusFromDb(); row != nil { | ||
| prev = &PreviousEval{ | ||
| RemediationStatus: RemediationStatus(row.RemStatus), | ||
| AlertStatus: AlertStatus(row.AlertStatus), | ||
| RemediationMeta: &row.RemMetadata, | ||
| AlertMeta: &row.AlertMetadata, | ||
| } | ||
| } |
There was a problem hiding this comment.
Should we construct prev once for both remediate and alert?
| Msg("panic in action execution") | ||
| finalErr = enginerr.ErrInternal | ||
|
|
||
| err = enginerr.ErrInternal // restore behavior |
There was a problem hiding this comment.
I don't think this comment makes sense. LLM leftover?
| // previous remediation state. | ||
| func shouldRemediate(prevEval *PreviousEval, evalStatus EvalStatus) engif.ActionCmd { | ||
| // Determine current evaluation status | ||
| newEval := evalStatus |
| // Determine current evaluation status | ||
| newEval := evalStatus |
There was a problem hiding this comment.
Again, I think we can just rename the evalStatus argument to newEval and avoid this assignment.
| // Case 1 - Successful remediation of a type that is not PR is considered instant. | ||
| // Case: successful remediation (non-PR type) |
There was a problem hiding this comment.
The original comment explained that non-PR remediations were "instant", so they would already be fixed. That's useful context for why this case is what it is.
| // Case 3 - Evaluation changed from something else to ERROR -> Alert should be ON | ||
| // Case 4 - Evaluation has changed from something else to FAILED -> Alert should be ON | ||
| // The Alert should be on (if it wasn't already) | ||
| if db.AlertStatusTypesOn != prevAlert { |
There was a problem hiding this comment.
Can you keep these "Case X" statements? It helps understand the original intent of the code -- unless you feel like you've substantially adjusted the logic here on purpose.
| if err != nil { | ||
| logger.Error().Err(err).Msg("error marshaling empty json.RawMessage") | ||
| logger.Error().Err(err).Msg("error marshaling empty json") | ||
| } |
There was a problem hiding this comment.
We should just replace this with a direct assignment, and avoid the need for error handling.
| } | |
| // Even though meta is an empty json struct by default, there's no risk of overwriting | |
| // any existing meta entry since we don't upsert in case of conflict while skipping | |
| m := json.RawMessage("{}") |
71f4d58 to
24ffab7
Compare
|
@evankanderson Thanks for the feedback !!! really helpful! I’ve gone through and addressed the points you mentioned, including cleaning up some leftover comments, reusing Happy to tweak anything further if needed. |
evankanderson
left a comment
There was a problem hiding this comment.
There's still a lot of comment movement; it makes it somewhat harder to review the diff (since about 25% of it is no-op rewordings or addition of "." to the end of sentences). I can work around it, but it's generally helpful to practice producing minimal diffs when there's another human reviewing the code.
| // Case 2 - Evaluation changed from something else to ERROR -> Remediation should be OFF | ||
| // Case 3 - Evaluation changed from something else to PASSING -> Remediation should be OFF |
There was a problem hiding this comment.
You're still deleting these "Case N" statements. Are you sure that the code is matching the comments? If not, I'd leave in the (conflicting) comments until we figure out whether the code or the comments are correct.
| // Case 4 - Evaluation has changed from something else to FAILED -> Remediation should be ON | ||
| // We should remediate only if the previous remediation was skipped, so we don't risk endless remediation loops |
There was a problem hiding this comment.
And keep these comments, please.
There was a problem hiding this comment.
(Keep this comment about endless remediation loops, too)
|
|
||
| // Case 1 - Successful remediation of a type that is not PR is considered instant. | ||
| if remType != pull_request.RemediateType && remErr == nil { | ||
| // If this is the case either skip alerting or turn it off if it was on |
There was a problem hiding this comment.
Again, I think this comment helps clarify the intent beyond simply what the code does.
There was a problem hiding this comment.
Please keep this comment about skipping alerting or turning it off -- this code is more subtle than I'd like (it tries to interpret past and present to "understand" what edge-change is happening, rather than simply copying status from A to B).
| // Case 5 - Evaluation changed from something else to PASSING -> Alert should be OFF | ||
| // The Alert should be turned OFF (if it wasn't already) |
There was a problem hiding this comment.
Again, keep this comment if you can. If you can keep the indentation the same, it will help GitHub line up the diff correctly (right now, it's anchoring on blank lines stupidly, because it can't figure out which lines are common).
| // If the action is not found, definitely skip it | ||
| logger.Msg("action type not found, skipping") | ||
| zerolog.Ctx(ctx).Info(). | ||
| Str("eval_status", fmt.Sprintf("%v", evalErr)). |
There was a problem hiding this comment.
If evalErr is an error, you can simply call evalErr.Error() to get the string representation.
| logger.Msg("unknown action option, skipping") | ||
| return true | ||
| case models.ActionOptDryRun, models.ActionOptOn: | ||
| // Action is on or dry-run, do not skip yet. Check the evaluation error |
There was a problem hiding this comment.
I think this error helps to explain why this is not simply false or true. Can you leave at least this comment?
There was a problem hiding this comment.
Can we keep some comment about some on and dry-run actions still get skipped because the evaluation requested a "skip" result.
| // getAlertMeta returns alert metadata from previous evaluation. | ||
| func getAlertMeta(prevEval *PreviousEval) *json.RawMessage { | ||
| if prevEval != nil { | ||
| return prevEval.AlertMeta | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Now that PreviousEval is a struct we define here (rather than a DB row), we can define a GetAlertMeta() *json.RawMessage function that will do this even if the pointer is nil, rather than needing a free function:
| // getAlertMeta returns alert metadata from previous evaluation. | |
| func getAlertMeta(prevEval *PreviousEval) *json.RawMessage { | |
| if prevEval != nil { | |
| return prevEval.AlertMeta | |
| } | |
| return nil | |
| } | |
| // getAlertMeta returns alert metadata from previous evaluation. | |
| func (p *PreviousEval) GetAlertMeta() *json.RawMessage { | |
| if p!= nil { | |
| return p.AlertMeta | |
| } | |
| return nil | |
| } |
There was a problem hiding this comment.
It's also not clear to me that PreviousEval needs to be exported, so it might be spelled previousEval...
| @@ -213,7 +223,17 @@ func TestShouldAlert(t *testing.T) { | |||
| if !tt.nilRow { | |||
| prevRow = &db.ListRuleEvaluationsByProfileIdRow{AlertStatus: tt.prevAlert} | |||
| } | |||
| got := shouldAlert(prevRow, tt.evalErr, tt.remErr, tt.remType) | |||
| var prev *PreviousEval | |||
| if prevRow != nil { | |||
| prev = &PreviousEval{ | |||
| AlertStatus: AlertStatus(prevRow.AlertStatus), | |||
| } | |||
| } | |||
There was a problem hiding this comment.
There's no need to have prevRow be a DB type anymore, right? It could just be a *PreviousEval?
| } | |
| var prev *PreviousEval | |
| if !tt.nilRow { | |
| prev = &PreviousEval{ | |
| AlertStatus: AlertStatus(tt.prevAlert), | |
| } | |
| } |
(Some of the rows are outside the suggestion window, so it presents this like a comment on a single line.
f09e0c6 to
af3b673
Compare
|
@evankanderson I've addressed the review comments:
I also rebased the branch on the latest main and resolved conflicts. Please let me know if anything else should be adjusted. |
evankanderson
left a comment
There was a problem hiding this comment.
One lint error, and a bit more return of the comments, and this should be good to go.
| "github.com/rs/zerolog" | ||
| "google.golang.org/protobuf/reflect/protoreflect" | ||
|
|
||
| "strings" |
There was a problem hiding this comment.
"strings" should be in the block with "time" and "fmt", as it's part of the go standard library (and the linter is complaining)
| if prevRemediation != RemediationStatus("skipped") { | ||
| return engif.ActionCmdOff | ||
| } | ||
| // We should do nothing if remediation was already skipped |
There was a problem hiding this comment.
This comment again is helpful explaining why we return "Do Nothing" when the comment says " -> OFF". It might have been better to store the previous state On/Off state, but that's not what's going on here, so it's useful to understand the intents.
| // Case 4 - Evaluation has changed from something else to FAILED -> Remediation should be ON | ||
| // We should remediate only if the previous remediation was skipped, so we don't risk endless remediation loops |
There was a problem hiding this comment.
(Keep this comment about endless remediation loops, too)
|
|
||
| // Case 1 - Successful remediation of a type that is not PR is considered instant. | ||
| if remType != pull_request.RemediateType && remErr == nil { | ||
| // If this is the case either skip alerting or turn it off if it was on |
There was a problem hiding this comment.
Please keep this comment about skipping alerting or turning it off -- this code is more subtle than I'd like (it tries to interpret past and present to "understand" what edge-change is happening, rather than simply copying status from A to B).
| logger.Msg("unknown action option, skipping") | ||
| return true | ||
| case models.ActionOptDryRun, models.ActionOptOn: | ||
| // Action is on or dry-run, do not skip yet. Check the evaluation error |
There was a problem hiding this comment.
Can we keep some comment about some on and dry-run actions still get skipped because the evaluation requested a "skip" result.
af3b673 to
8781cf7
Compare
|
@evankanderson Thanks for the detailed feedback, that clarified the intent a lot. I’ve updated the comments to better reflect the reasoning behind remediation decisions and alert transitions, especially around avoiding repeated remediation and handling state changes correctly. I also addressed the lint issues and rebased on the latest upstream. Happy to make further adjustments if needed. |
evankanderson
left a comment
There was a problem hiding this comment.
You need to revert the changes to entities/properties/service from this PR.
(In general, it's good ta take a look at the actual file diff to make sure that the contents roughly match what you expect the reviewer to be looking at.)
| // handle duplicate key error gracefully (acts like update) | ||
| if strings.Contains(err.Error(), "duplicate key") { |
There was a problem hiding this comment.
Why is this here (why is it part of this PR, at least)?
Secondarily (meaning: if you are opening another PR for this), It looks like UpsertPropertyValueV1 bottoms out in:
INSERT INTO properties (
entity_id,
key,
value,
updated_at
) VALUES ($1, $2, $3, NOW())
ON CONFLICT (entity_id, key) DO UPDATE
SET
value = $4,
updated_at = NOW()
RETURNING id, entity_id, key, value, updated_at
Which should already handle the duplicate key case. If we get an error, it seems like we probably have some other problem that prevented this.
1e2a2a0 to
22c3fbc
Compare
|
@evankanderson It now only contains the actions logic updates along with the new internal types to decouple from database representations. I've also addressed the previous feedback around comments and structure. Happy to refine further if needed. |
Summary
This PR builds on the recently merged #6289 and further simplifies the engine layer by removing remaining direct dependencies on
internal/dbtypes from the actions logic.Previously, the engine layer (
internal/engine/actions) relied on database-specific types such asdb.ListRuleEvaluationsByProfileIdRow, creating tight coupling between business logic and persistence.While #6289 introduced an adapter-based approach for mapping database types, this PR refines the design by:
EvalStatus,PreviousEval)shouldRemediate,shouldAlert) to operate solely on these typesDoActions) instead of relying on adapter mappingsAdditionally, this PR fixes incorrect handling of evaluation states by properly distinguishing between:
This ensures correct action behavior (e.g., avoiding unintended alerts or remediation on generic errors).
Changes
internal/dbtypes from engine logicEvalStatus,PreviousEval)Benefits
No new dependencies are introduced.
Testing
The changes were validated using the following steps:
Build Verification
Linting
Unit Tests
go test ./...Updated
actions_test.goto use engine-level abstractionsVerified correct behavior across:
Manual Verification
Validated that:
All checks pass successfully with no lint issues or test failures.
Fixes #NONE