mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 11:48:43 -07:00
If a configuration variable starts with numbers and has a character that such as K/k/G/g/etc.. it would assume that it was a number (expressed in Kb, Gb, etc..). This PR checks that if the special characters are not the suffix, that is, the variable does not end after those characters, then the parsing will treat the whole thing as a string. Resolves #3431 Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
406 lines
8.6 KiB
Go
406 lines
8.6 KiB
Go
package conf
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"reflect"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// Test to make sure we get what we expect.
|
||
|
||
func test(t *testing.T, data string, ex map[string]interface{}) {
|
||
t.Helper()
|
||
m, err := Parse(data)
|
||
if err != nil {
|
||
t.Fatalf("Received err: %v\n", err)
|
||
}
|
||
if m == nil {
|
||
t.Fatal("Received nil map")
|
||
}
|
||
|
||
if !reflect.DeepEqual(m, ex) {
|
||
t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex)
|
||
}
|
||
}
|
||
|
||
func TestSimpleTopLevel(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": "1",
|
||
"bar": float64(2.2),
|
||
"baz": true,
|
||
"boo": int64(22),
|
||
}
|
||
test(t, "foo='1'; bar=2.2; baz=true; boo=22", ex)
|
||
}
|
||
|
||
func TestBools(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": true,
|
||
}
|
||
test(t, "foo=true", ex)
|
||
test(t, "foo=TRUE", ex)
|
||
test(t, "foo=true", ex)
|
||
test(t, "foo=yes", ex)
|
||
test(t, "foo=on", ex)
|
||
}
|
||
|
||
var varSample = `
|
||
index = 22
|
||
foo = $index
|
||
`
|
||
|
||
func TestSimpleVariable(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"index": int64(22),
|
||
"foo": int64(22),
|
||
}
|
||
test(t, varSample, ex)
|
||
}
|
||
|
||
var varNestedSample = `
|
||
index = 22
|
||
nest {
|
||
index = 11
|
||
foo = $index
|
||
}
|
||
bar = $index
|
||
`
|
||
|
||
func TestNestedVariable(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"index": int64(22),
|
||
"nest": map[string]interface{}{
|
||
"index": int64(11),
|
||
"foo": int64(11),
|
||
},
|
||
"bar": int64(22),
|
||
}
|
||
test(t, varNestedSample, ex)
|
||
}
|
||
|
||
func TestMissingVariable(t *testing.T) {
|
||
_, err := Parse("foo=$index")
|
||
if err == nil {
|
||
t.Fatalf("Expected an error for a missing variable, got none")
|
||
}
|
||
if !strings.HasPrefix(err.Error(), "variable reference") {
|
||
t.Fatalf("Wanted a variable reference err, got %q\n", err)
|
||
}
|
||
}
|
||
|
||
func TestEnvVariable(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": int64(22),
|
||
}
|
||
evar := "__UNIQ22__"
|
||
os.Setenv(evar, "22")
|
||
defer os.Unsetenv(evar)
|
||
test(t, fmt.Sprintf("foo = $%s", evar), ex)
|
||
}
|
||
|
||
func TestEnvVariableString(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": "xyz",
|
||
}
|
||
evar := "__UNIQ22__"
|
||
os.Setenv(evar, "xyz")
|
||
defer os.Unsetenv(evar)
|
||
test(t, fmt.Sprintf("foo = $%s", evar), ex)
|
||
}
|
||
|
||
func TestEnvVariableStringStartingWithNumber(t *testing.T) {
|
||
evar := "__UNIQ22__"
|
||
os.Setenv(evar, "3xyz")
|
||
defer os.Unsetenv(evar)
|
||
|
||
_, err := Parse("foo = $%s")
|
||
if err == nil {
|
||
t.Fatalf("Expected err not being able to process string: %v\n", err)
|
||
}
|
||
}
|
||
|
||
func TestEnvVariableStringStartingWithNumberAndSizeUnit(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": "3Gyz",
|
||
}
|
||
evar := "__UNIQ22__"
|
||
os.Setenv(evar, "3Gyz")
|
||
defer os.Unsetenv(evar)
|
||
test(t, fmt.Sprintf("foo = $%s", evar), ex)
|
||
}
|
||
|
||
func TestEnvVariableStringStartingWithNumberUsingQuotes(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": "3xyz",
|
||
}
|
||
evar := "__UNIQ22__"
|
||
os.Setenv(evar, "'3xyz'")
|
||
defer os.Unsetenv(evar)
|
||
test(t, fmt.Sprintf("foo = $%s", evar), ex)
|
||
}
|
||
|
||
func TestBcryptVariable(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"password": "$2a$11$ooo",
|
||
}
|
||
test(t, "password: $2a$11$ooo", ex)
|
||
}
|
||
|
||
var easynum = `
|
||
k = 8k
|
||
kb = 4kb
|
||
ki = 3ki
|
||
kib = 4ki
|
||
m = 1m
|
||
mb = 2MB
|
||
mi = 2Mi
|
||
mib = 64MiB
|
||
g = 2g
|
||
gb = 22GB
|
||
gi = 22Gi
|
||
gib = 22GiB
|
||
tb = 22TB
|
||
ti = 22Ti
|
||
tib = 22TiB
|
||
pb = 22PB
|
||
pi = 22Pi
|
||
pib = 22PiB
|
||
`
|
||
|
||
func TestConvenientNumbers(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"k": int64(8 * 1000),
|
||
"kb": int64(4 * 1024),
|
||
"ki": int64(3 * 1024),
|
||
"kib": int64(4 * 1024),
|
||
"m": int64(1000 * 1000),
|
||
"mb": int64(2 * 1024 * 1024),
|
||
"mi": int64(2 * 1024 * 1024),
|
||
"mib": int64(64 * 1024 * 1024),
|
||
"g": int64(2 * 1000 * 1000 * 1000),
|
||
"gb": int64(22 * 1024 * 1024 * 1024),
|
||
"gi": int64(22 * 1024 * 1024 * 1024),
|
||
"gib": int64(22 * 1024 * 1024 * 1024),
|
||
"tb": int64(22 * 1024 * 1024 * 1024 * 1024),
|
||
"ti": int64(22 * 1024 * 1024 * 1024 * 1024),
|
||
"tib": int64(22 * 1024 * 1024 * 1024 * 1024),
|
||
"pb": int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
|
||
"pi": int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
|
||
"pib": int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
|
||
}
|
||
test(t, easynum, ex)
|
||
}
|
||
|
||
var sample1 = `
|
||
foo {
|
||
host {
|
||
ip = '127.0.0.1'
|
||
port = 4242
|
||
}
|
||
servers = [ "a.com", "b.com", "c.com"]
|
||
}
|
||
`
|
||
|
||
func TestSample1(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": map[string]interface{}{
|
||
"host": map[string]interface{}{
|
||
"ip": "127.0.0.1",
|
||
"port": int64(4242),
|
||
},
|
||
"servers": []interface{}{"a.com", "b.com", "c.com"},
|
||
},
|
||
}
|
||
test(t, sample1, ex)
|
||
}
|
||
|
||
var cluster = `
|
||
cluster {
|
||
port: 4244
|
||
|
||
authorization {
|
||
user: route_user
|
||
password: top_secret
|
||
timeout: 1
|
||
}
|
||
|
||
# Routes are actively solicited and connected to from this server.
|
||
# Other servers can connect to us if they supply the correct credentials
|
||
# in their routes definitions from above.
|
||
|
||
// Test both styles of comments
|
||
|
||
routes = [
|
||
nats-route://foo:bar@apcera.me:4245
|
||
nats-route://foo:bar@apcera.me:4246
|
||
]
|
||
}
|
||
`
|
||
|
||
func TestSample2(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"cluster": map[string]interface{}{
|
||
"port": int64(4244),
|
||
"authorization": map[string]interface{}{
|
||
"user": "route_user",
|
||
"password": "top_secret",
|
||
"timeout": int64(1),
|
||
},
|
||
"routes": []interface{}{
|
||
"nats-route://foo:bar@apcera.me:4245",
|
||
"nats-route://foo:bar@apcera.me:4246",
|
||
},
|
||
},
|
||
}
|
||
|
||
test(t, cluster, ex)
|
||
}
|
||
|
||
var sample3 = `
|
||
foo {
|
||
expr = '(true == "false")'
|
||
text = 'This is a multi-line
|
||
text block.'
|
||
}
|
||
`
|
||
|
||
func TestSample3(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"foo": map[string]interface{}{
|
||
"expr": "(true == \"false\")",
|
||
"text": "This is a multi-line\ntext block.",
|
||
},
|
||
}
|
||
test(t, sample3, ex)
|
||
}
|
||
|
||
var sample4 = `
|
||
array [
|
||
{ abc: 123 }
|
||
{ xyz: "word" }
|
||
]
|
||
`
|
||
|
||
func TestSample4(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"array": []interface{}{
|
||
map[string]interface{}{"abc": int64(123)},
|
||
map[string]interface{}{"xyz": "word"},
|
||
},
|
||
}
|
||
test(t, sample4, ex)
|
||
}
|
||
|
||
var sample5 = `
|
||
now = 2016-05-04T18:53:41Z
|
||
gmt = false
|
||
|
||
`
|
||
|
||
func TestSample5(t *testing.T) {
|
||
dt, _ := time.Parse("2006-01-02T15:04:05Z", "2016-05-04T18:53:41Z")
|
||
ex := map[string]interface{}{
|
||
"now": dt,
|
||
"gmt": false,
|
||
}
|
||
test(t, sample5, ex)
|
||
}
|
||
|
||
func TestIncludes(t *testing.T) {
|
||
ex := map[string]interface{}{
|
||
"listen": "127.0.0.1:4222",
|
||
"authorization": map[string]interface{}{
|
||
"ALICE_PASS": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q",
|
||
"BOB_PASS": "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly",
|
||
"users": []interface{}{
|
||
map[string]interface{}{
|
||
"user": "alice",
|
||
"password": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q"},
|
||
map[string]interface{}{
|
||
"user": "bob",
|
||
"password": "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly"},
|
||
},
|
||
"timeout": float64(0.5),
|
||
},
|
||
}
|
||
|
||
m, err := ParseFile("simple.conf")
|
||
if err != nil {
|
||
t.Fatalf("Received err: %v\n", err)
|
||
}
|
||
if m == nil {
|
||
t.Fatal("Received nil map")
|
||
}
|
||
|
||
if !reflect.DeepEqual(m, ex) {
|
||
t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex)
|
||
}
|
||
}
|
||
|
||
var varIncludedVariablesSample = `
|
||
authorization {
|
||
|
||
include "./includes/passwords.conf"
|
||
|
||
CAROL_PASS: foo
|
||
|
||
users = [
|
||
{user: alice, password: $ALICE_PASS}
|
||
{user: bob, password: $BOB_PASS}
|
||
{user: carol, password: $CAROL_PASS}
|
||
]
|
||
}
|
||
`
|
||
|
||
func TestIncludeVariablesWithChecks(t *testing.T) {
|
||
p, err := parse(varIncludedVariablesSample, "", true)
|
||
if err != nil {
|
||
t.Fatalf("Received err: %v\n", err)
|
||
}
|
||
key := "authorization"
|
||
m, ok := p.mapping[key]
|
||
if !ok {
|
||
t.Errorf("Expected %q to be in the config", key)
|
||
}
|
||
expectKeyVal := func(t *testing.T, m interface{}, expectedKey string, expectedVal string, expectedLine, expectedPos int) {
|
||
t.Helper()
|
||
tk := m.(*token)
|
||
v := tk.Value()
|
||
vv := v.(map[string]interface{})
|
||
value, ok := vv[expectedKey]
|
||
if !ok {
|
||
t.Errorf("Expected key %q", expectedKey)
|
||
}
|
||
tk, ok = value.(*token)
|
||
if !ok {
|
||
t.Fatalf("Expected token %v", value)
|
||
}
|
||
if tk.Line() != expectedLine {
|
||
t.Errorf("Expected token to be at line %d, got: %d", expectedLine, tk.Line())
|
||
}
|
||
if tk.Position() != expectedPos {
|
||
t.Errorf("Expected token to be at position %d, got: %d", expectedPos, tk.Position())
|
||
}
|
||
v = tk.Value()
|
||
if v != expectedVal {
|
||
t.Errorf("Expected %q, got: %s", expectedVal, v)
|
||
}
|
||
}
|
||
expectKeyVal(t, m, "ALICE_PASS", "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q", 2, 1)
|
||
expectKeyVal(t, m, "BOB_PASS", "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly", 3, 1)
|
||
expectKeyVal(t, m, "CAROL_PASS", "foo", 6, 3)
|
||
}
|
||
|
||
func TestParserNoInfiniteLoop(t *testing.T) {
|
||
for _, test := range []string{`A@@Føøøø?˛ø:{øøøø˙˙`, `include "9/<2F>`} {
|
||
if _, err := Parse(test); err == nil {
|
||
t.Fatal("expected an error")
|
||
} else if !strings.Contains(err.Error(), "Unexpected EOF") {
|
||
t.Fatal("expected unexpected eof error")
|
||
}
|
||
}
|
||
}
|