Skip to content

Commit 195bac9

Browse files
authored
Fail the config validation if an empty node is set at root level (#3080)
Signed-off-by: Marco Pracucci <[email protected]>
1 parent cbaf36e commit 195bac9

File tree

5 files changed

+51
-9
lines changed

5 files changed

+51
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* [ENHANCEMENT] Add config validation to the experimental Alertmanager API. Invalid configs are no longer accepted. #3053
4646
* [ENHANCEMENT] Add "integration" as a label for `cortex_alertmanager_notifications_total` and `cortex_alertmanager_notifications_failed_total` metrics. #3056
4747
* [ENHANCEMENT] Add `cortex_ruler_config_last_reload_successful` and `cortex_ruler_config_last_reload_successful_seconds` to check status of users rule manager. #3056
48+
* [ENHANCEMENT] The configuration validation now fails if an empty YAML node has been set for a root YAML config property. #3080
4849
* [ENHANCEMENT] Memcached dial() calls now have an optional circuit-breaker to avoid hammering a broken cache #3051
4950
* [ENHANCEMENT] `-ruler.evaluation-delay-duration` is now overridable as a per-tenant limit, `ruler_evaluation_delay_duration`. #3098
5051
* [ENHANCEMENT] Add TLS support to etcd client. #3102

cmd/cortex/main.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ func main() {
106106
}
107107
}
108108

109+
// Validate the config once both the config file has been loaded
110+
// and CLI flags parsed.
111+
err = cfg.Validate(util.Logger)
112+
if err != nil {
113+
fmt.Fprintf(os.Stderr, "error validating config: %v\n", err)
114+
if !testMode {
115+
os.Exit(1)
116+
}
117+
}
118+
109119
// Continue on if -modules flag is given. Code handling the
110120
// -modules flag will not start cortex.
111121
if testMode && !cfg.ListModules {
@@ -118,13 +128,6 @@ func main() {
118128
}
119129

120130
util.InitLogger(&cfg.Server)
121-
// Validate the config once both the config file has been loaded
122-
// and CLI flags parsed.
123-
err = cfg.Validate(util.Logger)
124-
if err != nil {
125-
fmt.Printf("error validating config: %v\n", err)
126-
os.Exit(1)
127-
}
128131

129132
// Allocate a block of memory to alter GC behaviour. See https://github.com/golang/go/issues/23044
130133
ballast := make([]byte, ballastBytes)

cmd/cortex/main_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ func TestFlagParsing(t *testing.T) {
7575
stderrExcluded: "ingester\n",
7676
},
7777

78+
"root level configuration option specified as an empty node in YAML": {
79+
yaml: "querier:",
80+
stderrMessage: "the Querier configuration in YAML has been specified as an empty YAML node",
81+
},
82+
7883
// we cannot test the happy path, as cortex would then fully start
7984
} {
8085
t.Run(name, func(t *testing.T) {

pkg/chunk/encoding/factory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var (
2222
// RegisterFlags registers configuration settings.
2323
func (Config) RegisterFlags(f *flag.FlagSet) {
2424
f.Var(&DefaultEncoding, "ingester.chunk-encoding", "Encoding version to use for chunks.")
25-
flag.IntVar(&bigchunkSizeCapBytes, "store.bigchunk-size-cap-bytes", bigchunkSizeCapBytes, "When using bigchunk encoding, start a new bigchunk if over this size (0 = unlimited)")
25+
f.IntVar(&bigchunkSizeCapBytes, "store.bigchunk-size-cap-bytes", bigchunkSizeCapBytes, "When using bigchunk encoding, start a new bigchunk if over this size (0 = unlimited)")
2626
}
2727

2828
// Validate errors out if the encoding is set to Delta.

pkg/cortex/cortex.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"net/http"
99
"os"
10+
"reflect"
1011

1112
"github.com/go-kit/kit/log"
1213
"github.com/go-kit/kit/log/level"
@@ -43,6 +44,7 @@ import (
4344
"github.com/cortexproject/cortex/pkg/storegateway"
4445
"github.com/cortexproject/cortex/pkg/util"
4546
"github.com/cortexproject/cortex/pkg/util/fakeauth"
47+
"github.com/cortexproject/cortex/pkg/util/flagext"
4648
"github.com/cortexproject/cortex/pkg/util/grpc/healthcheck"
4749
"github.com/cortexproject/cortex/pkg/util/modules"
4850
"github.com/cortexproject/cortex/pkg/util/runtimeconfig"
@@ -143,12 +145,16 @@ func (c *Config) RegisterFlags(f *flag.FlagSet) {
143145
c.MemberlistKV.RegisterFlags(f, "")
144146

145147
// 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.")
147149
}
148150

149151
// Validate the cortex config and returns an error if the validation
150152
// doesn't pass
151153
func (c *Config) Validate(log log.Logger) error {
154+
if err := c.validateYAMLEmptyNodes(); err != nil {
155+
return err
156+
}
157+
152158
if err := c.Schema.Validate(); err != nil {
153159
return errors.Wrap(err, "invalid schema config")
154160
}
@@ -199,6 +205,33 @@ func (c *Config) Validate(log log.Logger) error {
199205
return nil
200206
}
201207

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+
202235
// Cortex is the root datastructure for Cortex.
203236
type Cortex struct {
204237
Cfg Config

0 commit comments

Comments
 (0)