telegraf/cmd/telegraf/cmd_config.go

241 lines
7.7 KiB
Go

// Command handling for configuration "config" command
package main
import (
"errors"
"fmt"
"io"
"log"
"net/url"
"os"
"path/filepath"
"github.com/fatih/color"
"github.com/urfave/cli/v2"
"github.com/influxdata/telegraf/agent"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/logger"
"github.com/influxdata/telegraf/migrations"
)
func getConfigCommands(configHandlingFlags []cli.Flag, outputBuffer io.Writer) []*cli.Command {
return []*cli.Command{
{
Name: "config",
Usage: "commands for generating and migrating configurations",
Flags: configHandlingFlags,
Action: func(cCtx *cli.Context) error {
// The sub_Filters are populated when the filter flags are set after the subcommand config
// e.g. telegraf config --section-filter inputs
filters := processFilterFlags(cCtx)
printSampleConfig(outputBuffer, filters)
return nil
},
Subcommands: []*cli.Command{
{
Name: "check",
Usage: "check configuration file(s) for issues",
Description: `
The 'check' command reads the configuration files specified via '--config' or
'--config-directory' and tries to initialize, but not start, the plugins.
Syntax and semantic errors detectable without starting the plugins will
be reported.
If no configuration file is explicitly specified the command reads the
default locations and uses those configuration files.
To check the file 'mysettings.conf' use
> telegraf config check --config mysettings.conf
`,
Flags: configHandlingFlags,
Action: func(cCtx *cli.Context) error {
// Setup logging
logConfig := &logger.Config{Debug: cCtx.Bool("debug")}
if err := logger.SetupLogging(logConfig); err != nil {
return err
}
// Collect the given configuration files
configFiles := cCtx.StringSlice("config")
configDir := cCtx.StringSlice("config-directory")
for _, fConfigDirectory := range configDir {
files, err := config.WalkDirectory(fConfigDirectory)
if err != nil {
return err
}
configFiles = append(configFiles, files...)
}
// If no "config" or "config-directory" flag(s) was
// provided we should load default configuration files
if len(configFiles) == 0 {
paths, err := config.GetDefaultConfigPath()
if err != nil {
return err
}
configFiles = paths
}
// Load the config and try to initialize the plugins
c := config.NewConfig()
c.Agent.Quiet = cCtx.Bool("quiet")
if err := c.LoadAll(configFiles...); err != nil {
return err
}
ag := agent.NewAgent(c)
// Set the default for processor skipping
if c.Agent.SkipProcessorsAfterAggregators == nil {
msg := `The default value of 'skip_processors_after_aggregators' will change to 'true' with Telegraf v1.40.0! `
msg += `If you need the current default behavior, please explicitly set the option to 'false'!`
log.Print("W! [agent] ", color.YellowString(msg))
skipProcessorsAfterAggregators := false
c.Agent.SkipProcessorsAfterAggregators = &skipProcessorsAfterAggregators
}
return ag.InitPlugins()
},
},
{
Name: "create",
Usage: "create a full sample configuration and show it",
Description: `
The 'create' produces a full configuration containing all plugins as an example
and shows it on the console. You may apply 'section' or 'plugin' filtering
to reduce the output to the plugins you need
Create the full configuration
> telegraf config create
To produce a configuration only containing a Modbus input plugin and an
InfluxDB v2 output plugin use
> telegraf config create --section-filter "inputs:outputs" --input-filter "modbus" --output-filter "influxdb_v2"
`,
Flags: configHandlingFlags,
Action: func(cCtx *cli.Context) error {
filters := processFilterFlags(cCtx)
printSampleConfig(outputBuffer, filters)
return nil
},
},
{
Name: "migrate",
Usage: "migrate deprecated plugins and options of the configuration(s)",
Description: `
The 'migrate' command reads the configuration files specified via '--config' or
'--config-directory' and tries to migrate plugins or options that are currently
deprecated using the recommended replacements. If no configuration file is
explicitly specified the command reads the default locations and uses those
configuration files. Migrated files are stored with a '.migrated' suffix at the
location of the inputs. If you are migrating remote configurations the migrated
configurations is stored in the current directory using the filename of the URL
with a '.migrated' suffix.
It is highly recommended to test those migrated configurations before using
those files unattended!
To migrate the file 'mysettings.conf' use
> telegraf config migrate --config mysettings.conf
`,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "force",
Usage: "forces overwriting of an existing migration file",
},
},
Action: func(cCtx *cli.Context) error {
// Setup logging
logConfig := &logger.Config{Debug: cCtx.Bool("debug")}
if err := logger.SetupLogging(logConfig); err != nil {
return err
}
// Check if we have migrations at all. There might be
// none if you run a custom build without migrations
// enabled.
if len(migrations.PluginMigrations) == 0 {
return errors.New("no migrations available")
}
log.Printf("%d plugin migration(s) available", len(migrations.PluginMigrations))
// Collect the given configuration files
configFiles := cCtx.StringSlice("config")
configDir := cCtx.StringSlice("config-directory")
for _, fConfigDirectory := range configDir {
files, err := config.WalkDirectory(fConfigDirectory)
if err != nil {
return err
}
configFiles = append(configFiles, files...)
}
// If no "config" or "config-directory" flag(s) was
// provided we should load default configuration files
if len(configFiles) == 0 {
paths, err := config.GetDefaultConfigPath()
if err != nil {
return err
}
configFiles = paths
}
for _, fn := range configFiles {
log.Printf("D! Trying to migrate %q...", fn)
// Read and parse the config file
data, remote, err := config.LoadConfigFile(fn)
if err != nil {
return fmt.Errorf("opening input %q failed: %w", fn, err)
}
out, applied, err := config.ApplyMigrations(data)
if err != nil {
return err
}
// Do not write a migration file if nothing was done
if applied == 0 {
log.Printf("I! No migration applied for %q", fn)
continue
}
// Construct the output filename
// For remote locations we just save the filename
// with the migrated suffix.
outfn := fn + ".migrated"
if remote {
u, err := url.Parse(fn)
if err != nil {
return fmt.Errorf("parsing remote config URL %q failed: %w", fn, err)
}
outfn = filepath.Base(u.Path) + ".migrated"
}
log.Printf("I! %d migration applied for %q, writing result as %q", applied, fn, outfn)
// Make sure the file does not exist yet if we should not overwrite
if !cCtx.Bool("force") {
if _, err := os.Stat(outfn); !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("output file %q already exists", outfn)
}
}
// Write the output file
if err := os.WriteFile(outfn, out, 0640); err != nil {
return fmt.Errorf("writing output %q failed: %w", outfn, err)
}
}
return nil
},
},
},
},
}
}