Unify and refactor config handling #1610
Labels
No labels
P0
P1
P2
P3
badger
frostfs-adm
frostfs-cli
frostfs-ir
frostfs-lens
frostfs-node
good first issue
triage
Infrastructure
blocked
bug
config
discussion
documentation
duplicate
enhancement
go
help wanted
internal
invalid
kludge
observability
perfomance
question
refactoring
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: TrueCloudLab/frostfs-node#1610
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
viper
library has a beautifulUnmarshal()
function8b223a45ce/README.md (L523)
It allows to take viper as-is (i.e. with all different config sources) and map it to a struct.
This should help us tremendously:
Config
with hand-rolled hierarchy handling, just use struct fields.To give an example:
This section in the markdown doc https://git.frostfs.info/TrueCloudLab/frostfs-node/src/branch/master/docs/storage-node-configuration.md#tls-subsection can be autogenerated from
I suggest making a POC with IR and then support it in node, cli and adm.
How to handle
shard.default
is not obvious, but is possible.Both
frostfs-node
andfrostfs-ir
can read configuration from files and environment variables.Unmarshal
can use environment variables but this feature is still experimental and hidden behind a build tag. I'd like to know, is that okay for us to enable an experimental feature?I compared the configuration from one of our virtual clusters and the example configuration. I found out the example is missing two sections:
audit
andkludge_compatibility_mode
.Moreover,
frostfs-ir
tries to readnode.kludge_compatibility_mode
instead of justkludge_compatibility_mode
. I have several questions:max_sleep_interval
be?TL;DR:
viper.Unmarshal
doesn't work well with slices when they come from environment variables. A workaround is needed.Problem
Consider this config file:
I want to load it into this Go struct:
How
viper.Unmarshal
works:yaml
/json
file into amap[string]any
.mapstructure
library to turn the map into a struct.This works fine for files. Now I want do define config via environment variables:
viper
has an experimental feature that lets it read environment variables while unmarshalling. When theviper_bind_struct
flag is on, it works as follows:mapstructure
, then flattens that map to generate a list of config keys, for example,control.grpc.endpoint
.yaml
/json
file into amap[string]any
and populates it with the keys got on step 1, using their corresponding values from environment variables.mapstructure
library to turn the map into the struct.However,
AuthorizedKeys
is a slice, and configuration keys for slice elements (likecontrol.authorized_keys.0.key
) are missing because there's no information about the slice length in step 1.This is the main problem.
viper.Unmarshal
works well foryaml
/json
filesBut it does not work for slices defined via environment variables.
Possible solutions
0. Do nothing
Keep things as they are and don't use
viper.Unmarshal
.1. Don't use environment variables for defining slices
Define slices only in config files.
2. Use decode hooks
mapstructure
allows defining custom hooks. A custom decode hook may look like this:But I think these hooks will be hard to write and maintain.
3. Specify keys for environment variable manually
I think we can read all environment variables with
os.Environ
and create a map with config variables to use when decoding. However, this can be hard because environment variables lack information about config hierarchy. For example,CONTROL_AUTHORIZED_KEYS
could be interpreted ascontrol.authorized_keys
,control.authorized.keys
,control_authorized.keys
, orcontrol_authorized_keys
. So, we'd need to preprocess the config struct with Go's reflect to get correct keys. I think the solution will be hard to implement and cumbersome.4. Unify the way we read configs and write a custom decoder
We use the same approach when reading configs. We manually call
viper.Get
for each config key. I think we can move config keys to struct tags, then walk the struct and do allviper.Get
calls based on the struct tags. For example, consider the following struct:after walking this struct with Go's reflection, we'll do all needed
viper.Get
calls like theseI made an example of how this can be implemented. It doesn't support slices of structs because it's still a bit challenge, but it already supports:
string
,int
,uint
,bool
and etc.)time.Duration
andnet.IP
Also it can returns errors if:
key
tag)default
tag)