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

tsh: ignore empty or non-existing config files #11495

Merged

Conversation

marcoandredinis
Copy link
Contributor

@marcoandredinis marcoandredinis commented Mar 28, 2022

If the newly created config.yaml didnt exist we would load the default
values and continue the flow
However, we were not resetting the err's value and would have an ERROR
message at the end and an invalid exit code

Most of the commands would reset that variable to the output of the
command's execution
One of them was not: tsh version
The version command has no return value, so the program would execute
as expected until the last statement: trace.Wrap(error) which would
re-use the err variable whose value is the result of the loadConfig
method.

We could either reset the errs value inside the PrintVersion switch
case block or reset it right after we check for IsNotFound.

We ended up picking the first option as it seems cleaner

 # before
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
ERROR: open /home/marco/.tsh/config/config.yaml: no such file or directory

 # after
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18

@github-actions github-actions bot added the tsh tsh - Teleport's command line tool for logging into nodes running Teleport. label Mar 28, 2022
@github-actions github-actions bot requested review from r0mant and russjones March 28, 2022 09:55
Copy link
Collaborator

@r0mant r0mant left a comment

Choose a reason for hiding this comment

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

@marcoandredinis Good catch! Have one comment.

tool/tsh/tsh.go Outdated
Comment on lines 691 to 694
confOptions, err := loadConfig(cf.HomePath)
if err != nil && !trace.IsNotFound(err) {
if trace.IsNotFound(err) {
err = nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hm, I wonder if it would be a little cleaner interface if we made loadConfig return an empty config in case the config file does not exist. Then the caller won't have to worry about handling the not found error. cc @lxea

Copy link
Contributor

Choose a reason for hiding this comment

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

That makes sense to me

Copy link
Collaborator

@r0mant r0mant left a comment

Choose a reason for hiding this comment

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

@marcoandredinis Also, please include test coverage with the change. If you take my suggestion, I think you can just write a test for loadConfig and make sure it doesn't return error if the file isn't found.

@marcoandredinis marcoandredinis force-pushed the marcoandredinis/tsh_unset_error_on_config_notfound branch from 727fb99 to df62670 Compare March 29, 2022 09:15
@marcoandredinis
Copy link
Contributor Author

Should we also get a valid config if file is empty? 🤔

Copy link
Collaborator

@r0mant r0mant left a comment

Choose a reason for hiding this comment

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

Should we also get a valid config if file is empty?

@marcoandredinis If yaml decoder fails with empty file, I'd return an empty config in that case also, yes. Otherwise lgtm.

@marcoandredinis marcoandredinis force-pushed the marcoandredinis/tsh_unset_error_on_config_notfound branch from 80f5d81 to ade6f70 Compare March 29, 2022 15:52
@marcoandredinis marcoandredinis requested a review from lxea March 29, 2022 16:59
@marcoandredinis marcoandredinis force-pushed the marcoandredinis/tsh_unset_error_on_config_notfound branch from a8d5f6a to 85cdae7 Compare March 29, 2022 17:01
@marcoandredinis marcoandredinis enabled auto-merge (squash) March 29, 2022 17:05
Copy link
Collaborator

@r0mant r0mant left a comment

Choose a reason for hiding this comment

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

Have some comments after the latest changes.

if err != nil {
return nil, trace.ConvertSystemError(err)
}
if len(bs) == 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: I'd use bytes.TrimSpace before comparing the length just in case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I ended up removing the empty/only spaces checks
yaml.Unmarshal handles that just fine

We initially added the check because we were decoding using yaml.NewDecoder(configFile).Decode(&cfg) and that requires at least one valid node: https://pkg.go.dev/gopkg.in/yaml.v2#Decoder.Decode
However, the Unmarshal is fine with empty input

cfg := TshConfig{}
err = yaml.NewDecoder(configFile).Decode(&cfg)
return &cfg, trace.Wrap(err)
if yaml.Unmarshal(bs, &cfg); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if yaml.Unmarshal(bs, &cfg); err != nil {
if err := yaml.Unmarshal(bs, &cfg); err != nil {

confPath := filepath.Join(profile.FullProfilePath(homePath), tshConfigPath)
configFile, err := os.Open(confPath)
func loadConfig(fullConfigPath string) (*TshConfig, error) {
emptyConfig := &TshConfig{}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: I think that usually you'd just return &TshConfig{}, nil inline, so you don't really need additional emptyConfig variable.

return nil, trace.ConvertSystemError(err)
}
defer configFile.Close()

bs, err := io.ReadAll(configFile)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not just use os.ReadFile instead of Open+ReadAll then?

@marcoandredinis marcoandredinis changed the title tsh: don't propagate error on config notfound tsh: ignore empty or non-existing config files Mar 30, 2022
Copy link
Contributor

@lxea lxea left a comment

Choose a reason for hiding this comment

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

Thanks!

Copy link
Collaborator

@r0mant r0mant left a comment

Choose a reason for hiding this comment

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

@marcoandredinis Let's merge and backport to branch/v9 please.

If the newly created config.yaml doesn't exist we load the default
values and continue the flow
However, we were not resetting the `err`'s value and would have an ERROR
message at the end and an invalid exit code

Most of the commands would reset that variable to the output of the
command's execution
One of them was not: `tsh version`
The version command has not return value, so the program would execute
as expected until the last statement: `trace.Wrap(error)` which would
re-use the `err` variable whose value is the result of the `loadConfig`
method.

We could clean up the `err`'s value or return no error at all and an
empty config when the file doesn't exist

We ended up picking the latter option as it seems cleaner

```
 # before
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
ERROR: open /home/marco/.tsh/config/config.yaml: no such file or directory

 # after
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
```
yaml.Unmarshal handles empty files just fine, no need to do prior checks for
empty or space only files
@marcoandredinis marcoandredinis force-pushed the marcoandredinis/tsh_unset_error_on_config_notfound branch from bddd3e0 to ee119d6 Compare March 30, 2022 15:04
@marcoandredinis marcoandredinis merged commit 83a32f4 into master Mar 30, 2022
@marcoandredinis marcoandredinis deleted the marcoandredinis/tsh_unset_error_on_config_notfound branch March 30, 2022 15:58
marcoandredinis added a commit that referenced this pull request Mar 30, 2022
If the newly created config.yaml didnt exist we would load the default
values and continue the flow
However, we were not resetting the `err`'s value and would have an ERROR
message at the end and an invalid exit code

Most of the commands would reset that variable to the output of the
command's execution
One of them was not: `tsh version`
The version command has no return value, so the program would execute
as expected until the last statement: `trace.Wrap(error)` which would
re-use the `err` variable whose value is the result of the `loadConfig`
method.

We could either reset the `err`s value inside the `PrintVersion` switch
case block or reset it right after we check for `IsNotFound`.

We ended up picking the first option as it seems cleaner

```
 # before
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
ERROR: open /home/marco/.tsh/config/config.yaml: no such file or directory

 # after
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
```
r0mant pushed a commit that referenced this pull request Mar 30, 2022
If the newly created config.yaml didnt exist we would load the default
values and continue the flow
However, we were not resetting the `err`'s value and would have an ERROR
message at the end and an invalid exit code

Most of the commands would reset that variable to the output of the
command's execution
One of them was not: `tsh version`
The version command has no return value, so the program would execute
as expected until the last statement: `trace.Wrap(error)` which would
re-use the `err` variable whose value is the result of the `loadConfig`
method.

We could either reset the `err`s value inside the `PrintVersion` switch
case block or reset it right after we check for `IsNotFound`.

We ended up picking the first option as it seems cleaner

```
 # before
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
ERROR: open /home/marco/.tsh/config/config.yaml: no such file or directory

 # after
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
```
@zmb3
Copy link
Collaborator

zmb3 commented Apr 5, 2022

@marcoandredinis Looks like this needs to go to branch/v8 as well.

@marcoandredinis
Copy link
Contributor Author

@zmb3 Indeed
On it

marcoandredinis added a commit that referenced this pull request Apr 5, 2022
If the newly created config.yaml didnt exist we would load the default
values and continue the flow
However, we were not resetting the `err`'s value and would have an ERROR
message at the end and an invalid exit code

Most of the commands would reset that variable to the output of the
command's execution
One of them was not: `tsh version`
The version command has no return value, so the program would execute
as expected until the last statement: `trace.Wrap(error)` which would
re-use the `err` variable whose value is the result of the `loadConfig`
method.

We could either reset the `err`s value inside the `PrintVersion` switch
case block or reset it right after we check for `IsNotFound`.

We ended up picking the first option as it seems cleaner

```
 # before
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
ERROR: open /home/marco/.tsh/config/config.yaml: no such file or directory

 # after
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
```
marcoandredinis added a commit that referenced this pull request Apr 5, 2022
* tsh: ignore empty or non-existing config files (#11495)

If the newly created config.yaml didnt exist we would load the default
values and continue the flow
However, we were not resetting the `err`'s value and would have an ERROR
message at the end and an invalid exit code

Most of the commands would reset that variable to the output of the
command's execution
One of them was not: `tsh version`
The version command has no return value, so the program would execute
as expected until the last statement: `trace.Wrap(error)` which would
re-use the `err` variable whose value is the result of the `loadConfig`
method.

We could either reset the `err`s value inside the `PrintVersion` switch
case block or reset it right after we check for `IsNotFound`.

We ended up picking the first option as it seems cleaner

```
 # before
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
ERROR: open /home/marco/.tsh/config/config.yaml: no such file or directory

 # after
$ make full > /dev/null; build/tsh version
Teleport v10.0.0-dev git:v8.0.0-alpha.1-899-g335adf1f4 go1.18
```

* fix tsh config test (#11603)

check for error when writing to a file
@webvictim webvictim mentioned this pull request Apr 19, 2022
@webvictim webvictim mentioned this pull request Jun 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tsh tsh - Teleport's command line tool for logging into nodes running Teleport.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants