-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: assafad <[email protected]>
- Loading branch information
Showing
1,160 changed files
with
580,469 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# monitoring-linter | ||
|
||
Monitoring Linter is a Golang linter designed to ensure that in Kubernetes operator projects, | ||
monitoring-related practices are implemented within the `pkg/monitoring` directory using [operator-observability](https://github.com/machadovilaca/operator-observability/tree/main) methods. | ||
It verifies that all metrics, alerts and recording rules registrations are centralized in this directory. | ||
The use of [Prometheus registration methods](https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#Registerer) is restricted across the entire project. | ||
|
||
## Installation | ||
|
||
```shell | ||
go install github.com/kubevirt/monitoring/monitoringlinter/cmd/monitoringlinter@latest | ||
``` | ||
|
||
## Usage | ||
Once installed, you can run the Monitoring Linter against your Kubernetes operator project by using the following command: | ||
|
||
```shell | ||
monitoringlinter ./... | ||
``` | ||
|
||
|
||
## Linter Rules | ||
|
||
- The following Prometheus registration methods calls are restricted across all the project: | ||
```go | ||
prometheus.Register() | ||
prometheus.MustRegister() | ||
``` | ||
|
||
- The following operator-observability methods calls are allowed only within `pkg/monitoring` directory: | ||
```go | ||
operatormetrics.RegisterMetrics() | ||
operatorrules.RegisterAlerts() | ||
operatorrules.RegisterRecordingRules() | ||
``` | ||
|
||
- Examples for calls that are allowed across all the project, as they are not registering metrics, alerts or recording rules: | ||
```go | ||
prometheus.NewHistogram() | ||
operatormetrics.ListMetrics() | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package monitoringlinter | ||
|
||
import ( | ||
"go/ast" | ||
"path" | ||
"strings" | ||
|
||
"golang.org/x/tools/go/analysis" | ||
) | ||
|
||
const ( | ||
prometheusImportPath = `"github.com/prometheus/client_golang/prometheus"` | ||
operatorMetricsImportPath = `"github.com/machadovilaca/operator-observability/pkg/operatormetrics"` | ||
operatorRulesImportPath = `"github.com/machadovilaca/operator-observability/pkg/operatorrules"` | ||
) | ||
|
||
// NewAnalyzer returns an Analyzer. | ||
func NewAnalyzer() *analysis.Analyzer { | ||
return &analysis.Analyzer{ | ||
Name: "monitoringlinter", | ||
Doc: "Ensures that in Kubernetes operators projects, monitoring related practices are implemented " + | ||
"within pkg/monitoring directory, using operator-observability packages.", | ||
Run: run, | ||
} | ||
} | ||
|
||
// run is the main assertion function. | ||
func run(pass *analysis.Pass) (interface{}, error) { | ||
for _, file := range pass.Files { | ||
prometheusLocalName, prometheusIsImported := getPackageLocalName(file, prometheusImportPath) | ||
operatorMetricsLocalName, OperatorMetricsIsImported := getPackageLocalName(file, operatorMetricsImportPath) | ||
operatorRulesLocalName, operatorRulesIsImported := getPackageLocalName(file, operatorRulesImportPath) | ||
|
||
if !prometheusIsImported && !OperatorMetricsIsImported && !operatorRulesIsImported { | ||
continue // no monitoring related packages are imported => nothing to do in this file; | ||
} | ||
|
||
ast.Inspect(file, func(node ast.Node) bool { | ||
call, ok := node.(*ast.CallExpr) | ||
if !ok { | ||
return true | ||
} | ||
|
||
selectorExpr, ok := call.Fun.(*ast.SelectorExpr) | ||
if !ok { | ||
return true | ||
} | ||
|
||
ident, ok := selectorExpr.X.(*ast.Ident) | ||
if !ok { | ||
return true | ||
} | ||
|
||
methodPackage := ident.Name | ||
methodName := selectorExpr.Sel.Name | ||
|
||
if prometheusIsImported && methodPackage == prometheusLocalName { | ||
checkPrometheusMethodCall(methodName, pass, node) | ||
return true | ||
|
||
} | ||
|
||
if !isMonitoringDir(pass.Fset.File(file.Pos()).Name()) { | ||
if OperatorMetricsIsImported && methodPackage == operatorMetricsLocalName { | ||
checkOperatorMetricsMethodCall(methodName, pass, node) | ||
return true | ||
} | ||
|
||
if operatorRulesIsImported && methodPackage == operatorRulesLocalName { | ||
checkOperatorRulesMethodCall(methodName, pass, node) | ||
return true | ||
} | ||
} | ||
|
||
return true | ||
}) | ||
} | ||
|
||
return nil, nil | ||
} | ||
|
||
// checkPrometheusMethodCall checks if Prometheus method call should be reported. | ||
func checkPrometheusMethodCall(methodName string, pass *analysis.Pass, node ast.Node) { | ||
if methodName == "Register" || methodName == "MustRegister" { | ||
pass.Reportf(node.Pos(), "monitoring-linter: metrics should be registered only within pkg/monitoring directory, "+ | ||
"using operator-observability packages.") | ||
} | ||
} | ||
|
||
// checkOperatorMetricsMethodCall checks if operatormetrics method call should be reported. | ||
func checkOperatorMetricsMethodCall(methodName string, pass *analysis.Pass, node ast.Node) { | ||
if methodName == "RegisterMetrics" { | ||
pass.Reportf(node.Pos(), "monitoring-linter: metrics should be registered only within pkg/monitoring directory.") | ||
} | ||
} | ||
|
||
// checkOperatorRulesMethodCall checks if operatorrules method call should be reported. | ||
func checkOperatorRulesMethodCall(methodName string, pass *analysis.Pass, node ast.Node) { | ||
if methodName == "RegisterAlerts" || methodName == "RegisterRecordingRules" { | ||
pass.Reportf(node.Pos(), "monitoring-linter: alerts and recording rules should be registered only within pkg/monitoring directory.") | ||
} | ||
} | ||
|
||
// getPackageLocalName returns the name a package was imported with in the file. | ||
// e.g. import prom "github.com/prometheus/client_golang/prometheus" | ||
func getPackageLocalName(file *ast.File, importPath string) (string, bool) { | ||
for _, imp := range file.Imports { | ||
if imp.Path.Value == importPath { | ||
if name := imp.Name.String(); name != "<nil>" { | ||
return name, true | ||
} | ||
pathWithoutQuotes := strings.Trim(importPath, `"`) | ||
return path.Base(pathWithoutQuotes), true | ||
} | ||
} | ||
return "", false | ||
} | ||
|
||
func isMonitoringDir(filePath string) bool { | ||
return strings.Contains(filePath, "pkg/monitoring") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package monitoringlinter_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"golang.org/x/tools/go/analysis/analysistest" | ||
|
||
"github.com/monitoring/monitoringlinter" | ||
) | ||
|
||
func TestAllUseCases(t *testing.T) { | ||
for _, testcase := range []struct { | ||
name string | ||
data string | ||
}{ | ||
{ | ||
name: "Verify metrics registrations in pkg/controller, using operatorobservability, is reported.", | ||
data: "a/testrepo/pkg/controller/operatorobservability", | ||
}, | ||
{ | ||
name: "Verify metrics registrations in pkg/controller, using prometheus, is reported.", | ||
data: "a/testrepo/pkg/controller/prometheus", | ||
}, | ||
{ | ||
name: "Verify metrics registrations in pkg/monitoring, using operatorobservability, is not reported.", | ||
data: "a/testrepo/pkg/monitoring/operatorobservability", | ||
}, | ||
{ | ||
name: "Verify metrics registrations in pkg/monitoring, using prometheus, is reported.", | ||
data: "a/testrepo/pkg/monitoring/prometheus", | ||
}, | ||
} { | ||
t.Run(testcase.name, func(tt *testing.T) { | ||
analysistest.Run(tt, analysistest.TestData(), monitoringlinter.NewAnalyzer(), testcase.data) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package main | ||
|
||
import ( | ||
"golang.org/x/tools/go/analysis/singlechecker" | ||
|
||
"github.com/monitoring/monitoringlinter" | ||
) | ||
|
||
func main() { | ||
singlechecker.Main(monitoringlinter.NewAnalyzer()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module github.com/monitoring/monitoringlinter | ||
|
||
go 1.21 | ||
|
||
require golang.org/x/tools v0.18.0 | ||
|
||
require golang.org/x/mod v0.15.0 // indirect |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= | ||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= | ||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= | ||
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
module testrepo | ||
|
||
go 1.21 | ||
|
||
require ( | ||
github.com/machadovilaca/operator-observability v0.0.14 | ||
github.com/prometheus/client_golang v1.18.0 | ||
) | ||
|
||
require ( | ||
github.com/beorn7/perks v1.0.1 // indirect | ||
github.com/cespare/xxhash/v2 v2.2.0 // indirect | ||
github.com/go-logr/logr v1.2.4 // indirect | ||
github.com/gogo/protobuf v1.3.2 // indirect | ||
github.com/google/gofuzz v1.2.0 // indirect | ||
github.com/json-iterator/go v1.1.12 // indirect | ||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
github.com/modern-go/reflect2 v1.0.2 // indirect | ||
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.68.0 // indirect | ||
github.com/prometheus/client_model v0.5.0 // indirect | ||
github.com/prometheus/common v0.45.0 // indirect | ||
github.com/prometheus/procfs v0.12.0 // indirect | ||
golang.org/x/net v0.17.0 // indirect | ||
golang.org/x/sys v0.15.0 // indirect | ||
golang.org/x/text v0.13.0 // indirect | ||
google.golang.org/protobuf v1.31.0 // indirect | ||
gopkg.in/inf.v0 v0.9.1 // indirect | ||
gopkg.in/yaml.v2 v2.4.0 // indirect | ||
k8s.io/api v0.28.1 // indirect | ||
k8s.io/apimachinery v0.28.1 // indirect | ||
k8s.io/klog/v2 v2.100.1 // indirect | ||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect | ||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect | ||
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect | ||
) |
Oops, something went wrong.