Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Allow variables to be used as part of a value in configuration.#7464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
joeriddles wants to merge3 commits intonats-io:main
base:main
Choose a base branch
Loading
fromjoeriddles:fix-5320
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletionsconf/lex.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -900,6 +900,18 @@ func (lx *lexer) isVariable() bool {
return false
}

// Check if the unquoted string is a variable reference with braces
func (lx *lexer) isVariableWithBraces() bool {
if lx.start >= len(lx.input) {
return false
}
if len(lx.input) > 3 && lx.input[lx.start:lx.start+2] == "${" {
lx.start += 2
return true
}
return false
}

// lexQuotedString consumes the inner contents of a string. It assumes that the
// beginning '"' has already been consumed and ignored. It will not interpret any
// internal contents.
Expand DownExpand Up@@ -964,6 +976,14 @@ func lexString(lx *lexer) stateFn {
lx.emitString()
} else if lx.isBool() {
lx.emit(itemBool)
} else if lx.isVariableWithBraces() {
lx.emit(itemVariable)

// consume the trailing '}'
if lx.pos < len(lx.input) && lx.input[lx.pos] == '}' {
lx.next()
lx.ignore()
}
} else if lx.isVariable() {
lx.emit(itemVariable)
} else {
Expand Down
24 changes: 24 additions & 0 deletionsconf/lex_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -531,6 +531,14 @@ func TestVariableValues(t *testing.T) {
}
lx=lex("foo $bar")
expect(t,lx,expectedItems)

expectedItems= []item{
{itemKey,"foo",1,0},
{itemVariable,"bar",1,8},
{itemEOF,"",1,0},
}
lx=lex("foo = ${bar}")
expect(t,lx,expectedItems)
}

funcTestArrays(t*testing.T) {
Expand DownExpand Up@@ -711,6 +719,22 @@ func TestNestedMaps(t *testing.T) {
expect(t,lx,expectedItems)
}

funcTestSimpleMapWithVariable(t*testing.T) {
expectedItems:= []item{
{itemKey,"foo",1,0},
{itemMapStart,"",1,7},
{itemKey,"ip",1,7},
{itemVariable,"IP",1,12},
{itemKey,"port",1,17},
{itemVariable,"PORT",1,26},
{itemMapEnd,"",1,32},
{itemEOF,"",1,0},
}

lx:=lex("foo = {ip=${IP}, port = ${PORT}}")
expect(t,lx,expectedItems)
}

funcTestQuotedKeys(t*testing.T) {
expectedItems:= []item{
{itemKey,"foo",1,0},
Expand Down
33 changes: 33 additions & 0 deletionsconf/parse.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,6 +31,7 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
Expand DownExpand Up@@ -287,6 +288,10 @@ func (p *parser) processItem(it item, fp string) error {
setValue(it, p.popContext())
case itemString:
// FIXME(dlc) sanitize string?
err := p.checkForEmbeddedVariables(&it)
if err != nil {
return err
}
setValue(it, it.val)
case itemInteger:
lastDigit := 0
Expand DownExpand Up@@ -430,6 +435,34 @@ const pkey = "pk"
// We special case raw strings here that are bcrypt'd. This allows us not to force quoting the strings
const bcryptPrefix = "2a$"

// To match embedded variables.
var varPat = regexp.MustCompile(`\$\{[^@\s]+\}`)

// checkForEmbeddedVariable will check for embedded variables in an itemString.
// If they are found and we can look them up we will replace them in item, otherwise will error.
func (p *parser) checkForEmbeddedVariables(it *item) error {
if !strings.ContainsAny(it.val, "${") {
return nil
}
// We have some embedded variables.
for _, m := range varPat.FindAllString(it.val, -1) {
ref := m[2 : len(m)-1] // Strip leading ${ and trailing }
value, found, err := p.lookupVariable(ref)
if err != nil {
return fmt.Errorf("variable reference for '%s' on line %d could not be parsed: %s",
m, it.line, err)
}
if !found {
return fmt.Errorf("variable reference for '%s' on line %d can not be found",
m, it.line)
}
if v, ok := value.(string); ok {
it.val = strings.Replace(it.val, m, v, 1)
}
}
return nil
}

// lookupVariable will lookup a variable reference. It will use block scoping on keys
// it has seen before, with the top level scoping being the environment variables. We
// ignore array contexts and only process the map contexts..
Expand Down
105 changes: 105 additions & 0 deletionsconf/parse_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -84,6 +84,19 @@ func TestSimpleVariable(t *testing.T) {
test(t, varSample, ex)
}

var varSampleWithBraces = `
index = 22
foo = ${index}
`

func TestSimpleVariableWithBraces(t *testing.T) {
ex := map[string]any{
"index": int64(22),
"foo": int64(22),
}
test(t, varSampleWithBraces, ex)
}

var varNestedSample = `
index = 22
nest {
Expand DownExpand Up@@ -113,6 +126,14 @@ func TestMissingVariable(t *testing.T) {
if !strings.HasPrefix(err.Error(), "variable reference") {
t.Fatalf("Wanted a variable reference err, got %q\n", err)
}

_, 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) {
Expand DownExpand Up@@ -166,6 +187,90 @@ func TestEnvVariableStringStartingWithNumberUsingQuotes(t *testing.T) {
test(t, fmt.Sprintf("foo = $%s", evar), ex)
}

func TestEnvVariableEmbedded(t *testing.T) {
cluster := `
cluster {
# set the variable token
TOKEN: abc
authorization {
user: user
password: "${TOKEN}"
}
routes = [ "nats://user:${TOKEN}@server.example.com:6222" ]
}`
ex := map[string]any{
"cluster": map[string]any{
"TOKEN": "abc",
"authorization": map[string]any{
"user": "user",
"password": "abc",
},
"routes": []any{
"nats://user:abc@server.example.com:6222",
},
},
}

// don't use test() here because we want to test the Parse function without checking pedantic mode.
m, err := Parse(cluster)
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 TestEnvVariableEmbeddedMissing(t *testing.T) {
cluster := `
cluster {
authorization {
user: user
password: ${TOKEN}
}
}`

_, err := Parse(cluster)
if err == nil {
t.Fatalf("Expected err not being able to process embedded variable, got none")
}
}

func TestEnvVariableEmbeddedOutsideOfQuotes(t *testing.T) {
cluster := `
cluster {
# set the variable token
TOKEN: abc
authorization {
user: user
# ok
password: ${TOKEN}
}
# not ok
routes = [ nats://user:${TOKEN}@server.example.com:6222 ]
}`

_, err := Parse(cluster)
if err == nil {
t.Fatalf("Expected err not being able to process embedded variable, got none")
}
}

func TestEnvVariableEmbeddedSYS(t *testing.T) {
// https://github.com/nats-io/nats-server/pull/5544#discussion_r1641577620
cluster := `
system_account: "$SYS"
`
ex := map[string]any{
"system_account": "$SYS",
}
test(t, cluster, ex)
}

func TestBcryptVariable(t *testing.T) {
ex := map[string]any{
"password": "$2a$11$ooo",
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp