From 76d08989f024fba08a0d5e9aa217c172ef64ac8f Mon Sep 17 00:00:00 2001 From: Kyle Brandt Date: Thu, 11 Jul 2019 07:32:07 -0400 Subject: [PATCH] provisioning: escape literal '$' with '$$' to avoid interpolation (#18045) fixes #17986 --- docs/sources/administration/provisioning.md | 2 ++ pkg/services/provisioning/values/values.go | 9 ++++++- .../provisioning/values/values_test.go | 25 +++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md index 1299b757752..15080b1ebab 100644 --- a/docs/sources/administration/provisioning.md +++ b/docs/sources/administration/provisioning.md @@ -45,6 +45,8 @@ datasources: password: $PASSWORD ``` +If you have a literal `$` in your value and want to avoid interpolation, `$$` can be used. +
## Configuration Management Tools diff --git a/pkg/services/provisioning/values/values.go b/pkg/services/provisioning/values/values.go index 63da76a9338..de86085671e 100644 --- a/pkg/services/provisioning/values/values.go +++ b/pkg/services/provisioning/values/values.go @@ -14,6 +14,7 @@ import ( "os" "reflect" "strconv" + "strings" "github.com/grafana/grafana/pkg/util/errutil" ) @@ -185,8 +186,14 @@ func transformMap(i map[interface{}]interface{}) interface{} { // interpolateValue returns final value after interpolation. At the moment only env var interpolation is done // here but in the future something like interpolation from file could be also done here. +// For a literal '$', '$$' can be used to avoid interpolation. func interpolateValue(val string) string { - return os.ExpandEnv(val) + parts := strings.Split(val, "$$") + interpolated := make([]string, len(parts)) + for i, v := range parts { + interpolated[i] = os.ExpandEnv(v) + } + return strings.Join(interpolated, "$") } type interpolated struct { diff --git a/pkg/services/provisioning/values/values_test.go b/pkg/services/provisioning/values/values_test.go index 284069be395..064dee86d6a 100644 --- a/pkg/services/provisioning/values/values_test.go +++ b/pkg/services/provisioning/values/values_test.go @@ -1,16 +1,18 @@ package values import ( - . "github.com/smartystreets/goconvey/convey" - "gopkg.in/yaml.v2" "os" "testing" + + . "github.com/smartystreets/goconvey/convey" + "gopkg.in/yaml.v2" ) func TestValues(t *testing.T) { Convey("Values", t, func() { os.Setenv("INT", "1") os.Setenv("STRING", "test") + os.Setenv("EMPTYSTRING", "") os.Setenv("BOOL", "true") Convey("IntValue", func() { @@ -61,6 +63,24 @@ func TestValues(t *testing.T) { So(d.Val.Value(), ShouldEqual, "") So(d.Val.Raw, ShouldEqual, "") }) + + Convey("empty var should have empty value", func() { + unmarshalingTest(`val: $EMPTYSTRING`, d) + So(d.Val.Value(), ShouldEqual, "") + So(d.Val.Raw, ShouldEqual, "$EMPTYSTRING") + }) + + Convey("$$ should be a literal $", func() { + unmarshalingTest(`val: $$`, d) + So(d.Val.Value(), ShouldEqual, "$") + So(d.Val.Raw, ShouldEqual, "$$") + }) + + Convey("$$ should be a literal $ and not expanded within a string", func() { + unmarshalingTest(`val: mY,Passwo$$rd`, d) + So(d.Val.Value(), ShouldEqual, "mY,Passwo$rd") + So(d.Val.Raw, ShouldEqual, "mY,Passwo$$rd") + }) }) Convey("BoolValue", func() { @@ -199,6 +219,7 @@ func TestValues(t *testing.T) { Reset(func() { os.Unsetenv("INT") os.Unsetenv("STRING") + os.Unsetenv("EMPTYSTRING") os.Unsetenv("BOOL") }) })