|
7 | 7 | "fmt" |
8 | 8 | "net/http" |
9 | 9 | "os" |
| 10 | + "reflect" |
10 | 11 |
|
11 | 12 | "github.com/go-kit/kit/log" |
12 | 13 | "github.com/go-kit/kit/log/level" |
@@ -43,6 +44,7 @@ import ( |
43 | 44 | "github.com/cortexproject/cortex/pkg/storegateway" |
44 | 45 | "github.com/cortexproject/cortex/pkg/util" |
45 | 46 | "github.com/cortexproject/cortex/pkg/util/fakeauth" |
| 47 | + "github.com/cortexproject/cortex/pkg/util/flagext" |
46 | 48 | "github.com/cortexproject/cortex/pkg/util/grpc/healthcheck" |
47 | 49 | "github.com/cortexproject/cortex/pkg/util/modules" |
48 | 50 | "github.com/cortexproject/cortex/pkg/util/runtimeconfig" |
@@ -143,12 +145,16 @@ func (c *Config) RegisterFlags(f *flag.FlagSet) { |
143 | 145 | c.MemberlistKV.RegisterFlags(f, "") |
144 | 146 |
|
145 | 147 | // These don't seem to have a home. |
146 | | - flag.IntVar(&chunk_util.QueryParallelism, "querier.query-parallelism", 100, "Max subqueries run in parallel per higher-level query.") |
| 148 | + f.IntVar(&chunk_util.QueryParallelism, "querier.query-parallelism", 100, "Max subqueries run in parallel per higher-level query.") |
147 | 149 | } |
148 | 150 |
|
149 | 151 | // Validate the cortex config and returns an error if the validation |
150 | 152 | // doesn't pass |
151 | 153 | func (c *Config) Validate(log log.Logger) error { |
| 154 | + if err := c.validateYAMLEmptyNodes(); err != nil { |
| 155 | + return err |
| 156 | + } |
| 157 | + |
152 | 158 | if err := c.Schema.Validate(); err != nil { |
153 | 159 | return errors.Wrap(err, "invalid schema config") |
154 | 160 | } |
@@ -199,6 +205,33 @@ func (c *Config) Validate(log log.Logger) error { |
199 | 205 | return nil |
200 | 206 | } |
201 | 207 |
|
| 208 | +// validateYAMLEmptyNodes ensure that no empty node has been specified in the YAML config file. |
| 209 | +// When an empty node is defined in YAML, the YAML parser sets the whole struct to its zero value |
| 210 | +// and so we loose all default values. It's very difficult to detect this case for the user, so we |
| 211 | +// try to prevent it (on the root level) with this custom validation. |
| 212 | +func (c *Config) validateYAMLEmptyNodes() error { |
| 213 | + defaults := Config{} |
| 214 | + flagext.DefaultValues(&defaults) |
| 215 | + |
| 216 | + defStruct := reflect.ValueOf(defaults) |
| 217 | + cfgStruct := reflect.ValueOf(*c) |
| 218 | + |
| 219 | + // We expect all structs are the exact same. This check should never fail. |
| 220 | + if cfgStruct.NumField() != defStruct.NumField() { |
| 221 | + return errors.New("unable to validate configuration because of mismatching internal config data structure") |
| 222 | + } |
| 223 | + |
| 224 | + for i := 0; i < cfgStruct.NumField(); i++ { |
| 225 | + // If the struct has been reset due to empty YAML value and the zero struct value |
| 226 | + // doesn't match the default one, then we should warn the user about the issue. |
| 227 | + if cfgStruct.Field(i).Kind() == reflect.Struct && cfgStruct.Field(i).IsZero() && !defStruct.Field(i).IsZero() { |
| 228 | + return fmt.Errorf("the %s configuration in YAML has been specified as an empty YAML node", cfgStruct.Type().Field(i).Name) |
| 229 | + } |
| 230 | + } |
| 231 | + |
| 232 | + return nil |
| 233 | +} |
| 234 | + |
202 | 235 | // Cortex is the root datastructure for Cortex. |
203 | 236 | type Cortex struct { |
204 | 237 | Cfg Config |
|
0 commit comments