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

Decouple Action Engine from Rule Type Engine #3599

Merged
merged 4 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions cmd/dev/app/rule_type/rttst.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
serverconfig "github.com/stacklok/minder/internal/config/server"
"github.com/stacklok/minder/internal/db"
"github.com/stacklok/minder/internal/engine"
"github.com/stacklok/minder/internal/engine/actions"
"github.com/stacklok/minder/internal/engine/entities"
"github.com/stacklok/minder/internal/engine/errors"
"github.com/stacklok/minder/internal/engine/eval/rego"
Expand Down Expand Up @@ -170,7 +171,15 @@ func testCmdRun(cmd *cobra.Command, _ []string) error {
}

// TODO: use cobra context here
eng, err := engine.NewRuleTypeEngine(context.Background(), profile, ruletype, prov)
ctx := context.Background()
eng, err := engine.NewRuleTypeEngine(ctx, ruletype, prov)
if err != nil {
return fmt.Errorf("cannot create rule type engine: %w", err)
}
actionEngine, err := actions.NewRuleActions(ctx, profile, ruletype, prov)
if err != nil {
return fmt.Errorf("cannot create rule actions engine: %w", err)
}

inf := &entities.EntityInfoWrapper{
Entity: ent,
Expand All @@ -184,7 +193,7 @@ func testCmdRun(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("no rules found with type %s", ruletype.Name)
}

return runEvaluationForRules(cmd, eng, inf, remediateStatus, remMetadata, rules)
return runEvaluationForRules(cmd, eng, inf, remediateStatus, remMetadata, rules, actionEngine)
}

