diff --git a/ee/tuf/library_lookup.go b/ee/tuf/library_lookup.go index 19a0b22e4..240545dc8 100644 --- a/ee/tuf/library_lookup.go +++ b/ee/tuf/library_lookup.go @@ -13,6 +13,7 @@ import ( "github.com/kolide/launcher/pkg/launcher" "github.com/kolide/launcher/pkg/traces" "github.com/peterbourgon/ff/v3" + "github.com/spf13/pflag" ) type BinaryUpdateInfo struct { @@ -38,7 +39,7 @@ var channelsUsingNewAutoupdater = map[string]bool{ // For now, it is only available when launcher is on the nightly update channel. func CheckOutLatestWithoutConfig(binary autoupdatableBinary, logger log.Logger) (*BinaryUpdateInfo, error) { logger = log.With(logger, "component", "tuf_library_lookup") - cfg, err := getAutoupdateConfig() + cfg, err := getAutoupdateConfig(os.Args[1:]) if err != nil { return nil, fmt.Errorf("could not get autoupdate config: %w", err) } @@ -52,7 +53,7 @@ func CheckOutLatestWithoutConfig(binary autoupdatableBinary, logger log.Logger) } func UsingNewAutoupdater() bool { - cfg, err := getAutoupdateConfig() + cfg, err := getAutoupdateConfig(os.Args[1:]) if err != nil { return false } @@ -60,13 +61,53 @@ func UsingNewAutoupdater() bool { return ChannelUsesNewAutoupdater(cfg.channel) } -// getAutoupdateConfig reads launcher's config file to determine the configuration values -// needed to work with the autoupdate library. -func getAutoupdateConfig() (*autoupdateConfig, error) { - configFilePath := launcher.ConfigFilePath(os.Args[1:]) - if configFilePath == "" { - return nil, errors.New("could not get config file path") +// getAutoupdateConfig pulls the configuration values necessary to work with the autoupdate library +// from either the given args or from the config file. +func getAutoupdateConfig(args []string) (*autoupdateConfig, error) { + // Create a flagset with options that are relevant to autoupdate only. + // Ensure that we won't fail out when we see other command-line options. + pflagSet := pflag.NewFlagSet("autoupdate options", pflag.ContinueOnError) + pflagSet.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{UnknownFlags: true} + + // Extract the config flag plus the autoupdate flags + var flConfigFilePath, flRootDirectory, flUpdateDirectory, flUpdateChannel, flLocalDevelopmentPath string + pflagSet.StringVar(&flConfigFilePath, "config", "", "") + pflagSet.StringVar(&flRootDirectory, "root_directory", "", "") + pflagSet.StringVar(&flUpdateDirectory, "update_directory", "", "") + pflagSet.StringVar(&flUpdateChannel, "update_channel", "", "") + pflagSet.StringVar(&flLocalDevelopmentPath, "localdev_path", "", "") + + if err := pflagSet.Parse(args); err != nil { + return nil, fmt.Errorf("parsing command-line flags: %w", err) + } + + // If the config file wasn't set AND the other critical flags weren't set, fall back + // to looking in the default config flag file location. (The update directory and local + // development path are both optional flags and not critical to library lookup + // functionality.) We expect all the flags to be set either via config flag (flConfigFilePath + // is set) or via command line (flRootDirectory and flUpdateChannel are set), but do not + // support a mix of both for this usage. + if flConfigFilePath == "" && flRootDirectory == "" && flUpdateChannel == "" { + return getAutoupdateConfigFromFile(launcher.ConfigFilePath(args)) + } + + if flConfigFilePath != "" { + return getAutoupdateConfigFromFile(flConfigFilePath) } + + cfg := &autoupdateConfig{ + rootDirectory: flRootDirectory, + updateDirectory: flUpdateDirectory, + channel: flUpdateChannel, + localDevelopmentPath: flLocalDevelopmentPath, + } + + return cfg, nil +} + +// getAutoupdateConfigFromFile reads launcher's config file to determine the configuration values +// needed to work with the autoupdate library. +func getAutoupdateConfigFromFile(configFilePath string) (*autoupdateConfig, error) { if _, err := os.Stat(configFilePath); err != nil && os.IsNotExist(err) { return nil, fmt.Errorf("could not read config file because it does not exist at %s: %w", configFilePath, err) } diff --git a/ee/tuf/library_lookup_test.go b/ee/tuf/library_lookup_test.go index 55a09f73d..1fab2011d 100644 --- a/ee/tuf/library_lookup_test.go +++ b/ee/tuf/library_lookup_test.go @@ -214,3 +214,62 @@ func TestChannelUsesNewAutoupdater(t *testing.T) { require.Equal(t, channel.usesNewAutoupdater, ChannelUsesNewAutoupdater(channel.channelName)) } } + +func Test_getAutoupdateConfig_ConfigFlagSet(t *testing.T) { + t.Parallel() + + tempConfDir := t.TempDir() + configFilepath := filepath.Join(tempConfDir, "launcher.flags") + + testRootDir := t.TempDir() + testChannel := "nightly" + + fileContents := fmt.Sprintf(` +with_initial_runner +autoupdate +hostname localhost +root_directory %s +update_channel %s +transport jsonrpc +`, + testRootDir, + testChannel, + ) + + require.NoError(t, os.WriteFile(configFilepath, []byte(fileContents), 0755), "expected to set up test config file") + + cfg, err := getAutoupdateConfig([]string{"--config", configFilepath}) + require.NoError(t, err, "expected no error getting autoupdate config") + + require.NotNil(t, cfg, "expected valid autoupdate config") + require.Equal(t, testRootDir, cfg.rootDirectory, "root directory is incorrect") + require.Equal(t, "", cfg.updateDirectory, "update directory should not have been set") + require.Equal(t, testChannel, cfg.channel, "channel is incorrect") + require.Equal(t, "", cfg.localDevelopmentPath, "local development path should not have been set") +} + +func Test_getAutoupdateConfig_ConfigFlagNotSet(t *testing.T) { + t.Parallel() + + testRootDir := t.TempDir() + testUpdateDir := t.TempDir() + testChannel := "nightly" + testLocaldevPath := filepath.Join("some", "path", "to", "a", "local", "build") + + cfg, err := getAutoupdateConfig([]string{ + "--root_directory", testRootDir, + "--osquery_flag", "enable_watchdog_debug=true", + "--update_directory", testUpdateDir, + "--autoupdate", + "--update_channel", testChannel, + "--localdev_path", testLocaldevPath, + "--transport", "jsonrpc", + }) + require.NoError(t, err, "expected no error getting autoupdate config") + + require.NotNil(t, cfg, "expected valid autoupdate config") + require.Equal(t, testRootDir, cfg.rootDirectory, "root directory is incorrect") + require.Equal(t, testUpdateDir, cfg.updateDirectory, "update directory is incorrect") + require.Equal(t, testChannel, cfg.channel, "channel is incorrect") + require.Equal(t, testLocaldevPath, cfg.localDevelopmentPath, "local development path is incorrect") +} diff --git a/go.mod b/go.mod index 3dbd290fd..6bd41b6c8 100644 --- a/go.mod +++ b/go.mod @@ -67,6 +67,7 @@ require ( github.com/felixge/httpsnoop v1.0.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/samber/lo v1.38.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/tools v0.16.0 // indirect