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
TOOLS-2779: Add --config option for password values #62
Conversation
options/options.go
Outdated
break | ||
} else if strings.HasPrefix(arg, "--config") { | ||
// No next argument or the next argument is an option. | ||
if i+1 == len(args) || strings.HasPrefix(args[i+1], "-") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One ambiguity I ran into was if the user does this:
$ mongodump --config -d myDB -c ...
Do we want to treat this as a missing config file, or try to open a file named -d
instead? I see validity in both, though I'm maybe leaning a bit towards the latter option of just trying to open it and letting the user deduce what happened from the os.Open()
error message.
Reason being that they could theoretically have a config file that starts with a hyphen:
$ mongodump --config -tools-config.yaml -d myDB -c ...
It's unlikely, but we'd either have to ensure the arg after --config
isn't an option (lot of manual checking) or force the user to rename -tools-config.yaml
to something else.
I can remove the || strings.HasPrefix(args[i+1], "-")
check if others agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good question, go-flags
has this behavior:
$ mongodump --sslPEMKeyFile -d
2020-11-25T18:58:47.875+0100 error parsing command line options: expected argument for flag `--sslPEMKeyFile', but got option `-d'
So I think disallowing files beginning with -
is consistent. If people do have files beginning with a -
, they can always use the --config="foo"
format. But I also think trying to open -d
is fine too. It would very quickly obvious what the problem is. If you prefer that approach, I think go for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I'm still leaning towards trying to open -d
just because otherwise the error message would be kinda hard to get right. Like if a user says --config -tools-config.yaml
the error would be --config option specified but no file given
, which would be confusing since they obviously did provide a file and might not think to use --config=
. We could separate out the conditions, but that's already more complexity than it's worth I feel.
Instead I'll add a bit of context to the ioutil.ReadFile()
error, so now it'll look like:
$ mongodump --config -d myDB
2020-11-25T14:26:20.168-0500 error parsing command line options: error opening file with --config: open -d: no such file or directory
If --config
is at the end, it'll stay the same:
$ mongodump -d myDB --config
2020-11-25T14:35:30.196-0500 error parsing command line options: --config option specified but no file given
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good tests. I have a couple of concerns about parsing the config option.
options/options.go
Outdated
break | ||
} else if strings.HasPrefix(arg, "--config") { | ||
// No next argument or the next argument is an option. | ||
if i+1 == len(args) || strings.HasPrefix(args[i+1], "-") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good question, go-flags
has this behavior:
$ mongodump --sslPEMKeyFile -d
2020-11-25T18:58:47.875+0100 error parsing command line options: expected argument for flag `--sslPEMKeyFile', but got option `-d'
So I think disallowing files beginning with -
is consistent. If people do have files beginning with a -
, they can always use the --config="foo"
format. But I also think trying to open -d
is fine too. It would very quickly obvious what the problem is. If you prefer that approach, I think go for it.
options/options.go
Outdated
} | ||
configPath = arg[9:] | ||
break | ||
} else if strings.HasPrefix(arg, "--config") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would match --configFOO
too. We need to do an exact match here.
options/options.go
Outdated
// Get config file path from the arguments, if specified. | ||
configPath := "" | ||
for i, arg := range args { | ||
if strings.HasPrefix(arg, "--config=") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there going to be a difference between --config=foo
and --config="foo"
? Will the latter make the config path be \"foo\"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, interesting point. It seems the underlying call to os.Open()
is smart enough to recognize that the surrounding quotation marks aren't intended unless they're explicitly escaped. So there should be no difference.
From trying it on the command line as-is:
$ echo "uri: mongodb://abc:@localhost:33333" > config.yaml
$ mongodump --config=config.yaml
Enter password:
$ mongodump --config="config.yaml"
Enter password:
$ mongodump --config=\"config.yaml\"
2020-11-25T14:19:02.953-0500 error parsing command line options: open "config.yaml": no such file or directory
$ mongodump --config="\"config.yaml\""
2020-11-25T14:19:12.025-0500 error parsing command line options: open "config.yaml": no such file or directory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's the call to os.Open
-- my understanding is that the shell consumes the quotes, so they are never passed as args in this specific example.
I think there are other issues here though. For example, does go-flags allow /
-style flags on windows? If so, that's a whole separate class of thing that we have to manually check for here.
As I have noted in another PR, I'm generally skeptical of trying to match the library's flag-parsing behavior by manually scanning arguments
options/options_test.go
Outdated
} | ||
} | ||
|
||
Convey(testCase.description, func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can just be Convey(testCase.description, assertion)
So(opts.ParseConfigFile(args), ShouldNotBeNil) | ||
}) | ||
|
||
Convey("should error with non-existent config file specified", t, func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the different positions here are overkill to just test a non-existent file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All seems good! Just a comment on the description of this option.
options/options.go
Outdated
Version bool `long:"version" description:"print the tool version and exit"` | ||
Help bool `long:"help" description:"print usage"` | ||
Version bool `long:"version" description:"print the tool version and exit"` | ||
ConfigPath string `long:"config" description:"path to a configuration file with password secrets"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering do we need with password secrets
since later we could introduce more fields inside?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I don't think we really need it either. We could also drop that phrase later on when we do support more/all fields, but I'm not too attached to it. I can go ahead and remove it if @rychipman agrees.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think removing it makes sense!
options/options.go
Outdated
// Get config file path from the arguments, if specified. | ||
configPath := "" | ||
for i, arg := range args { | ||
if strings.HasPrefix(arg, "--config=") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's the call to os.Open
-- my understanding is that the shell consumes the quotes, so they are never passed as args in this specific example.
I think there are other issues here though. For example, does go-flags allow /
-style flags on windows? If so, that's a whole separate class of thing that we have to manually check for here.
As I have noted in another PR, I'm generally skeptical of trying to match the library's flag-parsing behavior by manually scanning arguments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted the --config help menu phrase and changed out the manual command line parsing for the opts.parser.ParseArgs()
functionality. Should take care of a lot of what-ifs.
// Assign each parsed value to its respective ToolOptions field. | ||
opts.Auth.Password = config.Password | ||
opts.URI.ConnectionString = config.ConnectionString | ||
opts.SSL.SSLPEMKeyPassword = config.SSLPEMKeyPassword |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To bring up a comment I made in the tech design a while ago -- before storing each field here, we could actually check to see if the fields were already set by the call to opts.parser.ParseArgs()
at the beginning of this func. This would allow us to log a message that a config file's value will be overridden by the respective command line option.
Might offer a bit of transparency but I don't feel strongly either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't recommend doing that because the fields aren't necessarily directly set inside cmd parameters, for example, the password could be set inside connectionString. In that case, you won't be able to catch the overridden.
If you really wanna log the warning message, this function should return a separate ToolOptions object and later used as the comparison with the ParseArgs()
result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, it's true we wouldn't know if a password comes from --uri
vs. a positional connection string, but would that matter a lot?
It might be enough to just say something like warning: connection string in config file will be overridden by connection string on command line
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we already decided this wasn't a requirement for this project, I would also recommend that we not do this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
// Assign each parsed value to its respective ToolOptions field. | ||
opts.Auth.Password = config.Password | ||
opts.URI.ConnectionString = config.ConnectionString | ||
opts.SSL.SSLPEMKeyPassword = config.SSLPEMKeyPassword |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't recommend doing that because the fields aren't necessarily directly set inside cmd parameters, for example, the password could be set inside connectionString. In that case, you won't be able to catch the overridden.
If you really wanna log the warning message, this function should return a separate ToolOptions object and later used as the comparison with the ParseArgs()
result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
// Assign each parsed value to its respective ToolOptions field. | ||
opts.Auth.Password = config.Password | ||
opts.URI.ConnectionString = config.ConnectionString | ||
opts.SSL.SSLPEMKeyPassword = config.SSLPEMKeyPassword |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we already decided this wasn't a requirement for this project, I would also recommend that we not do this
Jira: https://jira.mongodb.org/browse/TOOLS-2779
This adds support for the
--config
option along with some unit tests underTestParseConfigFile
. Also vendors in thegopkg.in/yaml.v2
package.