Skip to content

Conversation

@tomaz-lc
Copy link

@tomaz-lc tomaz-lc commented Feb 4, 2025

Description

This pull request is an attempt to start a discussion about improving command line argument handling in various normal and edge conditions and improving the end user experience.

Additionally, it also introduces the new -help and -version CLI flag.

Background, Context

While using the tool, I noticed it includes sub-optional user experience in various scenarios, including:

  1. If an error occurs, an error is printed at the very top and then it lists all the available sensors. This makes for a bad UX since the user needs to scroll up a lot to find error along with all the "noise".
  2. During various error conditions, usage is printed twice
  3. Error messages about syntax and other errors in the config file are not user friendly since we just expose raw JSON/YAML parser error messages. This is not something I have tackled yet.
  4. Remove some duplicate printing of errors and usage.

Examples

Before
# 1. No arguments
starting
error: not enough arguments
Usage: ./adapter adapter_type [config_file.yaml | <param>...]
Available configs:


For healthcheck
----------------------------------


For syslog
----------------------------------
client_options.identity.oid
client_options.identity.installation_key
client_options.hostname
client_options.platform
client_options.architecture
client_options.mapping.parsing_re
client_options.mapping.sensor_key_path
client_options.mapping.sensor_hostname_path
client_options.mapping.event_type_path
client_options.mapping.event_time_path
client_options.mapping.investigation_id_path
client_options.mapping.rename_only
client_options.mapping.mappings
client_options.mapping.transform
client_options.mapping.drop_fields
client_options.mappings
client_options.indexing
client_options.is_compressed
client_options.sensor_seed_key
client_options.dest_url
port
iface
is_udp
ssl_cert
ssl_key
mutual_tls_cert
write_timeout_sec

For pubsub
----------------------------------
client_options.identity.oid
client_options.identity.installation_key
client_options.hostname
client_options.platform
client_options.architecture
client_options.mapping.parsing_re
client_options.mapping.sensor_key_path
client_options.mapping.sensor_hostname_path
client_options.mapping.event_type_path
client_options.mapping.event_time_path
client_options.mapping.investigation_id_path
client_options.mapping.rename_only
client_options.mapping.mappings
client_options.mapping.transform
client_options.mapping.drop_fields
client_options.mappings
client_options.indexing
client_options.is_compressed
client_options.sensor_seed_key
client_options.dest_url
sub_name
project_name
service_account_creds
max_ps_buffer
...


# 2. Invalid config
starting
loading config from file: 1.yaml
Usage: ./adapter adapter_type [config_file.yaml | <param>...]
Available configs:


For healthcheck
----------------------------------


For syslog
----------------------------------
client_options.identity.oid
client_options.identity.installation_key
client_options.hostname
client_options.platform
client_options.architecture
client_options.mapping.parsing_re
client_options.mapping.sensor_key_path
client_options.mapping.sensor_hostname_path
client_options.mapping.event_type_path
client_options.mapping.event_time_path
client_options.mapping.investigation_id_path
client_options.mapping.rename_only
client_options.mapping.mappings
client_options.mapping.transform
client_options.mapping.drop_fields
client_options.mappings
client_options.indexing
client_options.is_compressed
client_options.sensor_seed_key
client_options.dest_url
port
iface
is_udp
ssl_cert
ssl_key
mutual_tls_cert
write_timeout_sec

For pubsub
----------------------------------
client_options.identity.oid
client_options.identity.installation_key
client_options.hostname
client_options.platform
client_options.architecture
client_options.mapping.parsing_re
client_options.mapping.sensor_key_path
client_options.mapping.sensor_hostname_path
client_options.mapping.event_type_path
client_options.mapping.event_time_path
client_options.mapping.investigation_id_path
client_options.mapping.rename_only
client_options.mapping.mappings
client_options.mapping.transform
client_options.mapping.drop_fields
client_options.mappings
client_options.indexing
client_options.is_compressed
client_options.sensor_seed_key
client_options.dest_url
sub_name
project_name
service_account_creds
max_ps_buffer

...
After
# 1. No arguments
Same as before (for now).

# 2. Error in config file
./general file 1.yaml 
starting
loading config from file: 1.yaml

error: decoding error: json=invalid character '-' in numeric literal yaml=yaml: unmarshal errors:
  line 2: cannot unmarshal !!str `foo:x` into main.GeneralConfigs

Usage: ./adapter adapter_type [config_file.yaml | <param>...]

For a list of all the available configs, run: ./lc-adapter --help or run the program without any arguments

# 3. -help flag
./general -help
Usage: ./adapter adapter_type [config_file.yaml | <param>...]

For a list of all the available configs, run: ./lc-adapter --help or run the program without any arguments

Available configs:


For healthcheck
----------------------------------


For syslog
----------------------------------
client_options.identity.oid
client_options.identity.installation_key
client_options.hostname
client_options.platform
client_options.architecture
client_options.mapping.parsing_re
client_options.mapping.sensor_key_path
client_options.mapping.sensor_hostname_path
client_options.mapping.event_type_path
client_options.mapping.event_time_path
client_options.mapping.investigation_id_path
client_options.mapping.rename_only
client_options.mapping.mappings
client_options.mapping.transform
client_options.mapping.drop_fields
client_options.mappings
client_options.indexing
client_options.is_compressed
client_options.sensor_seed_key
client_options.dest_url
port
iface
is_udp
ssl_cert
ssl_key
mutual_tls_cert
write_timeout_sec
....

