Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
345
tools/custom_builder/packages.go
Normal file
345
tools/custom_builder/packages.go
Normal file
|
@ -0,0 +1,345 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/influxdata/telegraf/filter"
|
||||
)
|
||||
|
||||
// Define the categories we can handle and package filters
|
||||
var packageFilter = filter.MustCompile([]string{
|
||||
"*/all",
|
||||
"*/*_test",
|
||||
"inputs/example",
|
||||
"inputs/main",
|
||||
})
|
||||
|
||||
type packageInfo struct {
|
||||
Category string
|
||||
Plugin string
|
||||
Path string
|
||||
Tag string
|
||||
DefaultParser string
|
||||
DefaultSerializer string
|
||||
}
|
||||
|
||||
type packageCollection struct {
|
||||
root string
|
||||
packages map[string][]packageInfo
|
||||
}
|
||||
|
||||
// Define the package exceptions
|
||||
var exceptions = map[string][]packageInfo{
|
||||
"parsers": {
|
||||
{
|
||||
Category: "parsers",
|
||||
Plugin: "influx_upstream",
|
||||
Path: "plugins/parsers/influx/influx_upstream",
|
||||
Tag: "parsers.influx",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (p *packageCollection) collectPackagesForCategory(category string) error {
|
||||
var entries []packageInfo
|
||||
pluginDir := filepath.Join(p.root, "plugins", category)
|
||||
|
||||
// Add exceptional packages if any
|
||||
if pkgs, found := exceptions[category]; found {
|
||||
entries = append(entries, pkgs...)
|
||||
}
|
||||
|
||||
// Walk the directory and get the packages
|
||||
elements, err := os.ReadDir(pluginDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, element := range elements {
|
||||
path := filepath.Join(pluginDir, element.Name())
|
||||
if !element.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
var fset token.FileSet
|
||||
pkgs, err := parser.ParseDir(&fset, path, sourceFileFilter, parser.ParseComments)
|
||||
if err != nil {
|
||||
log.Printf("parsing directory %q failed: %v", path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for name, pkg := range pkgs {
|
||||
if packageFilter.Match(category + "/" + name) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract the names of the plugins registered by this package
|
||||
registeredNames := extractRegisteredNames(pkg, category)
|
||||
if len(registeredNames) == 0 {
|
||||
log.Printf("WARN: Could not extract information from package %q", name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract potential default parsers for input and processor packages
|
||||
// as well as serializers for the output package
|
||||
var defaultParser, defaultSerializer string
|
||||
switch category {
|
||||
case "inputs":
|
||||
dataformat, err := extractDefaultDataFormat(path)
|
||||
if err != nil {
|
||||
log.Printf("Getting default data-format for %s.%s failed: %v", category, name, err)
|
||||
}
|
||||
defaultParser = dataformat
|
||||
case "processors":
|
||||
dataformat, err := extractDefaultDataFormat(path)
|
||||
if err != nil {
|
||||
log.Printf("Getting default data-format for %s.%s failed: %v", category, name, err)
|
||||
}
|
||||
defaultParser = dataformat
|
||||
// The execd processor requires both a parser and serializer
|
||||
if name == "execd" {
|
||||
defaultSerializer = dataformat
|
||||
}
|
||||
case "outputs":
|
||||
dataformat, err := extractDefaultDataFormat(path)
|
||||
if err != nil {
|
||||
log.Printf("Getting default data-format for %s.%s failed: %v", category, name, err)
|
||||
}
|
||||
defaultSerializer = dataformat
|
||||
}
|
||||
|
||||
for _, plugin := range registeredNames {
|
||||
path := filepath.Join("plugins", category, element.Name())
|
||||
tag := category + "." + element.Name()
|
||||
entries = append(entries, packageInfo{
|
||||
Category: category,
|
||||
Plugin: plugin,
|
||||
Path: filepath.ToSlash(path),
|
||||
Tag: tag,
|
||||
DefaultParser: defaultParser,
|
||||
DefaultSerializer: defaultSerializer,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
p.packages[category] = entries
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *packageCollection) CollectAvailable() error {
|
||||
p.packages = make(map[string][]packageInfo)
|
||||
|
||||
for _, category := range categories {
|
||||
if err := p.collectPackagesForCategory(category); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *packageCollection) ExtractTags() []string {
|
||||
var tags []string
|
||||
for category, pkgs := range p.packages {
|
||||
_ = category
|
||||
for _, pkg := range pkgs {
|
||||
tags = append(tags, pkg.Tag)
|
||||
}
|
||||
}
|
||||
sort.Strings(tags)
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
func (p *packageCollection) Print() {
|
||||
fmt.Println("-------------------------------------------------------------------------------")
|
||||
fmt.Println("Enabled plugins:")
|
||||
fmt.Println("-------------------------------------------------------------------------------")
|
||||
for _, category := range categories {
|
||||
pkgs := p.packages[category]
|
||||
sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Plugin < pkgs[j].Plugin })
|
||||
|
||||
fmt.Printf("%s (%d):\n", category, len(pkgs))
|
||||
for _, pkg := range pkgs {
|
||||
fmt.Printf(" %-30s %s\n", pkg.Plugin, pkg.Path)
|
||||
}
|
||||
fmt.Println("-------------------------------------------------------------------------------")
|
||||
}
|
||||
}
|
||||
|
||||
func sourceFileFilter(d fs.FileInfo) bool {
|
||||
return strings.HasSuffix(d.Name(), ".go") && !strings.HasSuffix(d.Name(), "_test.go")
|
||||
}
|
||||
|
||||
func findFunctionDecl(file *ast.File, name string) *ast.FuncDecl {
|
||||
for _, decl := range file.Decls {
|
||||
d, ok := decl.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if d.Name.Name == name && d.Recv == nil {
|
||||
return d
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findAddStatements(decl *ast.FuncDecl, pluginType string) []*ast.CallExpr {
|
||||
var statements []*ast.CallExpr
|
||||
for _, stmt := range decl.Body.List {
|
||||
s, ok := stmt.(*ast.ExprStmt)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
call, ok := s.X.(*ast.CallExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
fun, ok := call.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
e, ok := fun.X.(*ast.Ident)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if e.Name == pluginType && (fun.Sel.Name == "Add" || fun.Sel.Name == "AddStreaming") {
|
||||
statements = append(statements, call)
|
||||
}
|
||||
}
|
||||
|
||||
return statements
|
||||
}
|
||||
|
||||
func extractPluginInfo(file *ast.File, pluginType string, declarations map[string]string) ([]string, error) {
|
||||
var registeredNames []string
|
||||
|
||||
decl := findFunctionDecl(file, "init")
|
||||
if decl == nil {
|
||||
return nil, nil
|
||||
}
|
||||
calls := findAddStatements(decl, pluginType)
|
||||
if len(calls) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
for _, call := range calls {
|
||||
switch arg := call.Args[0].(type) {
|
||||
case *ast.Ident:
|
||||
resval, found := declarations[arg.Name]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("cannot resolve registered name variable %q", arg.Name)
|
||||
}
|
||||
registeredNames = append(registeredNames, strings.Trim(resval, "\""))
|
||||
case *ast.BasicLit:
|
||||
if arg.Kind != token.STRING {
|
||||
return nil, errors.New("registered name is not a string")
|
||||
}
|
||||
registeredNames = append(registeredNames, strings.Trim(arg.Value, "\""))
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled argument type: %v (%T)", arg, arg)
|
||||
}
|
||||
}
|
||||
return registeredNames, nil
|
||||
}
|
||||
|
||||
//nolint:staticcheck // Use deprecated ast.Package for now
|
||||
func extractPackageDeclarations(pkg *ast.Package) map[string]string {
|
||||
declarations := make(map[string]string)
|
||||
|
||||
for _, file := range pkg.Files {
|
||||
for _, d := range file.Decls {
|
||||
gendecl, ok := d.(*ast.GenDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, spec := range gendecl.Specs {
|
||||
spec, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, id := range spec.Names {
|
||||
valspec, ok := id.Obj.Decl.(*ast.ValueSpec)
|
||||
if !ok || len(valspec.Values) != 1 {
|
||||
continue
|
||||
}
|
||||
valdecl, ok := valspec.Values[0].(*ast.BasicLit)
|
||||
if !ok || valdecl.Kind != token.STRING {
|
||||
continue
|
||||
}
|
||||
declarations[id.Name] = strings.Trim(valdecl.Value, "\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return declarations
|
||||
}
|
||||
|
||||
//nolint:staticcheck // Use deprecated ast.Package for now
|
||||
func extractRegisteredNames(pkg *ast.Package, pluginType string) []string {
|
||||
var registeredNames []string
|
||||
|
||||
// Extract all declared variables of all files. This might be necessary when
|
||||
// using references across multiple files
|
||||
declarations := extractPackageDeclarations(pkg)
|
||||
|
||||
// Find the registry Add statement and extract all registered names
|
||||
for fn, file := range pkg.Files {
|
||||
names, err := extractPluginInfo(file, pluginType, declarations)
|
||||
if err != nil {
|
||||
log.Printf("%q error: %v", fn, err)
|
||||
continue
|
||||
}
|
||||
registeredNames = append(registeredNames, names...)
|
||||
}
|
||||
return registeredNames
|
||||
}
|
||||
|
||||
func extractDefaultDataFormat(pluginDir string) (string, error) {
|
||||
re := regexp.MustCompile(`^\s*#?\s*data_format\s*=\s*"(.*)"\s*$`)
|
||||
|
||||
// Exception for exec input which uses JSON by default
|
||||
if filepath.ToSlash(pluginDir) == "plugins/inputs/exec" {
|
||||
return "json", nil
|
||||
}
|
||||
|
||||
// Walk all config files in the package directory
|
||||
elements, err := os.ReadDir(pluginDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, element := range elements {
|
||||
path := filepath.Join(pluginDir, element.Name())
|
||||
if element.IsDir() || filepath.Ext(element.Name()) != ".conf" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Read the config and search for a "data_format" entry
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
match := re.FindStringSubmatch(scanner.Text())
|
||||
if len(match) == 2 {
|
||||
return match[1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue