Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/tools/agentlint/internal/findcomponents/findcomponents.go
4096 views
1
// Package findcomponents exposes an Analyzer which ensures that created Flow
2
// components are imported by a registry package.
3
package findcomponents
4
5
import (
6
"fmt"
7
"go/ast"
8
"go/types"
9
10
"golang.org/x/tools/go/analysis"
11
"golang.org/x/tools/go/packages"
12
)
13
14
var Analyzer = &analysis.Analyzer{
15
Name: "findcomponents",
16
Doc: "ensure Flow components are imported",
17
Run: run,
18
}
19
20
var (
21
componentPattern = "./component/..."
22
checkPackage = "github.com/grafana/agent/component/all"
23
)
24
25
func init() {
26
Analyzer.Flags.StringVar(&componentPattern, "components", componentPattern, "Pattern where components are defined")
27
Analyzer.Flags.StringVar(&checkPackage, "import-package", checkPackage, "Package that should import components")
28
}
29
30
func run(p *analysis.Pass) (interface{}, error) {
31
// Our linter works as follows:
32
//
33
// 1. Retrieve the list of direct imports of the package we are performing
34
// analysis on.
35
// 2. Find component packages across the module as defined by the -components
36
// flag.
37
// 3. Report a diagnostic for any component package which is not being
38
// imported.
39
//
40
// This linter should only be run against a single package to check for
41
// imports. The import-package flag is checked and all other packages are
42
// ignored.
43
44
if p.Pkg.Path() != checkPackage {
45
return nil, nil
46
}
47
48
imports := make(map[string]struct{})
49
for _, dep := range p.Pkg.Imports() {
50
imports[dep.Path()] = struct{}{}
51
}
52
53
componentPackages, err := findComponentPackages(componentPattern)
54
if err != nil {
55
return nil, err
56
}
57
for componentPackage := range componentPackages {
58
if _, imported := imports[componentPackage]; !imported {
59
p.Report(analysis.Diagnostic{
60
Pos: p.Files[0].Pos(),
61
Message: fmt.Sprintf("package does not import component %s", componentPackage),
62
})
63
}
64
}
65
66
return nil, nil
67
}
68
69
// findComponentPackages returns a map of discovered packages which declare
70
// Flow components. The pattern argument controls the full list of patterns
71
// which are searched (e.g., "./..." or "./component/...").
72
func findComponentPackages(pattern string) (map[string]struct{}, error) {
73
pkgs, err := packages.Load(&packages.Config{
74
Mode: packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo,
75
}, "pattern="+pattern)
76
if err != nil {
77
return nil, err
78
}
79
80
componentPackages := map[string]struct{}{}
81
82
for _, pkg := range pkgs {
83
for _, file := range pkg.Syntax {
84
if declaresComponent(pkg, file) {
85
componentPackages[pkg.ID] = struct{}{}
86
}
87
}
88
}
89
90
return componentPackages, nil
91
}
92
93
// declaresComponent inspects a file to see if it has something matching the
94
// following:
95
//
96
// func init() {
97
// component.Register(component.Registration{ ... })
98
// }
99
func declaresComponent(pkg *packages.Package, file *ast.File) bool {
100
// Look for an init function in the file.
101
for _, decl := range file.Decls {
102
funcDecl, ok := decl.(*ast.FuncDecl)
103
if !ok {
104
continue
105
}
106
107
if funcDecl.Name.Name != "init" || funcDecl.Recv != nil {
108
continue
109
}
110
111
var foundComponentDecl bool
112
113
// Given an init function, check to see if there's a function call to
114
// component.Register.
115
ast.Inspect(funcDecl.Body, func(n ast.Node) bool {
116
call, ok := n.(*ast.CallExpr)
117
if !ok {
118
return true
119
}
120
121
sel, ok := call.Fun.(*ast.SelectorExpr)
122
if !ok {
123
return true
124
}
125
126
ident, ok := sel.X.(*ast.Ident)
127
if !ok {
128
return true
129
}
130
131
// Check to see if the ident refers to
132
// github.com/grafana/agent/component.
133
if pkgName, ok := pkg.TypesInfo.Uses[ident].(*types.PkgName); ok {
134
if pkgName.Imported().Path() == "github.com/grafana/agent/component" &&
135
sel.Sel.Name == "Register" {
136
137
foundComponentDecl = true
138
return false
139
}
140
}
141
142
return true
143
})
144
145
if foundComponentDecl {
146
return true
147
}
148
}
149
150
return false
151
}
152
153