Open ezracelli opened 1 year ago
Hello,
I confirm the bug, as a workaround, you have to define explicitly the default value of at least one of the fields.
Paerser handles different sources of data (mainly labels, but also files and env vars), and this diversity of sources induces complexity of the side effects.
No doubt parsing is a complex task! Would you accept a PR to fix this specifically for the file
parser?
Ahh, after testing a bit further, I think I see why this is an issue specifically for plugins.
Emtpy maps are only allowed via the "emptyMap"
label, which is fine for traefik's typed config. For example: https://github.com/traefik/traefik/blob/d97d3a6726ad6e50110dd2ff2b552a8b17eeed55/pkg/config/dynamic/http_config.go#L50
type Router struct {
// -- snip --
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
}
But since a plugin's config is deserialized as a PluginConf
(an untyped map[string]interface{}
) (https://github.com/traefik/traefik/blob/v2.9.6/pkg/config/dynamic/plugins.go#L8), paerser don't have any "hints" as to what type it's supposed to be, and for empty maps falls back to an empty string.
type Configuration struct {
Key map[string]interface{} `json:"key" toml:"key" yaml:"key"`
}
const yamlContent = `key:
emptyMap: {}
`
func TestYaml(t *testing.T) {
configuration := &Configuration{}
err := file.DecodeContent(yamlContent, ".yaml", configuration)
if err != nil {
t.Fatal(err)
}
assertEmptyMap(t, configuration)
}
func assertEmptyMap(t *testing.T, configuration *Configuration) {
t.Helper()
value := configuration.Key["emptyMap"]
assert.IsType(t, "", value) // PASS
assert.IsType(t, make(map[string]interface{}), value) // FAIL
}
So, I think this only affects deserialization of map[string]interface{}
types.
I think this is the same issue as https://github.com/traefik/traefik/issues/8772, which was unfortunately closed without resolution.
The following traefik dynamic configuration object:
Is deserialized as (copied from traefik's
--log.level=DEBUG
logs):variantA
sub-option is""
(empty string) instead of{}
(empty map) in the JSON serializationpaerser_test.go
Context
I'm writing a traefik plugin which accepts one of a few possible configuration variants, which I was planning on implementing in the "traefik way" by using the pattern of one of many map fields.
For example, traefik dynamic config accepts:
...where "variant" might be
addPrefix
(the type of the middleware); exactly one variant is accepted.In the same vein, I was structuring my plugin's config to be something like:
...where "variant" is one of the few possibilities mentioned above; exactly one variant is accepted. However, I ran into this issue because one of my "variants" doesn't have any sub-options, but the key is still required to be present. I believe this does not affect traefik because all its "variants" have sub-options.
plugin.go
With the following dynamic config (same as above):
...I get the following error message from traefik: