-
Notifications
You must be signed in to change notification settings - Fork 72
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
feature(backend): Enabling Analyzer Configuration #2842
feature(backend): Enabling Analyzer Configuration #2842
Conversation
|
||
shouldSkip, reason := linter.ShouldSkip() | ||
shouldSkip := lintResource.ShouldSkip() |
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.
The analyzer resource should make the decisions of running or not the linter as it contains the configuration
Description string `json:"-"` | ||
} | ||
|
||
LinterRule struct { |
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 a new block, allowing users to configure rules
@@ -40,18 +58,130 @@ func (l Linter) Validate() error { | |||
} | |||
|
|||
for _, p := range l.Plugins { | |||
if p.Name == "" { | |||
return fmt.Errorf("plugin name cannot be empty") | |||
plugin, ok := findPlugin(p.Id, DefaultPlugins) |
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.
Validates that the plugins and rules coming from the new resource instance match what we have in the system, this should be more flexible in the future when we implement custom analyzer profiles
return !l.Enabled | ||
} | ||
|
||
func (l Linter) WithMetadata() (Linter, error) { |
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.
Combines the analyzer configuration (Database, CLI, FE) with the metadata stored within the repo, as it is not available for users to update (today)
|
||
var ( | ||
// plugins | ||
StandardsId = "standards" |
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.
Names, ids, descriptions, etc
@@ -0,0 +1,94 @@ | |||
package analyzer |
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.
The "analyzer" per se, doesn't have results, as those belong to the linter, but having them at the linter level creates an import cycle issue because the linter imports model.Trace
for rules and plugins, and at the same time, model.Run
requires the linter.Results
to persist the output from the analyzer.
Once the test migration changes are done, we can come back to this file and move it to the linter level and everyone will be happy.
At least is not in the models
folder anymore 🤷🏾
linter_plugin_security.NewSecurityPlugin(), | ||
linter_plugin_common.NewCommonPlugin(), | ||
} | ||
commonPlugin = plugins.NewPlugin( |
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.
Static registration of the linter configuration, we do not have much flexibility today, but in the future, the linter runner could generate a dynamic plugin registry with custom rules and plugins defined by user profile, test or test run levels
return p.ruleRegistry | ||
} | ||
|
||
func (p BasePlugin) Execute(ctx context.Context, trace model.Trace, config analyzer.LinterPlugin) (analyzer.PluginResult, error) { |
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.
All current plugins have the same execution logic, so instead of having to create a new plugin
file for each, we use the default, and if needed, we can create specific plugin files implementing the Plugin
interface to override execute method with custom logic
return r.id | ||
} | ||
|
||
func (r BaseRule) Evaluate(ctx context.Context, trace model.Trace, config analyzer.LinterRule) (analyzer.RuleResult, error) { |
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.
Rules on the other hand, have very custom logic, which makes it more difficult to encapsulate in a single default method, for these, we should create an implementation of the Rule
interface per rule
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 would say there's no need for this BaseRule
. it can be replaced by a func (r MyRule) ID() string {return someconstantID}
func, satisfying the interface. This also makes the ID immutable, and that's good in this context
@@ -222,6 +222,15 @@ func (m *manager[T]) create(w http.ResponseWriter, r *http.Request) { | |||
targetResource.Spec = m.rh.SetID(targetResource.Spec, m.config.idgen()) | |||
} | |||
|
|||
if err := targetResource.Spec.Validate(); err != 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.
I added this validate step to the resource manager, as it wasn't currently in use, not sure if I should better move this to another PR
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.
The code overall is ok! Good job!
One thing to consider in the future is how we can test these rules to guarantee that they are working as intended and are only sensitive to changes in the future without us knowing it.
Hey @danielbdias thanks for the review, I have added tests for the analyzer and linter critical points, and will add more for rules in the following PRs! |
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 looks good in general. I think we should try to embrace go's naming conventions, so it'd be great to rename all the Id
s to ID
.
I outlined a couple of duplicated loops and bugs, not necessarily related specifically to this work, but since we're here 🤷
Finally, more oriented to the architecutre:
- I would remove the
BaseRule
, it's being used kind of as a base class, but it doesn't make a lot of sense here IMO. - We should try to reduce the amount of interfaces that are not strictly required. "Accept interfaces, return structs". You can leverage go's duck typing and declare interfaces where you want to avoid direct references to a package.
The rest is looking really good. I like the amount of tests that are included here!
Name string `json:"name"` | ||
Enabled bool `json:"enabled"` | ||
Required bool `json:"required"` | ||
Id string `json:"id"` |
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.
Id string `json:"id"` | |
ID string `json:"id"` |
return LinterPlugin{}, false | ||
} | ||
|
||
func findRule(Id string, rules []LinterRule) (LinterRule, bool) { |
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.
func findRule(Id string, rules []LinterRule) (LinterRule, bool) { | |
func findRule(id string, rules []LinterRule) (LinterRule, bool) { |
|
||
var ( | ||
// plugins | ||
StandardsId = "standards" |
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.
StandardsId = "standards" | |
StandardsID = "standards" |
var ( | ||
// plugins | ||
StandardsId = "standards" | ||
CommonId = "common" |
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.
CommonId = "common" | |
CommonID = "common" |
// plugins | ||
StandardsId = "standards" | ||
CommonId = "common" | ||
SecurityId = "security" |
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.
for consistency, and as per Go's recommendation the casing for ID should be ID
and not Id
for _, span := range trace.Flat { | ||
res = append(res, r.validateSpan(span)) | ||
} | ||
|
||
for _, result := range res { | ||
if !result.Passed { | ||
allPassed = 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.
same here
for _, span := range trace.Flat { | |
res = append(res, r.validateSpan(span)) | |
} | |
for _, result := range res { | |
if !result.Passed { | |
allPassed = false | |
} | |
} | |
for _, span := range trace.Flat { | |
analyzerResult := r.validateSpan(span) | |
res = append(res, analyzerResult) | |
if !analyzerResult.Passed { | |
allPassed = false | |
} | |
} |
return r.id | ||
} | ||
|
||
func (r BaseRule) Evaluate(ctx context.Context, trace model.Trace, config analyzer.LinterRule) (analyzer.RuleResult, error) { |
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 would say there's no need for this BaseRule
. it can be replaced by a func (r MyRule) ID() string {return someconstantID}
func, satisfying the interface. This also makes the ID immutable, and that's good in this context
Evaluate(context.Context, model.Trace, analyzer.LinterRule) (analyzer.RuleResult, error) | ||
} | ||
|
||
type RuleRegistry interface { |
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.
Who depends on this interface? can the dependees directly reference a struct? What benefits do we get from this interface?
If a client depends on this interface, it will be requiring this package. Given that we only support one implementation of the RuleRegistry
, and it's in memory without any dependencies, doesn't look like we would need to mock it, for example, or replace it by another implementation.
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.
Makes sense, I think the best case is just to remove the interface
@@ -255,6 +255,15 @@ func (m *manager[T]) doCreate(w http.ResponseWriter, r *http.Request, encoder En | |||
specs = m.rh.SetID(specs, m.config.idgen()) | |||
} | |||
|
|||
if err := specs.Validate(); err != 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.
niceeeee
@@ -288,6 +297,15 @@ func (m *manager[T]) upsert(w http.ResponseWriter, r *http.Request) { | |||
return | |||
} | |||
|
|||
if err := targetResource.Spec.Validate(); err != 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.
this is already handled on doCreate
, and if you move this to happen within the doUpdate
method, we can have both cases covered and remove the need for extra work here
Hey @schoren thanks for the review it was really insightful! I think I addressed all of your comments and updated the code accordingly, let me know if there is something else I could do |
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.
* feature(backend): Enabling Analyzer Configuration (#2842) * feature: Enabling Analyzer Configuration * feature: Enabling Analyzer Configuration * cleanup and improvements * cleanup and improvements * improvements, upgrades and simplification * improvements, upgrades and simplification * adding tests for the analyzer and linter * fixing linter runner * updates and fixes based on PR review comments * removing unnecessary loop * feature(frontend): Analyzer Configuration (#2850) * feature: Enabling Analyzer Configuration * feature: Enabling Analyzer Configuration * cleanup and improvements * cleanup and improvements * improvements, upgrades and simplification * improvements, upgrades and simplification * adding tests for the analyzer and linter * fixing linter runner * feature(frontend): Analyzer Configuration * feature(frontend): Analyzer Configuration * updates and fixes based on PR review comments * clean up changes * fix(frontend): fix analyzer score styles (#2860) * feature(cli): Analyzer Configuration (#2873) * remove file with conflict * fix disabled rule logic --------- Co-authored-by: Jorge Padilla <[email protected]>
This PR updates the architecture for plugins and rules, keeping configuring plugins more straightforward and flexible.
Changes
linter
out of themodels
to thelinter
directoryerror level
andweight
for rulesFixes
Checklist
Analyzer Arch Changes
https://www.loom.com/share/03e95872d383496aa81591be9d772123
Features & bug fixes
https://www.loom.com/share/0517b986f19941f3b41c5187d2344500