diff --git a/internal/commands/config/config.go b/internal/commands/config/config.go index 228ea3a1..0371e59a 100644 --- a/internal/commands/config/config.go +++ b/internal/commands/config/config.go @@ -10,6 +10,7 @@ import ( "github.com/observeinc/observe-agent/internal/commands/util/logger" "github.com/observeinc/observe-agent/internal/root" + "github.com/observeinc/observe-agent/observecol" "github.com/spf13/cobra" ) @@ -41,7 +42,32 @@ bundled OTel configuration.`, }, } +var configValidateCmd = &cobra.Command{ + Use: "validate", + Short: "Validates the configuration for this agent.", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := logger.WithCtx(context.Background(), logger.GetNop()) + col, cleanup, err := observecol.GetOtelCollector(ctx) + if cleanup != nil { + defer cleanup() + } + if err != nil { + fmt.Fprintln(os.Stderr, "❌ failed to generate config") + return err + } + err = col.DryRun(ctx) + if err != nil { + fmt.Fprintln(os.Stderr, "❌ invalid config") + return err + } + fmt.Fprintln(os.Stderr, "✅ configuration is valid") + return nil + }, +} + func init() { + configCmd.AddCommand(configValidateCmd) configCmd.Flags().Bool("render-otel-details", false, "Print the full resolved otel configuration including default values after the otel components perform their semantic processing.") configCmd.Flags().Bool("render-otel", false, "Print a single rendered otel configuration file. This file is equivalent to the bundled configuration enabled in the observe-agent config.") root.RootCmd.AddCommand(configCmd) diff --git a/internal/commands/config/config_test.go b/internal/commands/config/config_test.go index f28e6303..a431a57f 100644 --- a/internal/commands/config/config_test.go +++ b/internal/commands/config/config_test.go @@ -109,7 +109,7 @@ func runSnapshotTest(t *testing.T, test snapshotTest) { assert.True(t, ok) curPath := path.Dir(filename) - // Set the template base dir for all connections + // Set the template overrides for all connections for _, conn := range connections.AllConnectionTypes { conn.ApplyOptions(connections.WithConfigTemplateOverrides(getTemplateOverrides(t, test.packageType, curPath))) } diff --git a/internal/connections/bundledconfig/bundledconfig.go b/internal/connections/bundledconfig/bundledconfig.go index 044efc18..3037f97e 100644 --- a/internal/connections/bundledconfig/bundledconfig.go +++ b/internal/connections/bundledconfig/bundledconfig.go @@ -9,8 +9,10 @@ import ( "github.com/observeinc/observe-agent/internal/connections/bundledconfig/windows" ) +type ConfigTemplates = map[string]embed.FS + // TODO break up some of the larger connections in order to share more configs. -var SharedTemplateFS = map[string]embed.FS{ +var SharedTemplateFS = ConfigTemplates{ "common/attributes.yaml.tmpl": shared.AttributesTemplateFS, "common/internal_telemetry.yaml.tmpl": shared.InternalTelemetryTemplateFS, "common/health_check.yaml.tmpl": shared.HealthCheckTemplateFS, @@ -23,7 +25,7 @@ var SharedTemplateFS = map[string]embed.FS{ "self_monitoring/logs_and_metrics.yaml.tmpl": shared.LogsAndMetricsTemplateFS, } -var DockerTemplateFS = map[string]embed.FS{ +var DockerTemplateFS = ConfigTemplates{ "common/base.yaml.tmpl": docker.BaseTemplateFS, "host_monitoring/logs.yaml.tmpl": docker.LogsTemplateFS, "host_monitoring/host_metrics.yaml.tmpl": docker.HostMetricsTemplateFS, @@ -31,15 +33,15 @@ var DockerTemplateFS = map[string]embed.FS{ "self_monitoring/logs_and_metrics.yaml.tmpl": docker.LogsAndMetricsTemplateFS, } -var LinuxTemplateFS = map[string]embed.FS{ +var LinuxTemplateFS = ConfigTemplates{ "host_monitoring/logs.yaml.tmpl": linux.LogsTemplateFS, "host_monitoring/host_metrics.yaml.tmpl": linux.HostMetricsTemplateFS, "self_monitoring/logs_and_metrics.yaml.tmpl": linux.LogsAndMetricsTemplateFS, } -var MacOSTemplateFS = map[string]embed.FS{} +var MacOSTemplateFS = ConfigTemplates{} -var WindowsTemplateFS = map[string]embed.FS{ +var WindowsTemplateFS = ConfigTemplates{ "common/base.yaml.tmpl": windows.BaseTemplateFS, "host_monitoring/logs.yaml.tmpl": windows.LogsTemplateFS, "host_monitoring/host_metrics.yaml.tmpl": windows.HostMetricsTemplateFS, diff --git a/internal/connections/connections.go b/internal/connections/connections.go index 51dc925b..ccb3f59f 100644 --- a/internal/connections/connections.go +++ b/internal/connections/connections.go @@ -24,14 +24,12 @@ type BundledConfigFragment struct { colConfigFilePath string } -type ConfigOverrides = map[string]embed.FS - type ConnectionType struct { Name string BundledConfigFragments []BundledConfigFragment EnabledCheck EnabledCheckFn - templateOverrides ConfigOverrides + templateOverrides bundledconfig.ConfigTemplates } func (c *ConnectionType) getTemplate(tplName string) (*template.Template, error) { @@ -111,7 +109,7 @@ func MakeConnectionType(name string, enabledCheck EnabledCheckFn, fragments []Bu return c } -func WithConfigTemplateOverrides(templateOverrides ConfigOverrides) ConnectionTypeOption { +func WithConfigTemplateOverrides(templateOverrides bundledconfig.ConfigTemplates) ConnectionTypeOption { return func(c *ConnectionType) { c.templateOverrides = templateOverrides } diff --git a/internal/root/root.go b/internal/root/root.go index 16933839..10c1e016 100644 --- a/internal/root/root.go +++ b/internal/root/root.go @@ -15,12 +15,14 @@ import ( "github.com/observeinc/observe-agent/internal/commands/util/logger" "github.com/observeinc/observe-agent/internal/config" "github.com/observeinc/observe-agent/internal/connections" + "github.com/observeinc/observe-agent/internal/connections/bundledconfig" "github.com/observeinc/observe-agent/observecol" "github.com/spf13/cobra" "github.com/spf13/viper" ) var CfgFile string +var configMode string // RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ @@ -45,12 +47,39 @@ func init() { flags := RootCmd.PersistentFlags() flags.StringVar(&CfgFile, "observe-config", "", "observe-agent config file path") + flags.StringVar(&configMode, "config-mode", "", "The mode to use for bundled config. Valid values are Linux, Docker, Mac, and Windows.") + flags.MarkHidden("config-mode") observecol.AddConfigFlags(flags) observecol.AddFeatureGateFlag(flags) } +func setConfigMode() { + var overrides bundledconfig.ConfigTemplates + switch strings.ToLower(configMode) { + case "": + return + case "linux": + overrides = bundledconfig.LinuxTemplateFS + case "docker": + overrides = bundledconfig.DockerTemplateFS + case "mac": + overrides = bundledconfig.MacOSTemplateFS + case "windows": + overrides = bundledconfig.WindowsTemplateFS + default: + fmt.Fprintf(os.Stderr, "Invalid config mode specified: %s. Valid values are Linux, Docker, Mac, and Windows.\n", configMode) + os.Exit(1) + } + // Set the template overrides for all connections + for _, conn := range connections.AllConnectionTypes { + conn.ApplyOptions(connections.WithConfigTemplateOverrides(overrides)) + } + +} + // InitConfig reads in config file and ENV variables if set. func InitConfig() { + setConfigMode() ctx := logger.WithCtx(context.Background(), logger.Get()) // Some keys in OTEL component configs use "." as part of the key but viper ends up parsing that into // a subobject since the default key delimiter is "." which causes config validation to fail.