Skip to content

commands, config: tally and report unknown fields in config#636

Closed
odeke-em wants to merge 1 commit intotendermint:developfrom
orijtech:filter-out-unknown-config-fields
Closed

commands, config: tally and report unknown fields in config#636
odeke-em wants to merge 1 commit intotendermint:developfrom
orijtech:filter-out-unknown-config-fields

Conversation

@odeke-em
Copy link
Contributor

@odeke-em odeke-em commented Aug 31, 2017

Updates #628

With new flag --check-config, we can now tally fields from
the config file mapped to Go struct by tag "mapstructure",
with those parsed out by viper and report an error if we
find flags/fields that aren't accepted in the config.

Note: We use the --check-config flag to maintain backward/forward
compatibility with different versions of the file, as raised
by @ebuchman at
#636 (comment)

  • Exhibit:
    Given config in:
proxy_app = "tcp://127.0.0.1:46658"
moniker = "anonymous"
fast_sync = true
db_backend = "leveldb"
log_level = "state:info,*:error"

[rpc]
laddr = "tcp://0.0.0.0:46657"

[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = ""
trex = "hey"
picked = "bonjour"

Running:

$ tendermint --home ~/.tendermint node --config
ERROR: We've detected these unknown flags in your config file
p2p:
  picked
  trex

@ebuchman
Copy link
Contributor

ebuchman commented Sep 4, 2017

Question: should we be worried about future/backwards compatibility with this? Ie if the config struct changes, an old/new file could fail with a new/old version of tendermint.

Or maybe we make it a command to just tendermint init --verify or something which checks all files are properly formatted and all config options match up?

Also wondering if this is something that might be useful to PR into Viper itself ?

@ebuchman ebuchman added this to the 0.11.0 milestone Sep 5, 2017
@odeke-em
Copy link
Contributor Author

odeke-em commented Sep 6, 2017

Question: should we be worried about future/backwards compatibility with this? Ie if the config struct changes, an old/new file could fail with a new/old version of tendermint.

That's a great question. Thinking about it, in deed, that's gonna cause future compatibility problems. But as you've mentioned in your follow up suggestion to add the --verify flag, that cuts the deal. It makes sense to have a strict mode for compatibility violating behaviors.

We had talked offline about the PR into Viper, and the --verify behavior is the right direction towards getting that back into Viper. However, I think let's first roll with it within Tendermint and once we have some solid thoughts on how we can do it, make that PR to Viper as we had previously talked about.

odeke-em added a commit to tendermint/tmlibs that referenced this pull request Sep 6, 2017
For tendermint/tendermint#636
Updates tendermint/tendermint#628

Add global configuration flag `--verify` which is
used currently, primarily by Tendermint. Since
`cli/setup.go` has the definitions for Tendermint's
global configuration flags, place this one there too.
odeke-em added a commit to tendermint/tmlibs that referenced this pull request Sep 6, 2017
For tendermint/tendermint#636
Updates tendermint/tendermint#628

Add global configuration flag `--verify` which is
used currently, primarily by Tendermint. Since
`cli/setup.go` has the definitions for Tendermint's
global configuration flags, place this one there too.
@odeke-em
Copy link
Contributor Author

Requires tendermint/tmlibs#47 to be merged in first.

@ebuchman
Copy link
Contributor

Let's not put this in the upstream cli. Can we do it as a global hidden option on the tendermint command here? Maybe --check-config

https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://godoc.org/github.com/tendermint/tendermint)
[![chat](https://img.shields.io/badge/slack-join%20chat-pink.svg)](http://forum.tendermint.com:3000/)
[![Rocket.Chat](https://demo.rocket.chat/images/join-chat.svg)](https://cosmos.rocket.chat/)
Copy link
Contributor

Choose a reason for hiding this comment

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

@zramsay can u port this change to your readme update PR?


func TestParseInvalidConfig(t *testing.T) {
assert := assert.New(t)
cases := [...]struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

[] should work too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion @melekes, however in this case, when I get a failed test I'd like to easily jump to the index that failed, hence the [...] and subsequent 2: {}

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'll update this as agreed upon on in the meeting.

if !viper.GetBool(cli.VerifyFlag) {
return conf, nil
}
tagName := "mapstructure"
Copy link
Contributor

Choose a reason for hiding this comment

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

why create a var? and not inline the string

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was just an oversight. Thanks @melekes, yap I can inline that.

// Foo int `json:"foo"`
// Bar string `mapstructure:"bar"`
// recursively flattening anonymous struct fields by calling indexTaggedStructFields
// on them.
Copy link
Contributor

Choose a reason for hiding this comment

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

so how the result will look like? {tx.size: true}?

}
if len(splits) > 1 {
switch splits[1] {
case "omitempty":
Copy link
Contributor

Choose a reason for hiding this comment

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

what if both of them are present? omitempty,squash

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So far I haven't seen an inclusion of both in "mapstructure" as "squash" is the equivalent "omitempty" and the idea is that they are mutually exclusive.

Copy link
Contributor

Choose a reason for hiding this comment

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

We don't use both anywhere but I don't see why they would be mutually exclusive?

squash brings a substruct up to the current level in json serialization. omitempty leaves it out all together. I could imagine using both.

// Bar string `mapstructure:"bar"`
// recursively flattening anonymous struct fields by calling indexTaggedStructFields
// on them.
func indexTaggedStructFields(v interface{}, tagName string) map[string]interface{} {
Copy link
Contributor

Choose a reason for hiding this comment

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

have you tried to search for such func? maybe it exists in go or viper?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not in Go at least, nor in Viper from what I've checked in their godoc https://godoc.org/github.com/spf13/viper

func filterOutUnknownFields(want interface{}, gotFieldsMap map[string]interface{}, tagName string, excusedFlags map[string]map[string]bool) error {
wantFieldsMap := indexTaggedStructFields(want, tagName)
unpermittedFields := make(map[string][]string)
globalScope := "global"
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe this should be a const?

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 agree.

// A usecase is to report fields that were set in
// the config file for a command but are unexpected.
func filterOutUnknownFields(want interface{}, gotFieldsMap map[string]interface{}, tagName string, excusedFlags map[string]map[string]bool) error {
wantFieldsMap := indexTaggedStructFields(want, tagName)
Copy link
Contributor

Choose a reason for hiding this comment

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

don't you think it will be better to extract this call and pass wantFieldsMap to filterOutUnknownFields?

func filterOutUnknownFields(wantFieldsMap map[string]interface{}, gotFieldsMap map[string]interface{}, excusedFlags map[string]map[string]bool) error {

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 could, however we still need to pass in the tagName for the internal caller inside https://github.com/orijtech/tendermint/blob/11f3c811332779f438f45322b433b399711ca748/cmd/tendermint/commands/util.go#L93.
If that weren't the case, I agree, I'd actually be nicer to extract it out.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we simplify this by having indexTaggedStructFields grab all sub structs and put them under keys like p2p.laddr ? Then we don't need to call indexTaggedStructFields in filterOutUnknownFields and don't care about vars like specializedCommand. Then we can do what Anton suggested too.

@melekes
Copy link
Contributor

melekes commented Sep 18, 2017

The code looks a bit complicated to me. I've added a few comments. Maybe we should add more comments or split existing functions into smaller functions.

@codecov-io
Copy link

codecov-io commented Sep 21, 2017

Codecov Report

❗ No coverage uploaded for pull request base (config@abdc20e). Click here to learn what that means.
The diff coverage is 87.23%.

@@            Coverage Diff            @@
##             config     #636   +/-   ##
=========================================
  Coverage          ?   58.58%           
=========================================
  Files             ?       94           
  Lines             ?     9134           
  Branches          ?        0           
=========================================
  Hits              ?     5351           
  Misses            ?     3321           
  Partials          ?      462

@ebuchman
Copy link
Contributor

lets move this to next non breaking release so we can do more review

@ebuchman ebuchman removed this from the 0.11.0 milestone Sep 21, 2017
@zramsay zramsay changed the base branch from develop to config October 11, 2017 13:04
@ebuchman ebuchman mentioned this pull request Oct 24, 2017
5 tasks
@zramsay zramsay added this to the 0.12.0 milestone Oct 26, 2017
@zramsay
Copy link
Contributor

zramsay commented Oct 26, 2017

what are we doing with this?

Updates #628
Requires tendermint/tmlibs#47 first

With new flag `--check-config`, we can now tally fields from
the config file mapped to Go struct by tag "mapstructure",
with those parsed out by viper and report an error if we
find flags/fields that aren't accepted in the config.

Note: We use the `--check-config` flag to maintain backward/forward
compatibility with different versions of the file, as raised
by @ebuchman at
#636 (comment)

* Exhibit:
Given config in:
```shell
proxy_app = "tcp://127.0.0.1:46658"
moniker = "anonymous"
fast_sync = true
db_backend = "leveldb"
log_level = "state:info,*:error"

[rpc]
laddr = "tcp://0.0.0.0:46657"

[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = ""
trex = "hey"
picked = "bonjour"
```

Running:
```shell
$ tendermint --home ~/.tendermint node --check-config
ERROR: We've detected these unknown flags in your config file
p2p:
  picked
  trex

```
@zramsay zramsay changed the base branch from config to develop January 10, 2018 19:48
@zramsay
Copy link
Contributor

zramsay commented Jan 10, 2018

do we want to renew this PR now that #792 is merged?

@zramsay
Copy link
Contributor

zramsay commented Jan 11, 2018

I think it's a lot of extra code for not a lot of functionality gain. And I realized that now with #792, the whole config.toml is written with defaults, so accidentally adding a bad field is much less likely (the problem this PR was trying to solve).

@zramsay zramsay closed this Jan 11, 2018
@odeke-em odeke-em deleted the filter-out-unknown-config-fields branch May 11, 2021 13:11
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.

5 participants