diff --git a/server/opts.go b/server/opts.go index b799c18a..33d48a06 100644 --- a/server/opts.go +++ b/server/opts.go @@ -27,6 +27,7 @@ import ( "regexp" "strconv" "strings" + "sync/atomic" "time" "github.com/nats-io/jwt" @@ -34,6 +35,20 @@ import ( "github.com/nats-io/nkeys" ) +var allowUnknownTopLevelField = int32(0) + +// NoErrOnUnknownFields can be used to change the behavior the processing +// of a configuration file. By default, an error is reported if unknown +// fields are found. If `noError` is set to true, no error will be reported +// if top-level unknown fields are found. +func NoErrOnUnknownFields(noError bool) { + var val int32 + if noError { + val = int32(1) + } + atomic.StoreInt32(&allowUnknownTopLevelField, val) +} + // ClusterOpts are options for clusters. // NOTE: This structure is no longer used for monitoring endpoints // and json tags are deprecated and may be removed in the future. @@ -693,7 +708,7 @@ func (o *Options) ProcessConfigFile(configFile string) error { case "reconnect_error_reports": o.ReconnectErrorReports = int(v.(int64)) default: - if !tk.IsUsedVariable() { + if au := atomic.LoadInt32(&allowUnknownTopLevelField); au == 0 && !tk.IsUsedVariable() { err := &unknownConfigFieldErr{ field: k, configErr: configErr{ diff --git a/server/opts_test.go b/server/opts_test.go index 070acd8e..5986e889 100644 --- a/server/opts_test.go +++ b/server/opts_test.go @@ -1819,3 +1819,44 @@ func TestLargeMaxPayload(t *testing.T) { t.Fatalf("Expected an error from too large of a max_payload entry") } } + +func TestHandleUnknownTopLevelConfigurationField(t *testing.T) { + conf := createConfFile(t, []byte(` + port: 1234 + streaming { + id: "me" + } + `)) + defer os.Remove(conf) + + // Verify that we get an error because of unknown "streaming" field. + opts := &Options{} + if err := opts.ProcessConfigFile(conf); err == nil || !strings.Contains(err.Error(), "streaming") { + t.Fatal("Expected error, got none") + } + + // Verify that if that is set, we get no error + NoErrOnUnknownFields(true) + defer NoErrOnUnknownFields(false) + + if err := opts.ProcessConfigFile(conf); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if opts.Port != 1234 { + t.Fatalf("Port was not parsed correctly: %v", opts.Port) + } + + // Verify that ignore works only on top level fields. + changeCurrentConfigContentWithNewContent(t, conf, []byte(` + port: 1234 + cluster { + non_top_level_unknown_field: 123 + } + streaming { + id: "me" + } + `)) + if err := opts.ProcessConfigFile(conf); err == nil || !strings.Contains(err.Error(), "non_top_level") { + t.Fatal("Expected error, got none") + } +}