# 4. -version flag
./general -version
Version: (devel)

Proposed Implementation

In this PR I propose a simple change to handle some (but not yet all) of those edge cases better. It's by no means a final solution or anything like that.

I just wanted to start a discussion on how to handle that better and once we reach a consensus (e.g. should we use flag package, should we use go style -flags or --flags, should I just fix main issue or fully refactor the CLI handling - without extensive end to end test coverage, full refactoring will be risky, etc), I can implement those changes.

It's also worth pointing out that currently the code does some unconventional thing with args (e.g. service mode and handling any arg starting with - as pass through to service mode) which means we could not use flag package directly without more boiler plate. This means it could defeat the purpose and just increase the complexity so the current simple and naive implementation is better.

Additionally, I also included some simple bats (https://github.com/bats-core/bats-core) based end to end tests. If people have preference for some other end to end testing framework (e.g. some Golang based one such as https://github.com/elastic/testcli), we can discuss that as well.

TODO

  • Decode on the Implementation / approach
  • Decide how much refactoring we want to do
  • Agree on the end to end testing framework
  • Add basic unit tests
  • Possibly add end to end compiled binary tests

Related issues

Fix refractionPOINT/tracking#2983

@tomaz-lc tomaz-lc changed the title feat: Improve command line argument handling in various error / edge feat: Improve command line argument handling and user experience Feb 5, 2025
@tomaz-lc tomaz-lc force-pushed the feat/better-cli-arguments-handling-new-flags branch 11 times, most recently from 024f277 to a3dc2c8 Compare February 5, 2025 09:49
…andling in various error / edge

conditions and introduce new "--help" and "--version" flag.

NOTE: This is just a quick PoC / proposal so we can brainstorm better
solution. Ideal better command line argument handling would require
larger refactor.
…inary end to end tests and

corresponding GHA workflow which runs unit tests with coverage + end to
end tests.
…e mode and other edge cases and

refactor the code a bit more.
@tomaz-lc tomaz-lc force-pushed the feat/better-cli-arguments-handling-new-flags branch from 1094d6d to 84d2888 Compare February 6, 2025 15:45
"github.com/refractionPOINT/usp-adapters/evtx"
"github.com/refractionPOINT/usp-adapters/file"
"github.com/refractionPOINT/usp-adapters/gcs"
"github.com/refractionPOINT/usp-adapters/hubspot"
Copy link
Author

Choose a reason for hiding this comment

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

This fixes a minor lint / go fmt issue which managed to sneak into master.

scenarios:

- Multiple adapter configs specified in a single YAML config file
- Config options are specified using CLI arguments
- Config options are specified using environment variables (currently
  broken)

Also fix tests and add missing status assertions for bats "run"
statements.
which is not needed to avoid printing usage multiple times.
@tomaz-lc
Copy link
Author

tomaz-lc commented Feb 7, 2025

I added more test cases to try to cover all the documented scenarios / possible uses of the tool:

  1. Specifying config options in a YAML config file
  2. Specifying config options using CLI args
  3. Specifying config options using environment variables

It turns out that the last approach doesn't appear to be working. There appear to be multiple reasons for that and code would need multiple adjustments to make it work (possibly also handling special characters in environment variable names such as ".", etc), but the main one appears to be this line -

if len(args) < 2 {

For the time being (to keep the scope down and until we reach a consensus on how to approach it), I decided not to fix it.

func parseConfigsFromFile(filePath string) ([]*GeneralConfigs, error) {
f, err := os.Open(filePath)
if err != nil {
printUsage()
Copy link
Author

Choose a reason for hiding this comment

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

This was removed since the tool previously printed usage string multiple times (duplication) during some failure scenarios.

}
b, err := io.ReadAll(f)
if err != nil {
printUsage()
Copy link
Author

Choose a reason for hiding this comment

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

This was removed since the tool previously printed usage string multiple times (duplication) during some failure scenarios.

}

if jsonErr != nil && yamlErr != nil {
printUsage()
Copy link
Author

Choose a reason for hiding this comment

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

This was removed since the tool previously printed usage string multiple times (duplication) during some failure scenarios.

the GHA workflow.

For now, only report and fail on new issues due to multiple existing
issues being presents (those will be addressed separately).

Also split workflow into two jobs:

- lint jobs -> runs lint + static analysis using a single Go version
- tests -> runs unit and integration tests under multiple Go versions
@tomaz-lc tomaz-lc force-pushed the feat/better-cli-arguments-handling-new-flags branch from e688883 to b84e12b Compare February 7, 2025 12:25
with known data race (due to possible race in tail package) with race
detection enabled.
@@ -1,3 +1,6 @@
//go:build skiptests
// +build skiptests
Copy link
Author

Choose a reason for hiding this comment

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

There is one known race condition in tail library + how we use it (we call tell before stop).

@tomaz-lc tomaz-lc marked this pull request as ready for review February 10, 2025 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants