From dd5c1cf2118cbfd242b09c54f9899f58c2e820ab Mon Sep 17 00:00:00 2001 From: Lukasz Okraszewski Date: Wed, 30 Jul 2025 14:44:44 +0100 Subject: [PATCH 1/2] [tctl/top] Parse teleport config file Add config file parsing to `tctl top` command, this normally is done on client configuration but since the `top` client does not make use of the common tctl client it previously did not support the local config file. This this change it is now possible to use a custom datadir for the debug socket. changelog: tctl top respects local teleport config file --- tool/tctl/common/top/command.go | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tool/tctl/common/top/command.go b/tool/tctl/common/top/command.go index 14aeaf20597ea..979edd5e90455 100644 --- a/tool/tctl/common/top/command.go +++ b/tool/tctl/common/top/command.go @@ -26,6 +26,7 @@ import ( dto "github.com/prometheus/client_model/go" "github.com/gravitational/teleport/lib/client/debug" + "github.com/gravitational/teleport/lib/config" "github.com/gravitational/teleport/lib/service/servicecfg" commonclient "github.com/gravitational/teleport/tool/tctl/common/client" tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config" @@ -36,6 +37,7 @@ import ( // Teleport /metrics endpoint and displays diagnostic // information an easy to consume way. type Command struct { + global *tctlcfg.GlobalCLIFlags config *servicecfg.Config top *kingpin.CmdClause diagURL string @@ -45,8 +47,9 @@ type Command struct { const defaultDiagAddr = "http://127.0.0.1:3000" // Initialize sets up the "tctl top" command. -func (c *Command) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIFlags, config *servicecfg.Config) { +func (c *Command) Initialize(app *kingpin.Application, global *tctlcfg.GlobalCLIFlags, config *servicecfg.Config) { c.config = config + c.global = global c.top = app.Command("top", "Report diagnostic information.") c.top.Arg("diag-addr", "Diagnostic HTTP URL").StringVar(&c.diagURL) c.top.Arg("refresh", "Refresh period").Default("5s").DurationVar(&c.refreshPeriod) @@ -93,12 +96,43 @@ func (c *Command) newMetricsClient(ctx context.Context) (string, MetricsClient, "connecting to Teleport metrics server") } +func (c *Command) initConfig() error { + var fileConf *config.FileConfig + var err error + if c.global.ConfigFile != "" { + fileConf, err = config.ReadConfigFile(c.global.ConfigFile) + if err != nil { + return trace.Wrap(err) + } + } + + if c.global.ConfigString != "" { + fileConf, err = config.ReadFromString(c.global.ConfigString) + if err != nil { + return trace.Wrap(err) + } + } + + if fileConf != nil { + if err = config.ApplyFileConfig(fileConf, c.config); err != nil { + return trace.Wrap(err) + } + } + + return nil +} + // TryRun attempts to run subcommands. func (c *Command) TryRun(ctx context.Context, cmd string, _ commonclient.InitFunc) (match bool, err error) { if cmd != c.top.FullCommand() { return false, nil } + err = c.initConfig() + if err != nil { + return true, trace.Wrap(err) + } + addr, metricsClient, err := c.newMetricsClient(ctx) if err != nil { return true, trace.Wrap(err) From 185e08962596061d3ad3dcae87064ac462f7659c Mon Sep 17 00:00:00 2001 From: Lukasz Okraszewski Date: Wed, 30 Jul 2025 16:27:38 +0100 Subject: [PATCH 2/2] apply review feedback --- tool/tctl/common/top/command.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tool/tctl/common/top/command.go b/tool/tctl/common/top/command.go index 979edd5e90455..593fe71db4b34 100644 --- a/tool/tctl/common/top/command.go +++ b/tool/tctl/common/top/command.go @@ -96,6 +96,13 @@ func (c *Command) newMetricsClient(ctx context.Context) (string, MetricsClient, "connecting to Teleport metrics server") } +// initConfig populates the [*servicecfg.Config] +// +// This is required by `tctl top` as it does not make use of the [commonclient.InitFunc] +// which lazy loads the configuration during client creation, meaning the required configuration +// fields are not initilized as this command uses a standalone metrics client instead on a local connection. +// +// TODO(okraport): remove this workaround once the caller can safely parse the teleport config file without common client. func (c *Command) initConfig() error { var fileConf *config.FileConfig var err error @@ -128,8 +135,7 @@ func (c *Command) TryRun(ctx context.Context, cmd string, _ commonclient.InitFun return false, nil } - err = c.initConfig() - if err != nil { + if err := c.initConfig(); err != nil { return true, trace.Wrap(err) }