Looking at the kubescheduler componentconfig (for example), I am thinking if we should differentiate between values that are unset vs values that are zero, thoughout all of componentconfig.
For example, this is good:
BindTimeoutSeconds *int64 `json:"bindTimeoutSeconds"`
When we don't set that, the field is omitted (well, it would be easy enough to add omitempty!), and it's clear that the field wasn't set by the user, and the default value should be used.
But... how can we tell the difference between "not set" and "false" here?
DisablePreemption bool `json:"disablePreemption"`
MetricsBindAddress string `json:"metricsBindAddress"`
metricsBindAddress defaults to
0.0.0.0:10251, which means that although the value "" appears, that value would actually mean "
0.0.0.0:10251". So how would a user turn off the metrics endpoint? We typically end up with magic values - like "0" meaning don't bind. Also this means when we are serving it, we probably would specify "
0.0.0.0:10251" and not "", but then that obscures the fact that this is in fact the default value.
Some observations:
- Today we can tell the difference if we use a non-standard yaml parser - but only if we are sourcing from a configmap or a CRD. But one day CRDs may be backed by protobuf or a comparable binary serializer, and we might not be able to differentiate. More problematically today, this also means a componentconfig object is not the canonical representation of the configuration; we need the yaml.
- It makes it harder to generate the configuration - we can't use the k8s structs if we want to differentiate between disablePreemption=false and =nil, or if we just want a compact configuration that only specifies a few values (particularly useful if we are one day overlaying these configurations).
- If the default value changes I suspect this makes life a lot harder (c.f. kube-proxy --resource-container flag, kubelet --allow-privileged flag)
- A policy of populating the default values has lead to user dissatisfaction with the size of the yaml - it's harder to see the settings that are changed from the default.
The following are easy changes that don't break existing yaml:
DisablePreemption *bool `json:"disablePreemption,omitempty"`
MetricsBindAddress *string `json:"metricsBindAddress,omitempty"`
I think having a consistent behaviour for the bind addresses that makes explicit and leaves room for additional options e.g. authn/authz would be nice, but would break existing yaml:
Metrics *EndpointConfiguration `json:"metrics,omitempty"`
type EndpointConfiguration struct {
Enabled *bool `json:"enabled,omitempty"`
BindAddress string `json:"bindAddress,omitempty"`
...
}
My proposal:
- We should today change all the `bool` -> `*bool`, `string` -> `*string`, `int32` -> `*int32` etc, and include `omitempty`. This should not break any yaml configurations, and it should give us more compact output, and the ability to differentiate not-set vs zero.
- We should look at structuring common behaviors using objects in the next alpha release of these types, in particular so that we have consistency and don't have magic values.