mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Add -t pedantic config check to the server
Signed-off-by: Waldemar Quevedo <wally@synadia.com>
This commit is contained in:
119
conf/parse.go
119
conf/parse.go
@@ -51,13 +51,16 @@ type parser struct {
|
||||
|
||||
// The config file path, empty by default.
|
||||
fp string
|
||||
|
||||
// pedantic reports error when configuration is not correct.
|
||||
pedantic bool
|
||||
}
|
||||
|
||||
// Parse will return a map of keys to interface{}, although concrete types
|
||||
// underly them. The values supported are string, bool, int64, float64, DateTime.
|
||||
// Arrays and nested Maps are also supported.
|
||||
func Parse(data string) (map[string]interface{}, error) {
|
||||
p, err := parse(data, "")
|
||||
p, err := parse(data, "", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,20 +74,58 @@ func ParseFile(fp string) (map[string]interface{}, error) {
|
||||
return nil, fmt.Errorf("error opening config file: %v", err)
|
||||
}
|
||||
|
||||
p, err := parse(string(data), filepath.Dir(fp))
|
||||
p, err := parse(string(data), fp, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.mapping, nil
|
||||
}
|
||||
|
||||
func parse(data, fp string) (p *parser, err error) {
|
||||
func ParseFileWithChecks(fp string) (map[string]interface{}, error) {
|
||||
data, err := ioutil.ReadFile(fp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening config file: %v", err)
|
||||
}
|
||||
|
||||
p, err := parse(string(data), fp, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.mapping, nil
|
||||
}
|
||||
|
||||
type token struct {
|
||||
item item
|
||||
value interface{}
|
||||
usedVariable bool
|
||||
sourceFile string
|
||||
}
|
||||
|
||||
func (t *token) Value() interface{} {
|
||||
return t.value
|
||||
}
|
||||
|
||||
func (t *token) Line() int {
|
||||
return t.item.line
|
||||
}
|
||||
|
||||
func (t *token) IsUsedVariable() bool {
|
||||
return t.usedVariable
|
||||
}
|
||||
|
||||
func (t *token) SourceFile() string {
|
||||
return t.sourceFile
|
||||
}
|
||||
|
||||
func parse(data, fp string, pedantic bool) (p *parser, err error) {
|
||||
p = &parser{
|
||||
mapping: make(map[string]interface{}),
|
||||
lx: lex(data),
|
||||
ctxs: make([]interface{}, 0, 4),
|
||||
keys: make([]string, 0, 4),
|
||||
fp: fp,
|
||||
mapping: make(map[string]interface{}),
|
||||
lx: lex(data),
|
||||
ctxs: make([]interface{}, 0, 4),
|
||||
keys: make([]string, 0, 4),
|
||||
fp: filepath.Dir(fp),
|
||||
pedantic: pedantic,
|
||||
}
|
||||
p.pushContext(p.mapping)
|
||||
|
||||
@@ -93,7 +134,7 @@ func parse(data, fp string) (p *parser, err error) {
|
||||
if it.typ == itemEOF {
|
||||
break
|
||||
}
|
||||
if err := p.processItem(it); err != nil {
|
||||
if err := p.processItem(it, fp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -135,7 +176,15 @@ func (p *parser) popKey() string {
|
||||
return last
|
||||
}
|
||||
|
||||
func (p *parser) processItem(it item) error {
|
||||
func (p *parser) processItem(it item, fp string) error {
|
||||
setValue := func(it item, v interface{}) {
|
||||
if p.pedantic {
|
||||
p.setValue(&token{it, v, false, fp})
|
||||
} else {
|
||||
p.setValue(v)
|
||||
}
|
||||
}
|
||||
|
||||
switch it.typ {
|
||||
case itemError:
|
||||
return fmt.Errorf("Parse error on line %d: '%s'", it.line, it.val)
|
||||
@@ -145,9 +194,10 @@ func (p *parser) processItem(it item) error {
|
||||
newCtx := make(map[string]interface{})
|
||||
p.pushContext(newCtx)
|
||||
case itemMapEnd:
|
||||
p.setValue(p.popContext())
|
||||
setValue(it, p.popContext())
|
||||
case itemString:
|
||||
p.setValue(it.val) // FIXME(dlc) sanitize string?
|
||||
// FIXME(dlc) sanitize string?
|
||||
setValue(it, it.val)
|
||||
case itemInteger:
|
||||
lastDigit := 0
|
||||
for _, r := range it.val {
|
||||
@@ -167,21 +217,22 @@ func (p *parser) processItem(it item) error {
|
||||
}
|
||||
// Process a suffix
|
||||
suffix := strings.ToLower(strings.TrimSpace(it.val[lastDigit:]))
|
||||
|
||||
switch suffix {
|
||||
case "":
|
||||
p.setValue(num)
|
||||
setValue(it, num)
|
||||
case "k":
|
||||
p.setValue(num * 1000)
|
||||
setValue(it, num*1000)
|
||||
case "kb":
|
||||
p.setValue(num * 1024)
|
||||
setValue(it, num*1024)
|
||||
case "m":
|
||||
p.setValue(num * 1000 * 1000)
|
||||
setValue(it, num*1000*1000)
|
||||
case "mb":
|
||||
p.setValue(num * 1024 * 1024)
|
||||
setValue(it, num*1024*1024)
|
||||
case "g":
|
||||
p.setValue(num * 1000 * 1000 * 1000)
|
||||
setValue(it, num*1000*1000*1000)
|
||||
case "gb":
|
||||
p.setValue(num * 1024 * 1024 * 1024)
|
||||
setValue(it, num*1024*1024*1024)
|
||||
}
|
||||
case itemFloat:
|
||||
num, err := strconv.ParseFloat(it.val, 64)
|
||||
@@ -192,39 +243,55 @@ func (p *parser) processItem(it item) error {
|
||||
}
|
||||
return fmt.Errorf("Expected float, but got '%s'.", it.val)
|
||||
}
|
||||
p.setValue(num)
|
||||
setValue(it, num)
|
||||
case itemBool:
|
||||
switch strings.ToLower(it.val) {
|
||||
case "true", "yes", "on":
|
||||
p.setValue(true)
|
||||
setValue(it, true)
|
||||
case "false", "no", "off":
|
||||
p.setValue(false)
|
||||
setValue(it, false)
|
||||
default:
|
||||
return fmt.Errorf("Expected boolean value, but got '%s'.", it.val)
|
||||
}
|
||||
|
||||
case itemDatetime:
|
||||
dt, err := time.Parse("2006-01-02T15:04:05Z", it.val)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Expected Zulu formatted DateTime, but got '%s'.", it.val)
|
||||
}
|
||||
p.setValue(dt)
|
||||
setValue(it, dt)
|
||||
case itemArrayStart:
|
||||
var array = make([]interface{}, 0)
|
||||
p.pushContext(array)
|
||||
case itemArrayEnd:
|
||||
array := p.ctx
|
||||
p.popContext()
|
||||
p.setValue(array)
|
||||
setValue(it, array)
|
||||
case itemVariable:
|
||||
if value, ok := p.lookupVariable(it.val); ok {
|
||||
p.setValue(value)
|
||||
switch tk := value.(type) {
|
||||
case *token:
|
||||
// Mark that the variable was used.
|
||||
tk.usedVariable = true
|
||||
p.setValue(tk)
|
||||
default:
|
||||
p.setValue(value)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Variable reference for '%s' on line %d can not be found.",
|
||||
it.val, it.line)
|
||||
}
|
||||
case itemInclude:
|
||||
m, err := ParseFile(filepath.Join(p.fp, it.val))
|
||||
var (
|
||||
m map[string]interface{}
|
||||
err error
|
||||
)
|
||||
if p.pedantic {
|
||||
m, err = ParseFileWithChecks(filepath.Join(p.fp, it.val))
|
||||
} else {
|
||||
m, err = ParseFile(filepath.Join(p.fp, it.val))
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing include file '%s', %v.", it.val, err)
|
||||
}
|
||||
|
||||
4
main.go
4
main.go
@@ -33,6 +33,7 @@ Server Options:
|
||||
-c, --config <file> Configuration file
|
||||
-sl,--signal <signal>[=<pid>] Send signal to gnatsd process (stop, quit, reopen, reload)
|
||||
--client_advertise <string> Client URL to advertise to other servers
|
||||
-t Test configuration and exit
|
||||
|
||||
Logging Options:
|
||||
-l, --log <file> File to redirect log output
|
||||
@@ -87,6 +88,9 @@ func main() {
|
||||
server.PrintTLSHelpAndDie)
|
||||
if err != nil {
|
||||
server.PrintAndDie(err.Error())
|
||||
} else if opts.CheckConfig {
|
||||
fmt.Fprintf(os.Stderr, "configuration file %s test is successful\n", opts.ConfigFile)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Create the server with appropriate options.
|
||||
|
||||
519
server/config_check_test.go
Normal file
519
server/config_check_test.go
Normal file
@@ -0,0 +1,519 @@
|
||||
// Copyright 2018 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
// name is the name of the test.
|
||||
name string
|
||||
|
||||
// config is content of the configuration file.
|
||||
config string
|
||||
|
||||
// defaultErr is the error we get pedantic checks are not enabled.
|
||||
defaultErr error
|
||||
|
||||
// pedanticErr is the error we get when pedantic checks are enabled.
|
||||
pedanticErr error
|
||||
|
||||
// errorLine is the location of the error.
|
||||
errorLine int
|
||||
}{
|
||||
{
|
||||
name: "when unknown field is used at top level",
|
||||
config: `
|
||||
monitor = "127.0.0.1:4442"
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "monitor"`),
|
||||
errorLine: 2,
|
||||
},
|
||||
{
|
||||
name: "when default permissions are used at top level",
|
||||
config: `
|
||||
"default_permissions" {
|
||||
publish = ["_SANDBOX.>"]
|
||||
subscribe = ["_SANDBOX.>"]
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "default_permissions"`),
|
||||
|
||||
// NOTE: line number is '5' because it is where the map definition ends.
|
||||
errorLine: 5,
|
||||
},
|
||||
{
|
||||
name: "when authorization config is empty",
|
||||
config: `
|
||||
authorization = {
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: nil,
|
||||
},
|
||||
{
|
||||
name: "when authorization config has unknown fields",
|
||||
config: `
|
||||
authorization = {
|
||||
foo = "bar"
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "foo"`),
|
||||
errorLine: 3,
|
||||
},
|
||||
{
|
||||
name: "when authorization config has unknown fields",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
authorization = {
|
||||
user = "hello"
|
||||
foo = "bar"
|
||||
password = "world"
|
||||
}
|
||||
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "foo"`),
|
||||
errorLine: 6,
|
||||
},
|
||||
{
|
||||
name: "when user authorization config has unknown fields",
|
||||
config: `
|
||||
authorization = {
|
||||
users = [
|
||||
{
|
||||
user = "foo"
|
||||
pass = "bar"
|
||||
token = "quux"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "token"`),
|
||||
errorLine: 7,
|
||||
},
|
||||
{
|
||||
name: "when user authorization permissions config has unknown fields",
|
||||
config: `
|
||||
authorization {
|
||||
permissions {
|
||||
subscribe = {}
|
||||
inboxes = {}
|
||||
publish = {}
|
||||
}
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`Unknown field inboxes parsing permissions`),
|
||||
pedanticErr: errors.New(`unknown field "inboxes"`),
|
||||
errorLine: 5,
|
||||
},
|
||||
{
|
||||
name: "when user authorization permissions config has unknown fields within allow or deny",
|
||||
config: `
|
||||
authorization {
|
||||
permissions {
|
||||
subscribe = {
|
||||
allow = ["hello", "world"]
|
||||
deny = ["foo", "bar"]
|
||||
denied = "_INBOX.>"
|
||||
}
|
||||
publish = {}
|
||||
}
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`Unknown field name "denied" parsing subject permissions, only 'allow' or 'deny' are permitted`),
|
||||
pedanticErr: errors.New(`unknown field "denied"`),
|
||||
errorLine: 7,
|
||||
},
|
||||
{
|
||||
name: "when user authorization permissions config has unknown fields within allow or deny",
|
||||
config: `
|
||||
authorization {
|
||||
permissions {
|
||||
publish = {
|
||||
allow = ["hello", "world"]
|
||||
deny = ["foo", "bar"]
|
||||
allowed = "_INBOX.>"
|
||||
}
|
||||
subscribe = {}
|
||||
}
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`Unknown field name "allowed" parsing subject permissions, only 'allow' or 'deny' are permitted`),
|
||||
pedanticErr: errors.New(`unknown field "allowed"`),
|
||||
errorLine: 7,
|
||||
},
|
||||
{
|
||||
name: "when user authorization permissions config has unknown fields using arrays",
|
||||
config: `
|
||||
authorization {
|
||||
|
||||
default_permissions {
|
||||
subscribe = ["a"]
|
||||
publish = ["b"]
|
||||
inboxes = ["c"]
|
||||
}
|
||||
|
||||
users = [
|
||||
{
|
||||
user = "foo"
|
||||
pass = "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`Unknown field inboxes parsing permissions`),
|
||||
pedanticErr: errors.New(`unknown field "inboxes"`),
|
||||
errorLine: 7,
|
||||
},
|
||||
{
|
||||
name: "when user authorization permissions config has unknown fields using strings",
|
||||
config: `
|
||||
authorization {
|
||||
|
||||
default_permissions {
|
||||
subscribe = "a"
|
||||
requests = "b"
|
||||
publish = "c"
|
||||
}
|
||||
|
||||
users = [
|
||||
{
|
||||
user = "foo"
|
||||
pass = "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`Unknown field requests parsing permissions`),
|
||||
pedanticErr: errors.New(`unknown field "requests"`),
|
||||
errorLine: 6,
|
||||
},
|
||||
{
|
||||
name: "when user authorization permissions config is empty",
|
||||
config: `
|
||||
authorization = {
|
||||
users = [
|
||||
{
|
||||
user = "foo", pass = "bar", permissions = {
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: nil,
|
||||
},
|
||||
{
|
||||
name: "when unknown permissions are included in config",
|
||||
config: `
|
||||
authorization = {
|
||||
users = [
|
||||
{
|
||||
user = "foo", pass = "bar", permissions {
|
||||
inboxes = true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`Unknown field inboxes parsing permissions`),
|
||||
pedanticErr: errors.New(`unknown field "inboxes"`),
|
||||
errorLine: 6,
|
||||
},
|
||||
{
|
||||
name: "when clustering config is empty",
|
||||
config: `
|
||||
cluster = {
|
||||
}
|
||||
`,
|
||||
|
||||
defaultErr: nil,
|
||||
pedanticErr: nil,
|
||||
},
|
||||
{
|
||||
name: "when unknown option is in clustering config",
|
||||
config: `
|
||||
# NATS Server Configuration
|
||||
port = 4222
|
||||
|
||||
cluster = {
|
||||
|
||||
port = 6222
|
||||
|
||||
foo = "bar"
|
||||
|
||||
authorization {
|
||||
user = "hello"
|
||||
pass = "world"
|
||||
}
|
||||
|
||||
}
|
||||
`,
|
||||
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "foo"`),
|
||||
errorLine: 9,
|
||||
},
|
||||
{
|
||||
name: "when unknown option is in clustering authorization config",
|
||||
config: `
|
||||
cluster = {
|
||||
authorization {
|
||||
foo = "bar"
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "foo"`),
|
||||
errorLine: 4,
|
||||
},
|
||||
{
|
||||
name: "when unknown option is in clustering authorization permissions config",
|
||||
config: `
|
||||
cluster = {
|
||||
authorization {
|
||||
user = "foo"
|
||||
pass = "bar"
|
||||
permissions = {
|
||||
hello = "world"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`Unknown field hello parsing permissions`),
|
||||
pedanticErr: errors.New(`unknown field "hello"`),
|
||||
errorLine: 7,
|
||||
},
|
||||
{
|
||||
name: "when unknown option is in tls config",
|
||||
config: `
|
||||
tls = {
|
||||
hello = "world"
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`error parsing tls config, unknown field ["hello"]`),
|
||||
pedanticErr: errors.New(`unknown field "hello"`),
|
||||
errorLine: 3,
|
||||
},
|
||||
{
|
||||
name: "when unknown option is in cluster tls config",
|
||||
config: `
|
||||
cluster {
|
||||
tls = {
|
||||
foo = "bar"
|
||||
}
|
||||
}
|
||||
`,
|
||||
// Backwards compatibility: also report error by default even if pedantic checks disabled.
|
||||
defaultErr: errors.New(`error parsing tls config, unknown field ["foo"]`),
|
||||
pedanticErr: errors.New(`unknown field "foo"`),
|
||||
errorLine: 4,
|
||||
},
|
||||
{
|
||||
name: "when using cipher suites in the TLS config",
|
||||
config: `
|
||||
tls = {
|
||||
cipher_suites: [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
preferences = []
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`error parsing tls config, unknown field ["preferences"]`),
|
||||
pedanticErr: errors.New(`unknown field "preferences"`),
|
||||
errorLine: 7,
|
||||
},
|
||||
{
|
||||
name: "when using curve preferences in the TLS config",
|
||||
config: `
|
||||
tls = {
|
||||
curve_preferences: [
|
||||
"CurveP256",
|
||||
"CurveP384",
|
||||
"CurveP521"
|
||||
]
|
||||
suites = []
|
||||
}
|
||||
`,
|
||||
defaultErr: errors.New(`error parsing tls config, unknown field ["suites"]`),
|
||||
pedanticErr: errors.New(`unknown field "suites"`),
|
||||
errorLine: 8,
|
||||
},
|
||||
{
|
||||
name: "when unknown option is in cluster config with defined routes",
|
||||
config: `
|
||||
cluster {
|
||||
port = 6222
|
||||
routes = [
|
||||
nats://127.0.0.1:6222
|
||||
]
|
||||
peers = []
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "peers"`),
|
||||
errorLine: 7,
|
||||
},
|
||||
{
|
||||
name: "when used as variable in authorization block it should not be considered as unknown field",
|
||||
config: `
|
||||
# listen: 127.0.0.1:-1
|
||||
listen: 127.0.0.1:4222
|
||||
|
||||
authorization {
|
||||
# Superuser can do anything.
|
||||
super_user = {
|
||||
publish = ">"
|
||||
subscribe = ">"
|
||||
}
|
||||
|
||||
# Can do requests on foo or bar, and subscribe to anything
|
||||
# that is a response to an _INBOX.
|
||||
#
|
||||
# Notice that authorization filters can be singletons or arrays.
|
||||
req_pub_user = {
|
||||
publish = ["req.foo", "req.bar"]
|
||||
subscribe = "_INBOX.>"
|
||||
}
|
||||
|
||||
# Setup a default user that can subscribe to anything, but has
|
||||
# no publish capabilities.
|
||||
default_user = {
|
||||
subscribe = "PUBLIC.>"
|
||||
}
|
||||
|
||||
unused = "hello"
|
||||
|
||||
# Default permissions if none presented. e.g. susan below.
|
||||
default_permissions: $default_user
|
||||
|
||||
# Users listed with persmissions.
|
||||
users = [
|
||||
{user: alice, password: foo, permissions: $super_user}
|
||||
{user: bob, password: bar, permissions: $req_pub_user}
|
||||
{user: susan, password: baz}
|
||||
]
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: errors.New(`unknown field "unused"`),
|
||||
errorLine: 27,
|
||||
},
|
||||
{
|
||||
name: "when used as variable in top level config it should not be considered as unknown field",
|
||||
config: `
|
||||
monitoring_port = 8222
|
||||
|
||||
http_port = $monitoring_port
|
||||
|
||||
port = 4222
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: nil,
|
||||
},
|
||||
{
|
||||
name: "when used as variable in cluster config it should not be considered as unknown field",
|
||||
config: `
|
||||
cluster {
|
||||
clustering_port = 6222
|
||||
port = $clustering_port
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
checkConfig := func(config string, pedantic bool) error {
|
||||
opts := &Options{
|
||||
CheckConfig: pedantic,
|
||||
}
|
||||
return opts.ProcessConfigFile(config)
|
||||
}
|
||||
|
||||
checkErr := func(t *testing.T, err, expectedErr error) {
|
||||
t.Helper()
|
||||
switch {
|
||||
case err == nil && expectedErr == nil:
|
||||
// OK
|
||||
case err != nil && expectedErr == nil:
|
||||
t.Errorf("Unexpected error after processing config: %s", err)
|
||||
case err == nil && expectedErr != nil:
|
||||
t.Errorf("Expected %q error after processing invalid config but got nothing", expectedErr)
|
||||
}
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
conf := createConfFile(t, []byte(test.config))
|
||||
defer os.Remove(conf)
|
||||
|
||||
t.Run("with pedantic check enabled", func(t *testing.T) {
|
||||
err := checkConfig(conf, true)
|
||||
expectedErr := test.pedanticErr
|
||||
if err != nil && expectedErr != nil {
|
||||
msg := fmt.Sprintf("%s in %s:%d", expectedErr.Error(), conf, test.errorLine)
|
||||
if err.Error() != msg {
|
||||
t.Errorf("Expected %q, got %q", msg, err.Error())
|
||||
}
|
||||
}
|
||||
checkErr(t, err, test.pedanticErr)
|
||||
})
|
||||
|
||||
t.Run("with pedantic check disabled", func(t *testing.T) {
|
||||
err := checkConfig(conf, false)
|
||||
expectedErr := test.defaultErr
|
||||
if err != nil && expectedErr != nil && err.Error() != expectedErr.Error() {
|
||||
t.Errorf("Expected %q, got %q", expectedErr.Error(), err.Error())
|
||||
}
|
||||
checkErr(t, err, test.defaultErr)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigCheckIncludes(t *testing.T) {
|
||||
// Check happy path first using pedantic mode.
|
||||
opts := &Options{
|
||||
CheckConfig: true,
|
||||
}
|
||||
err := opts.ProcessConfigFile("./configs/include_conf_check_a.conf")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error processing include files with configuration check enabled: %s", err)
|
||||
}
|
||||
|
||||
opts = &Options{
|
||||
CheckConfig: true,
|
||||
}
|
||||
err = opts.ProcessConfigFile("./configs/include_bad_conf_check_a.conf")
|
||||
if err == nil {
|
||||
t.Errorf("Expected error processing include files with configuration check enabled: %s", err)
|
||||
}
|
||||
expectedErr := errors.New(`unknown field "monitoring_port" in configs/include_bad_conf_check_b.conf:2`)
|
||||
if err != nil && expectedErr != nil && err.Error() != expectedErr.Error() {
|
||||
t.Errorf("Expected %q, got %q", expectedErr.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
6
server/configs/include_bad_conf_check_a.conf
Normal file
6
server/configs/include_bad_conf_check_a.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
port = 4222
|
||||
|
||||
include "include_bad_conf_check_b.conf"
|
||||
|
||||
# http_port = $monitoring_port
|
||||
4
server/configs/include_bad_conf_check_b.conf
Normal file
4
server/configs/include_bad_conf_check_b.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
monitoring_port = 8222
|
||||
|
||||
include "include_conf_check_c.conf"
|
||||
6
server/configs/include_conf_check_a.conf
Normal file
6
server/configs/include_conf_check_a.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
port = 4222
|
||||
|
||||
include "include_conf_check_b.conf"
|
||||
|
||||
http_port = $monitoring_port
|
||||
3
server/configs/include_conf_check_b.conf
Normal file
3
server/configs/include_conf_check_b.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
monitoring_port = 8222
|
||||
|
||||
include "include_conf_check_c.conf"
|
||||
5
server/configs/include_conf_check_c.conf
Normal file
5
server/configs/include_conf_check_c.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
authorization {
|
||||
user = "foo"
|
||||
pass = "bar"
|
||||
}
|
||||
311
server/opts.go
311
server/opts.go
@@ -96,6 +96,9 @@ type Options struct {
|
||||
|
||||
CustomClientAuthentication Authentication `json:"-"`
|
||||
CustomRouterAuthentication Authentication `json:"-"`
|
||||
|
||||
// CheckConfig enables pedantic configuration file syntax checks.
|
||||
CheckConfig bool `json:"-"`
|
||||
}
|
||||
|
||||
// Clone performs a deep copy of the Options struct, returning a new clone
|
||||
@@ -186,6 +189,27 @@ e.g.
|
||||
Available cipher suites include:
|
||||
`
|
||||
|
||||
type token interface {
|
||||
Value() interface{}
|
||||
Line() int
|
||||
IsUsedVariable() bool
|
||||
SourceFile() string
|
||||
}
|
||||
|
||||
type unknownConfigFieldErr struct {
|
||||
field string
|
||||
token token
|
||||
configFile string
|
||||
}
|
||||
|
||||
func (e *unknownConfigFieldErr) Error() string {
|
||||
msg := fmt.Sprintf("unknown field %q", e.field)
|
||||
if e.token != nil {
|
||||
return msg + fmt.Sprintf(" in %s:%d", e.configFile, e.token.Line())
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// ProcessConfigFile processes a configuration file.
|
||||
// FIXME(dlc): Hacky
|
||||
func ProcessConfigFile(configFile string) (*Options, error) {
|
||||
@@ -196,6 +220,18 @@ func ProcessConfigFile(configFile string) (*Options, error) {
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// unwrapValue can be used to get the token and value from an item
|
||||
// to be able to report the line number in case of an incorrect
|
||||
// configuration.
|
||||
func unwrapValue(v interface{}) (token, interface{}) {
|
||||
switch tk := v.(type) {
|
||||
case token:
|
||||
return tk, tk.Value()
|
||||
default:
|
||||
return nil, v
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessConfigFile updates the Options structure with options
|
||||
// present in the given configuration file.
|
||||
// This version is convenient if one wants to set some default
|
||||
@@ -216,12 +252,24 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
m, err := conf.ParseFile(configFile)
|
||||
var (
|
||||
m map[string]interface{}
|
||||
tk token
|
||||
pedantic bool = o.CheckConfig
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
m, err = conf.ParseFileWithChecks(configFile)
|
||||
} else {
|
||||
m, err = conf.ParseFile(configFile)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for k, v := range m {
|
||||
// When pedantic checks are enabled then need to unwrap
|
||||
// to get the value along with reported error line.
|
||||
tk, v = unwrapValue(v)
|
||||
switch strings.ToLower(k) {
|
||||
case "listen":
|
||||
hp, err := parseListen(v)
|
||||
@@ -243,8 +291,12 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
case "logtime":
|
||||
o.Logtime = v.(bool)
|
||||
case "authorization":
|
||||
am := v.(map[string]interface{})
|
||||
auth, err := parseAuthorization(am)
|
||||
var auth *authorization
|
||||
if pedantic {
|
||||
auth, err = parseAuthorization(tk, o)
|
||||
} else {
|
||||
auth, err = parseAuthorization(v, o)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,8 +340,13 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
case "https_port":
|
||||
o.HTTPSPort = int(v.(int64))
|
||||
case "cluster":
|
||||
cm := v.(map[string]interface{})
|
||||
if err := parseCluster(cm, o); err != nil {
|
||||
var err error
|
||||
if pedantic {
|
||||
err = parseCluster(tk, o)
|
||||
} else {
|
||||
err = parseCluster(v, o)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "logfile", "log_file":
|
||||
@@ -319,8 +376,15 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
case "ping_max":
|
||||
o.MaxPingsOut = int(v.(int64))
|
||||
case "tls":
|
||||
tlsm := v.(map[string]interface{})
|
||||
tc, err := parseTLS(tlsm)
|
||||
var (
|
||||
tc *TLSConfigOpts
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
tc, err = parseTLS(tk, o)
|
||||
} else {
|
||||
tc, err = parseTLS(v, o)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -342,6 +406,14 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
o.WriteDeadline = time.Duration(v.(int64)) * time.Second
|
||||
fmt.Printf("WARNING: write_deadline should be converted to a duration\n")
|
||||
}
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -375,8 +447,17 @@ func parseListen(v interface{}) (*hostPort, error) {
|
||||
}
|
||||
|
||||
// parseCluster will parse the cluster config.
|
||||
func parseCluster(cm map[string]interface{}, opts *Options) error {
|
||||
func parseCluster(v interface{}, opts *Options) error {
|
||||
var (
|
||||
cm map[string]interface{}
|
||||
tk token
|
||||
pedantic bool = opts.CheckConfig
|
||||
)
|
||||
_, v = unwrapValue(v)
|
||||
cm = v.(map[string]interface{})
|
||||
for mk, mv := range cm {
|
||||
// Again, unwrap token value if line check is required.
|
||||
tk, mv = unwrapValue(mv)
|
||||
switch strings.ToLower(mk) {
|
||||
case "listen":
|
||||
hp, err := parseListen(mv)
|
||||
@@ -390,8 +471,15 @@ func parseCluster(cm map[string]interface{}, opts *Options) error {
|
||||
case "host", "net":
|
||||
opts.Cluster.Host = mv.(string)
|
||||
case "authorization":
|
||||
am := mv.(map[string]interface{})
|
||||
auth, err := parseAuthorization(am)
|
||||
var (
|
||||
auth *authorization
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
auth, err = parseAuthorization(tk, opts)
|
||||
} else {
|
||||
auth, err = parseAuthorization(mv, opts)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -409,6 +497,7 @@ func parseCluster(cm map[string]interface{}, opts *Options) error {
|
||||
ra := mv.([]interface{})
|
||||
opts.Routes = make([]*url.URL, 0, len(ra))
|
||||
for _, r := range ra {
|
||||
_, r = unwrapValue(r)
|
||||
routeURL := r.(string)
|
||||
url, err := url.Parse(routeURL)
|
||||
if err != nil {
|
||||
@@ -417,8 +506,15 @@ func parseCluster(cm map[string]interface{}, opts *Options) error {
|
||||
opts.Routes = append(opts.Routes, url)
|
||||
}
|
||||
case "tls":
|
||||
tlsm := mv.(map[string]interface{})
|
||||
tc, err := parseTLS(tlsm)
|
||||
var (
|
||||
tc *TLSConfigOpts
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
tc, err = parseTLS(tk, opts)
|
||||
} else {
|
||||
tc, err = parseTLS(mv, opts)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -442,12 +538,20 @@ func parseCluster(cm map[string]interface{}, opts *Options) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected permissions to be a map/struct, got %+v", mv)
|
||||
}
|
||||
perms, err := parseUserPermissions(pm)
|
||||
perms, err := parseUserPermissions(pm, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// This will possibly override permissions that were define in auth block
|
||||
setClusterPermissions(&opts.Cluster, perms)
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -468,9 +572,19 @@ func setClusterPermissions(opts *ClusterOpts, perms *Permissions) {
|
||||
}
|
||||
|
||||
// Helper function to parse Authorization configs.
|
||||
func parseAuthorization(am map[string]interface{}) (*authorization, error) {
|
||||
auth := &authorization{}
|
||||
func parseAuthorization(v interface{}, opts *Options) (*authorization, error) {
|
||||
var (
|
||||
am map[string]interface{}
|
||||
tk token
|
||||
pedantic bool = opts.CheckConfig
|
||||
auth *authorization = &authorization{}
|
||||
)
|
||||
|
||||
// Unwrap value first if pedantic config check enabled.
|
||||
_, v = unwrapValue(v)
|
||||
am = v.(map[string]interface{})
|
||||
for mk, mv := range am {
|
||||
tk, mv = unwrapValue(mv)
|
||||
switch strings.ToLower(mk) {
|
||||
case "user", "username":
|
||||
auth.user = mv.(string)
|
||||
@@ -488,22 +602,43 @@ func parseAuthorization(am map[string]interface{}) (*authorization, error) {
|
||||
}
|
||||
auth.timeout = at
|
||||
case "users":
|
||||
nkeys, users, err := parseUsers(mv)
|
||||
var (
|
||||
users []*User
|
||||
err error
|
||||
nkeys []*NkeyUser
|
||||
)
|
||||
if pedantic {
|
||||
nkeys, users, err = parseUsers(tk, opts)
|
||||
} else {
|
||||
nkeys, users, err = parseUsers(mv, opts)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth.users = users
|
||||
auth.nkeys = nkeys
|
||||
case "default_permission", "default_permissions", "permissions":
|
||||
pm, ok := mv.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Expected default permissions to be a map/struct, got %+v", mv)
|
||||
var (
|
||||
permissions *Permissions
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
permissions, err = parseUserPermissions(tk, opts)
|
||||
} else {
|
||||
permissions, err = parseUserPermissions(mv, opts)
|
||||
}
|
||||
permissions, err := parseUserPermissions(pm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth.defaultPermissions = permissions
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now check for permission defaults with multiple users, etc.
|
||||
@@ -520,25 +655,39 @@ func parseAuthorization(am map[string]interface{}) (*authorization, error) {
|
||||
}
|
||||
|
||||
// Helper function to parse multiple users array with optional permissions.
|
||||
func parseUsers(mv interface{}) ([]*NkeyUser, []*User, error) {
|
||||
func parseUsers(mv interface{}, opts *Options) ([]*NkeyUser, []*User, error) {
|
||||
var (
|
||||
tk token
|
||||
pedantic bool = opts.CheckConfig
|
||||
users []*User = []*User{}
|
||||
keys []*NkeyUser
|
||||
)
|
||||
_, mv = unwrapValue(mv)
|
||||
|
||||
// Make sure we have an array
|
||||
uv, ok := mv.([]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Expected users field to be an array, got %v", mv)
|
||||
}
|
||||
var users []*User
|
||||
var keys []*NkeyUser
|
||||
for _, u := range uv {
|
||||
_, u = unwrapValue(u)
|
||||
|
||||
// Check its a map/struct
|
||||
um, ok := u.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Expected user entry to be a map/struct, got %v", u)
|
||||
}
|
||||
var perms *Permissions
|
||||
var err error
|
||||
user := &User{}
|
||||
nkey := &NkeyUser{}
|
||||
|
||||
var (
|
||||
user *User = &User{}
|
||||
nkey *NkeyUser = &NkeyUser{}
|
||||
perms *Permissions
|
||||
err error
|
||||
)
|
||||
for k, v := range um {
|
||||
// Also needs to unwrap first
|
||||
tk, v = unwrapValue(v)
|
||||
|
||||
switch strings.ToLower(k) {
|
||||
case "nkey":
|
||||
nkey.Nkey = v.(string)
|
||||
@@ -547,14 +696,22 @@ func parseUsers(mv interface{}) ([]*NkeyUser, []*User, error) {
|
||||
case "pass", "password":
|
||||
user.Password = v.(string)
|
||||
case "permission", "permissions", "authorization":
|
||||
pm, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Expected user permissions to be a map/struct, got %+v", v)
|
||||
if pedantic {
|
||||
perms, err = parseUserPermissions(tk, opts)
|
||||
} else {
|
||||
perms, err = parseUserPermissions(v, opts)
|
||||
}
|
||||
perms, err = parseUserPermissions(pm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, nil, &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Place perms if we have them.
|
||||
@@ -588,38 +745,58 @@ func parseUsers(mv interface{}) ([]*NkeyUser, []*User, error) {
|
||||
}
|
||||
|
||||
// Helper function to parse user/account permissions
|
||||
func parseUserPermissions(pm map[string]interface{}) (*Permissions, error) {
|
||||
p := &Permissions{}
|
||||
func parseUserPermissions(mv interface{}, opts *Options) (*Permissions, error) {
|
||||
var (
|
||||
tk token
|
||||
pedantic bool = opts.CheckConfig
|
||||
p *Permissions = &Permissions{}
|
||||
)
|
||||
_, mv = unwrapValue(mv)
|
||||
pm, ok := mv.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Expected permissions to be a map/struct, got %+v", mv)
|
||||
}
|
||||
for k, v := range pm {
|
||||
tk, v = unwrapValue(v)
|
||||
|
||||
switch strings.ToLower(k) {
|
||||
// For routes:
|
||||
// Import is Publish
|
||||
// Export is Subscribe
|
||||
case "pub", "publish", "import":
|
||||
perms, err := parseVariablePermissions(v)
|
||||
perms, err := parseVariablePermissions(v, opts)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Publish = perms
|
||||
case "sub", "subscribe", "export":
|
||||
perms, err := parseVariablePermissions(v)
|
||||
perms, err := parseVariablePermissions(v, opts)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Subscribe = perms
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown field %s parsing permissions", k)
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Tope level parser for authorization configurations.
|
||||
func parseVariablePermissions(v interface{}) (*SubjectPermission, error) {
|
||||
switch v.(type) {
|
||||
// Top level parser for authorization configurations.
|
||||
func parseVariablePermissions(v interface{}, opts *Options) (*SubjectPermission, error) {
|
||||
switch vv := v.(type) {
|
||||
case map[string]interface{}:
|
||||
// New style with allow and/or deny properties.
|
||||
return parseSubjectPermission(v.(map[string]interface{}))
|
||||
return parseSubjectPermission(vv, opts)
|
||||
default:
|
||||
// Old style
|
||||
return parseOldPermissionStyle(v)
|
||||
@@ -629,13 +806,15 @@ func parseVariablePermissions(v interface{}) (*SubjectPermission, error) {
|
||||
// Helper function to parse subject singeltons and/or arrays
|
||||
func parseSubjects(v interface{}) ([]string, error) {
|
||||
var subjects []string
|
||||
switch v.(type) {
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
subjects = append(subjects, v.(string))
|
||||
subjects = append(subjects, vv)
|
||||
case []string:
|
||||
subjects = v.([]string)
|
||||
subjects = vv
|
||||
case []interface{}:
|
||||
for _, i := range v.([]interface{}) {
|
||||
for _, i := range vv {
|
||||
_, i = unwrapValue(i)
|
||||
|
||||
subject, ok := i.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Subject in permissions array cannot be cast to string")
|
||||
@@ -661,14 +840,15 @@ func parseOldPermissionStyle(v interface{}) (*SubjectPermission, error) {
|
||||
}
|
||||
|
||||
// Helper function to parse new style authorization into a SubjectPermission with Allow and Deny.
|
||||
func parseSubjectPermission(m map[string]interface{}) (*SubjectPermission, error) {
|
||||
func parseSubjectPermission(v interface{}, opts *Options) (*SubjectPermission, error) {
|
||||
m := v.(map[string]interface{})
|
||||
if len(m) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
p := &SubjectPermission{}
|
||||
|
||||
pedantic := opts.CheckConfig
|
||||
for k, v := range m {
|
||||
tk, v := unwrapValue(v)
|
||||
switch strings.ToLower(k) {
|
||||
case "allow":
|
||||
subjects, err := parseSubjects(v)
|
||||
@@ -683,6 +863,13 @@ func parseSubjectPermission(m map[string]interface{}) (*SubjectPermission, error
|
||||
}
|
||||
p.Deny = subjects
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown field name %q parsing subject permissions, only 'allow' or 'deny' are permitted", k)
|
||||
}
|
||||
}
|
||||
@@ -713,7 +900,6 @@ func PrintTLSHelpAndDie() {
|
||||
}
|
||||
|
||||
func parseCipher(cipherName string) (uint16, error) {
|
||||
|
||||
cipher, exists := cipherMap[cipherName]
|
||||
if !exists {
|
||||
return 0, fmt.Errorf("Unrecognized cipher %s", cipherName)
|
||||
@@ -731,9 +917,17 @@ func parseCurvePreferences(curveName string) (tls.CurveID, error) {
|
||||
}
|
||||
|
||||
// Helper function to parse TLS configs.
|
||||
func parseTLS(tlsm map[string]interface{}) (*TLSConfigOpts, error) {
|
||||
tc := TLSConfigOpts{}
|
||||
func parseTLS(v interface{}, opts *Options) (*TLSConfigOpts, error) {
|
||||
var (
|
||||
tlsm map[string]interface{}
|
||||
tk token
|
||||
tc TLSConfigOpts = TLSConfigOpts{}
|
||||
pedantic bool = opts.CheckConfig
|
||||
)
|
||||
_, v = unwrapValue(v)
|
||||
tlsm = v.(map[string]interface{})
|
||||
for mk, mv := range tlsm {
|
||||
tk, mv = unwrapValue(mv)
|
||||
switch strings.ToLower(mk) {
|
||||
case "cert_file":
|
||||
certFile, ok := mv.(string)
|
||||
@@ -766,6 +960,7 @@ func parseTLS(tlsm map[string]interface{}) (*TLSConfigOpts, error) {
|
||||
}
|
||||
tc.Ciphers = make([]uint16, 0, len(ra))
|
||||
for _, r := range ra {
|
||||
_, r = unwrapValue(r)
|
||||
cipher, err := parseCipher(r.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -779,6 +974,7 @@ func parseTLS(tlsm map[string]interface{}) (*TLSConfigOpts, error) {
|
||||
}
|
||||
tc.CurvePreferences = make([]tls.CurveID, 0, len(ra))
|
||||
for _, r := range ra {
|
||||
_, r = unwrapValue(r)
|
||||
cps, err := parseCurvePreferences(r.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -795,6 +991,14 @@ func parseTLS(tlsm map[string]interface{}) (*TLSConfigOpts, error) {
|
||||
}
|
||||
tc.Timeout = at
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("error parsing tls config, unknown field [%q]", mk)
|
||||
}
|
||||
}
|
||||
@@ -1134,6 +1338,7 @@ func ConfigureOptions(fs *flag.FlagSet, args []string, printVersion, printHelp,
|
||||
fs.IntVar(&opts.HTTPSPort, "https_port", 0, "HTTPS Port for /varz, /connz endpoints.")
|
||||
fs.StringVar(&configFile, "c", "", "Configuration file.")
|
||||
fs.StringVar(&configFile, "config", "", "Configuration file.")
|
||||
fs.BoolVar(&opts.CheckConfig, "t", false, "Check configuration and exit.")
|
||||
fs.StringVar(&signal, "sl", "", "Send signal to gnatsd process (stop, quit, reopen, reload)")
|
||||
fs.StringVar(&signal, "signal", "", "Send signal to gnatsd process (stop, quit, reopen, reload)")
|
||||
fs.StringVar(&opts.PidFile, "P", "", "File to store process pid.")
|
||||
@@ -1216,12 +1421,18 @@ func ConfigureOptions(fs *flag.FlagSet, args []string, printVersion, printHelp,
|
||||
// This will update the options with values from the config file.
|
||||
if err := opts.ProcessConfigFile(configFile); err != nil {
|
||||
return nil, err
|
||||
} else if opts.CheckConfig {
|
||||
// Report configuration file syntax test was successful and exit.
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// Call this again to override config file options with options from command line.
|
||||
// Note: We don't need to check error here since if there was an error, it would
|
||||
// have been caught the first time this function was called (after setting up the
|
||||
// flags).
|
||||
fs.Parse(args)
|
||||
} else if opts.CheckConfig {
|
||||
return nil, fmt.Errorf("must specify [-c, --config] option to check configuration file syntax")
|
||||
}
|
||||
|
||||
// Special handling of some flags
|
||||
|
||||
Reference in New Issue
Block a user