From a33e316f40594d67a28606cf6d534fc8114672ba Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Thu, 9 Feb 2023 14:45:32 +0100 Subject: [PATCH] Provisioning: Parse boolean and numeric values from environment variables (#63085) --- pkg/services/provisioning/values/values.go | 30 ++++++++++++++++++- .../provisioning/values/values_test.go | 13 ++++---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/pkg/services/provisioning/values/values.go b/pkg/services/provisioning/values/values.go index b384317f9d4..d528307271a 100644 --- a/pkg/services/provisioning/values/values.go +++ b/pkg/services/provisioning/values/values.go @@ -247,7 +247,7 @@ func transformInterface(i interface{}) (interface{}, interface{}, error) { case reflect.Map: return transformMap(i.(map[string]interface{})) case reflect.String: - return interpolateValue(i.(string)) + return interpolateIfaceValue(i.(string)) default: // Was int, float or some other value that we do not need to do any transform on. return i, i, nil @@ -281,6 +281,34 @@ func transformMap(i map[string]interface{}) (interface{}, interface{}, error) { return transformed, raw, nil } +func interpolateIfaceValue(val string) (interface{}, string, error) { + parts := strings.Split(val, "$$") + if len(parts) > 1 { + return interpolateValue(val) + } + expanded, err := setting.ExpandVar(val) + if err != nil { + return val, val, fmt.Errorf("failed to interpolate value '%s': %w", val, err) + } + expandedEnv := os.ExpandEnv(expanded) + if expandedEnv != val { + // If the value is an environment variable, consider it may not be a string + intV, err := strconv.ParseInt(expandedEnv, 10, 64) + if err == nil { + return intV, val, nil + } + floatV, err := strconv.ParseFloat(expandedEnv, 64) + if err == nil { + return floatV, val, nil + } + boolV, err := strconv.ParseBool(expandedEnv) + if err == nil { + return boolV, val, nil + } + } + return expandedEnv, val, nil +} + // interpolateValue returns the final value after interpolation. In addition to environment variable interpolation, // expanders available for the settings file are expanded here. // For a literal '$', '$$' can be used to avoid interpolation. diff --git a/pkg/services/provisioning/values/values_test.go b/pkg/services/provisioning/values/values_test.go index 7ee470ec8ff..70574280b3b 100644 --- a/pkg/services/provisioning/values/values_test.go +++ b/pkg/services/provisioning/values/values_test.go @@ -168,6 +168,7 @@ func TestValues(t *testing.T) { Some text with $STRING anchor: &label $INT anchored: *label + boolval: $BOOL ` unmarshalingTest(t, doc, d) @@ -191,12 +192,13 @@ func TestValues(t *testing.T) { }, "four": stringMap{ "nested": stringMap{ - "onemore": "1", + "onemore": int64(1), }, }, "multiline": "Some text with test\n", - "anchor": "1", - "anchored": "1", + "anchor": int64(1), + "anchored": int64(1), + "boolval": true, }) require.Equal(t, d.Val.Raw, stringMap{ @@ -224,6 +226,7 @@ func TestValues(t *testing.T) { "multiline": "Some text with $STRING\n", "anchor": "$INT", "anchored": "$INT", + "boolval": "$BOOL", }) }) }) @@ -251,12 +254,12 @@ func TestValues(t *testing.T) { require.Equal(t, []stringMap{ { "interpolatedString": "test", - "interpolatedInt": "1", + "interpolatedInt": int64(1), "string": "just a string", }, { "interpolatedString": "test", - "interpolatedInt": "1", + "interpolatedInt": int64(1), "string": "just a string", }, }, d.Val.Value())