func runEvaluationForRules(
Expand All @@ -194,6 +203,7 @@ func runEvaluationForRules(
remediateStatus db.NullRemediationStatusTypes,
remMetadata pqtype.NullRawMessage,
frags []*minderv1.Profile_Rule,
actionEngine *actions.RuleActionsEngine,
) error {
for idx := range frags {
frag := frags[idx]
Expand Down Expand Up @@ -227,7 +237,7 @@ func runEvaluationForRules(
evalStatus.SetEvalErr(eng.Eval(ctx, inf, evalStatus))

// Perform the actions, if any
evalStatus.SetActionsErr(ctx, eng.Actions(ctx, inf, evalStatus))
evalStatus.SetActionsErr(ctx, actionEngine.DoActions(ctx, inf.Entity, evalStatus))

if errors.IsActionFatalError(evalStatus.GetActionsErr().RemediateErr) {
cmd.Printf("Remediation failed with fatal error: %s", evalStatus.GetActionsErr().RemediateErr)
Expand Down
38 changes: 23 additions & 15 deletions internal/engine/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/rs/zerolog"

"github.com/stacklok/minder/internal/db"
"github.com/stacklok/minder/internal/engine/actions"
"github.com/stacklok/minder/internal/engine/actions/alert"
"github.com/stacklok/minder/internal/engine/actions/remediate"
"github.com/stacklok/minder/internal/engine/entities"
Expand Down Expand Up @@ -190,7 +191,7 @@ func (e *Executor) evalEntityEvent(ctx context.Context, inf *entities.EntityInfo
// Let's evaluate all the rules for this profile
err = profiles.TraverseRules(relevant, func(rule *pb.Profile_Rule) error {
// Get the engine evaluator for this rule type
evalParams, rte, err := e.getEvaluator(
evalParams, ruleEngine, actions, err := e.getEvaluator(
ctx, inf, provider, profile, rule, hierarchy, ingestCache)
if err != nil {
return err
Expand All @@ -200,12 +201,14 @@ func (e *Executor) evalEntityEvent(ctx context.Context, inf *entities.EntityInfo
defer e.updateLockLease(ctx, *inf.ExecutionID, evalParams)

// Evaluate the rule
evalParams.SetEvalErr(rte.Eval(ctx, inf, evalParams))
evalErr := ruleEngine.Eval(ctx, inf, evalParams)

// Perform actions, if any
evalParams.SetActionsErr(ctx, rte.Actions(ctx, inf, evalParams))
actionsErr := actions.DoActions(ctx, inf.Entity, evalParams)

// Log the evaluation
evalParams.SetEvalErr(evalErr)
evalParams.SetActionsErr(ctx, actionsErr)
logEval(ctx, inf, evalParams)

// Create or update the evaluation status
Expand Down Expand Up @@ -266,11 +269,11 @@ func (e *Executor) getEvaluator(
rule *pb.Profile_Rule,
hierarchy []uuid.UUID,
ingestCache ingestcache.Cache,
) (*engif.EvalStatusParams, *RuleTypeEngine, error) {
) (*engif.EvalStatusParams, *RuleTypeEngine, *actions.RuleActionsEngine, error) {
// Create eval status params
params, err := e.createEvalStatusParams(ctx, inf, profile, rule)
if err != nil {
return nil, nil, fmt.Errorf("error creating eval status params: %w", err)
return nil, nil, nil, fmt.Errorf("error creating eval status params: %w", err)
}

// NOTE: We're only using the first project in the hierarchy for now.
Expand All @@ -286,34 +289,39 @@ func (e *Executor) getEvaluator(
Name: rule.Type,
})
if err != nil {
return nil, nil, fmt.Errorf("error getting rule type when traversing profile %s: %w", params.ProfileID, err)
return nil, nil, nil, fmt.Errorf("error getting rule type when traversing profile %s: %w", params.ProfileID, err)
}

// Parse the rule type
rt, err := ruletypes.RuleTypePBFromDB(&dbrt)
ruleType, err := ruletypes.RuleTypePBFromDB(&dbrt)
if err != nil {
return nil, nil, fmt.Errorf("error parsing rule type when traversing profile %s: %w", params.ProfileID, err)
return nil, nil, nil, fmt.Errorf("error parsing rule type when traversing profile %s: %w", params.ProfileID, err)
}

// Save the rule type uuid
ruleTypeID, err := uuid.Parse(*rt.Id)
ruleTypeID, err := uuid.Parse(*ruleType.Id)
if err != nil {
return nil, nil, fmt.Errorf("error parsing rule type ID: %w", err)
return nil, nil, nil, fmt.Errorf("error parsing rule type ID: %w", err)
}
params.RuleTypeID = ruleTypeID
params.RuleType = rt
params.RuleType = ruleType

// Create the rule type engine
rte, err := NewRuleTypeEngine(ctx, profile, rt, provider)
rte, err := NewRuleTypeEngine(ctx, ruleType, provider)
if err != nil {
return nil, nil, fmt.Errorf("error creating rule type engine: %w", err)
return nil, nil, nil, fmt.Errorf("error creating rule type engine: %w", err)
}

rte = rte.WithIngesterCache(ingestCache)

actionEngine, err := actions.NewRuleActions(ctx, profile, ruleType, provider)
if err != nil {
return nil, nil, nil, fmt.Errorf("cannot create rule actions engine: %w", err)
}

// All okay
params.SetActionsOnOff(rte.GetActionsOnOff())
return params, rte, nil
params.SetActionsOnOff(actionEngine.GetOnOffState())
return params, rte, actionEngine, nil
}

func (e *Executor) updateLockLease(
Expand Down
31 changes: 1 addition & 30 deletions internal/engine/rule_type_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (

"github.com/rs/zerolog"

"github.com/stacklok/minder/internal/engine/actions"
"github.com/stacklok/minder/internal/engine/entities"
enginerr "github.com/stacklok/minder/internal/engine/errors"
"github.com/stacklok/minder/internal/engine/eval"
Expand Down Expand Up @@ -66,22 +65,16 @@ type RuleTypeEngine struct {
// ruleEvaluator is the rule evaluator
ruleEvaluator engif.Evaluator

// actionsEngine is the rule actions engine
actionsEngine *actions.RuleActionsEngine

ruleValidator *profiles.RuleValidator

ruletype *minderv1.RuleType

//provider provinfv1.Provider

ingestCache ingestcache.Cache
}

// NewRuleTypeEngine creates a new rule type engine
func NewRuleTypeEngine(
ctx context.Context,
profile *minderv1.Profile,
ruletype *minderv1.RuleType,
provider provinfv1.Provider,
) (*RuleTypeEngine, error) {
Expand All @@ -100,22 +93,15 @@ func NewRuleTypeEngine(
return nil, fmt.Errorf("cannot create rule evaluator: %w", err)
}

ae, err := actions.NewRuleActions(ctx, profile, ruletype, provider)
if err != nil {
return nil, fmt.Errorf("cannot create rule actions engine: %w", err)
}

rte := &RuleTypeEngine{
Meta: RuleMeta{
Name: ruletype.Name,
},
ruleValidator: rval,
ingester: rdi,
ruleEvaluator: reval,
actionsEngine: ae,
ruletype: ruletype,
//cli: cli,
ingestCache: ingestcache.NewNoopCache(),
ingestCache: ingestcache.NewNoopCache(),
}

if ruletype.Context.Project != nil && *ruletype.Context.Project != "" {
Expand Down Expand Up @@ -191,21 +177,6 @@ func (r *RuleTypeEngine) Eval(
return err
}

// Actions runs all actions for the rule type engine against the given entity
func (r *RuleTypeEngine) Actions(
ctx context.Context,
inf *entities.EntityInfoWrapper,
params engif.ActionsParams,
) enginerr.ActionsError {
// Process actions
return r.actionsEngine.DoActions(ctx, inf.Entity, params)
}

// GetActionsOnOff returns the on/off state of the actions
func (r *RuleTypeEngine) GetActionsOnOff() map[engif.ActionType]engif.ActionOpt {
return r.actionsEngine.GetOnOffState()
}

// GetRulesFromProfileOfType returns the rules from the profile of the given type
func GetRulesFromProfileOfType(p *minderv1.Profile, rt *minderv1.RuleType) ([]*minderv1.Profile_Rule, error) {
contextualRules, err := profiles.GetRulesForEntity(p, minderv1.EntityFromString(rt.Def.InEntity))
Expand Down