Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce ArgumentConfigSource #290

Closed
wants to merge 1 commit into from
Closed

Conversation

gastaldi
Copy link
Contributor

@gastaldi gastaldi commented Apr 22, 2020

  • Uses a GNU-style argument form (essentially --arg-name=foo and --arg-name foo).
  • Short arguments with no value should be supported corresponding to boolean properties e.g. -x

This should check one of the boxes in quarkusio/quarkus#6497

cc @dmlloyd

@dmlloyd
Copy link
Contributor

dmlloyd commented Apr 22, 2020

It looks like --arg foo is not tested, so it should be clarified whether support for it is there (I believe it is not part of GNU, but many tools seem to support it I am wrong, it is supported by GNU). Note that GNU is a superset of POSIX (meaning, support for single-letter argument names that can be condensed into single options or provided adjacently to their argument (if they have one), special support for -, and special support for --), so it should be explicitly decided whether GNU form is supported or whether we're striking out on our own.

Other requirements that should be considered:

  • Support for explicit (via --) and implicit argument list termination
  • Collection of the remaining non-argument values into a list property or multiple list properties
  • Support for arguments with many values
  • Detection of unrecognized arguments or arguments
  • Mapping of command line argument names like --foo-bar to "regular" property names like quarkus.foo.bar
  • Defining short names -x with long name aliases --exciting
  • Folding of e.g. --with-foo/--without-foo, --enable-foo/--disable-foo, and/or --foo/--no-foo to single boolean values

I don't think these requirements are possible to solve, without providing metadata to the config source that defines exactly what arguments are expected and what their form and behavior is expected to be.

@gastaldi gastaldi force-pushed the arguments branch 2 times, most recently from 0786eec to 3ab9cc8 Compare April 22, 2020 21:31
@gastaldi
Copy link
Contributor Author

gastaldi commented Apr 22, 2020

I reworded the commit message and added the missing test.

I am not sure if a previous metadata is really needed, since we only care about the provided arguments and not if they are required or malformed, etc (input validation is out of scope of the ConfigSource implementation IMHO).

About multiple values, isn't enough to return a comma-separated list of values (then the Converter would kick in to convert to the appropriate type)?

@gastaldi gastaldi force-pushed the arguments branch 3 times, most recently from 0caf1a1 to a245c29 Compare April 22, 2020 21:49
@dmlloyd
Copy link
Contributor

dmlloyd commented Apr 22, 2020

I reworded the commit message and added the missing test.

I am not sure if a previous metadata is really needed, since we only care about the provided arguments and not if they are required or malformed, etc (input validation is out of scope of the ConfigSource implementation IMHO).

Whether or not I agree that input validation is out of scope for ConfigSources (I don't BTW - consider that properties and YAML files must be well formed, for example), the config source still must make input validation at least be possible. That means at the very least the consuming party has to know which configuration properties were command line arguments, and possibly what order they occurred in. Arguments appearing multiple times need handling as well. At a point, the user is obligated to know so much special information about the fact that they are command line arguments rather than "regular" config, that having a config source hardly seems worth it, unless that all can be made transparent, similarly to how the classic getopt_long works:

               static struct option long_options[] = {
                   {"add",     required_argument, 0,  0 },
                   {"append",  no_argument,       0,  0 },
                   {"delete",  required_argument, 0,  0 },
                   {"verbose", no_argument,       0,  0 },
                   {"create",  required_argument, 0, 'c'},
                   {"file",    required_argument, 0,  0 },
                   {0,         0,                 0,  0 }
               };

               c = getopt_long(argc, argv, "abc:d:012",
                        long_options, &option_index);

This is enough to determine that the arguments and their values are well-formed, yet still allowing validation to happen at a later point. For SmallRye Config I'd take the above example one step farther, to be able to say things like:

  • Long argument "add" takes an argument, can only appear once, and maps to the configuration property quarkus.config.file.add
  • Long argument "create" takes one argument, can appear multiple times, has a short alias of c, and maps to property quarkus.config.file.create

...and so on like that. The well-formedness can be verified immediately by the config source, just like it is for YAML and friends. But both the validation piece and the detection of required-but-empty piece can then be handled cleanly by all the existing frameworks/mechanisms we have in place in Quarkus, and eventually will have in SmallRye Config, with no modifications.

About multiple values, isn't enough to return a comma-separated list of values (then the Converter would kick in to convert to the appropriate type)?

Properly quoted, yes, that is how it would need to be done. But in order to know whether to quote the values, you have to know whether it's a list or not going in, otherwise list values will behave incorrectly when parsed as non-lists and vice versa, and it will also be impossible to detect whether an argument was illegally given more than once (as opposed to innocently containing a valid comma for example) after the fact of parsing.

- Supports --arg-name=foo,--arg-name foo, -a, -a b and -a=b
- Short arguments with no value should be supported corresponding to boolean properties e.g. -x
- Supports multiple values (--foo=1 --foo=2 --foo=3)
@gastaldi
Copy link
Contributor Author

gastaldi commented Apr 22, 2020

I see. Do you have any preferred parser/library that would also perform this metadata validation?

@dmlloyd
Copy link
Contributor

dmlloyd commented Apr 22, 2020

No, I would devise a builder API for the config source to specify the necessary information. Using POSIX and GNU as a guide, I'd start with the following requirements:

  • For each recognized argument,
    • Allow mapping a long and/or short argument name
    • Map to a corresponding configuration property
    • Specify whether the argument must take a value, may take a value (use an empty string for provided with no value), or does not take a value (set to true if present, leave off if absent)
    • Specify whether the argument value is singular or multiple, unconditionally quoting the provided value as a list in the latter case
  • Support - as a plain value (this usually indicates stdin/stdout both when given as an option value and when given as a regular argument)
  • Support -- as a terminator for options (subsequent arguments are treated plainly)
  • Provide a configuration property name to which the list of subsequent arguments is written, and a flag specifying whether more than one subsequent argument is allowed (the property would be quoted as a list if more then one value is allowed, and not quoted as a list if only one is allowed; the property would not be given if no argument is given); if subsequent arguments are forbidden (i.e. not mapped to a property), then providing a subsequent argument would be not well formed and thus would result in error, although -- should still be allowed
  • Fail fast if the arguments are not well formed according to the above when the configuration source is constructed with a given argument list

For reference & ideas: https://linux.die.net/man/3/getopt_long

Future requirements:

@gastaldi
Copy link
Contributor Author

I found the library that does what you're looking for: http://jopt-simple.github.io/jopt-simple/examples.html (it's the same used in OpenJDK). I'll close this PR and come up with a hopefully better solution

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