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

Load external .so plugins, fixes #1717 #6024

Merged
merged 1 commit into from
Jul 30, 2019
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
54 changes: 53 additions & 1 deletion cmd/telegraf/telegraf.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
_ "net/http/pprof" // Comment this line to disable pprof endpoint.
"os"
"os/signal"
"path"
"path/filepath"
"plugin"
"runtime"
"strings"
"syscall"
Expand Down Expand Up @@ -64,6 +67,8 @@ var fService = flag.String("service", "",
var fServiceName = flag.String("service-name", "telegraf", "service name (windows only)")
var fServiceDisplayName = flag.String("service-display-name", "Telegraf Data Collector Service", "service display name (windows only)")
var fRunAsConsole = flag.Bool("console", false, "run as console application (windows only)")
var fPlugins = flag.String("plugin-directory", "",
"path to directory containing external plugins")

var (
version string
Expand Down Expand Up @@ -111,6 +116,45 @@ func reloadLoop(
}
}

// loadExternalPlugins loads external plugins from shared libraries (.so, .dll, etc.)
// in the specified directory.
func loadExternalPlugins(rootDir string) error {
return filepath.Walk(rootDir, func(pth string, info os.FileInfo, err error) error {
// Stop if there was an error.
if err != nil {
return err
}

// Ignore directories.
if info.IsDir() {
return nil
}

// Ignore files that aren't shared libraries.
ext := strings.ToLower(path.Ext(pth))
if ext != ".so" && ext != ".dll" {
return nil
}

// name will be the path to the plugin file beginning at the root
// directory, minus the extension.
// ie, if the plugin file is ./telegraf-plugins/foo.so, name
// will be "telegraf-plugins/foo"
name := strings.TrimPrefix(strings.TrimPrefix(pth, rootDir), string(os.PathSeparator))
name = strings.TrimSuffix(name, filepath.Ext(pth))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the var name defined?
Looks like is not being used.

Maybe it was thought to register the plugin with that name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, TOML doesn't like names with slasesh:

[[inputs.extplugins/ext-kernel]]
2019-08-05T11:01:53Z E! [telegraf] Error running agent: Error parsing prueba.conf, line 5: invalid TOML 

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is leftover of the previous implementation, you're right that it should be removed.

Also, TOML doesn't like names with slasesh

You can include a slash using ""

[[inputs."extplugins/ext-kernel"]]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably already have this figured out, but I removed the name code when merging.
Plugins can be loaded using the name specified in the registration function called from init().

I also have an example that I was using for testing over at https://github.com/danielnelson/telegraf-plugins


// Load plugin.
_, err = plugin.Open(pth)
if err != nil {
errorMsg := fmt.Sprintf("error loading [%s]: %s", pth, err)
log.Printf(errorMsg)
return errors.New(errorMsg)
}

return nil
})
}

func runAgent(ctx context.Context,
inputFilters []string,
outputFilters []string,
Expand Down Expand Up @@ -138,7 +182,7 @@ func runAgent(ctx context.Context,
if !*fTest && len(c.Outputs) == 0 {
return errors.New("Error: no outputs found, did you provide a valid config file?")
}
if len(c.Inputs) == 0 {
if *fPlugins == "" && len(c.Inputs) == 0 {
return errors.New("Error: no inputs found, did you provide a valid config file?")
}

Expand Down Expand Up @@ -279,6 +323,14 @@ func main() {
processorFilters = strings.Split(":"+strings.TrimSpace(*fProcessorFilters)+":", ":")
}

// Load external plugins, if requested.
if *fPlugins != "" {
log.Printf("Loading external plugins from: %s\n", *fPlugins)
if err := loadExternalPlugins(*fPlugins); err != nil {
log.Fatal(err.Error())
}
}

if *pprofAddr != "" {
go func() {
pprofHostPort := *pprofAddr
Expand Down
3 changes: 3 additions & 0 deletions internal/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ The commands & flags are:
--aggregator-filter <filter> filter the aggregators to enable, separator is :
--config <file> configuration file to load
--config-directory <directory> directory containing additional *.conf files
--plugin-directory directory containing *.so files, this directory will be
searched recursively. Any Plugin found will be loaded
and namespaced.
--debug turn on debug logging
--input-filter <filter> filter the inputs to enable, separator is :
--input-list print available input plugins.
Expand Down