diff options
author | Arne H Juul <arnej27959@users.noreply.github.com> | 2022-08-26 08:03:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-26 08:03:36 +0200 |
commit | 5bcc0f55bee316126e1e9d1b29e22c3d22b67ba2 (patch) | |
tree | 66eb5b2b1c1d9de277f9d00cade275362cdf7806 /client | |
parent | 52014d87048b6baa378ee6f2981ac5eae041cfb9 (diff) | |
parent | 65d0380041fe87bb4fc40f1e8e18f1d16a2f4545 (diff) |
Merge pull request #23788 from vespa-engine/arnej/load-default-env-for-go
go function loading default-env.txt
Diffstat (limited to 'client')
-rw-r--r-- | client/go/vespa/load_env.go | 109 | ||||
-rw-r--r-- | client/go/vespa/load_env_test.go | 100 |
2 files changed, 209 insertions, 0 deletions
diff --git a/client/go/vespa/load_env.go b/client/go/vespa/load_env.go new file mode 100644 index 00000000000..a0c127ca920 --- /dev/null +++ b/client/go/vespa/load_env.go @@ -0,0 +1,109 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// load default environment variables (from $VESPA_HOME/conf/vespa/default-env.txt) +// Author: arnej + +package vespa + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +// backwards-compatible parsing of default-env.txt +func LoadDefaultEnv() error { + const defEnvTxt = "/conf/vespa/default-env.txt" + vespaHome := FindHome() + f, err := os.Open(vespaHome + defEnvTxt) + if err != nil { + return err + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "#") { + continue + } + fields := nSpacedFields(line, 3) + action := fields[0] + if action == "" { + continue + } + varName := fields[1] + varVal := fields[2] + if !isValidShellVariableName(varName) { + err = fmt.Errorf("Not a valid environment variable name: '%s'", varName) + continue + } + if strings.HasPrefix(varVal, `"`) && strings.HasSuffix(varVal, `"`) { + varVal = varVal[1 : len(varVal)-1] + } + switch action { + case "override": + os.Setenv(varName, varVal) + case "fallback": + if os.Getenv(varName) == "" { + os.Setenv(varName, varVal) + } + case "unset": + os.Unsetenv(varName) + default: + err = fmt.Errorf("unknown action '%s'", action) + } + } + if err == nil { + err = scanner.Err() + } + return err +} + +// borrowed some code from strings.Fields() implementation: +func nSpacedFields(s string, n int) []string { + var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} + a := make([]string, n) + na := 0 + fieldStart := 0 + i := 0 + // Skip spaces in the front of the input. + for i < len(s) && asciiSpace[s[i]] != 0 { + i++ + } + fieldStart = i + for i < len(s) && na+1 < n { + if asciiSpace[s[i]] == 0 { + i++ + continue + } + a[na] = s[fieldStart:i] + na++ + i++ + // Skip spaces in between fields. + for i < len(s) && asciiSpace[s[i]] != 0 { + i++ + } + fieldStart = i + } + // ignore trailing spaces + for i = len(s); i > fieldStart && asciiSpace[s[i-1]] != 0; i-- { + } + a[na] = s[fieldStart:i] + return a +} + +// pretty strict for now, can be more lenient if needed +func isValidShellVariableName(s string) bool { + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case (b >= 'A' && b <= 'Z'): // ok + case (b >= 'a' && b <= 'z'): // ok + case (b >= '0' && b <= '9'): // ok + case b == '_': // ok + default: + return false + } + } + return len(s) > 0 +} diff --git a/client/go/vespa/load_env_test.go b/client/go/vespa/load_env_test.go new file mode 100644 index 00000000000..c5b42cae161 --- /dev/null +++ b/client/go/vespa/load_env_test.go @@ -0,0 +1,100 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package vespa + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func setup(t *testing.T, contents string) { + tmp := t.TempDir() + "/load_env_test.tmp" + vdir := tmp + "/vespa" + cdir := vdir + "/conf/vespa" + envf := cdir + "/default-env.txt" + err := os.MkdirAll(cdir, 0755) + assert.Nil(t, err) + os.Setenv("VESPA_HOME", vdir) + err = os.WriteFile(envf, []byte(contents), 0644) + assert.Nil(t, err) +} + +func TestLoadEnvSimple(t *testing.T) { + os.Setenv("VESPA_FOO", "was foo") + os.Setenv("VESPA_BAR", "was bar") + os.Setenv("VESPA_FOOBAR", "foobar") + os.Unsetenv("VESPA_QUUX") + setup(t, ` +# vespa env vars file +override VESPA_FOO "new foo" + +fallback VESPA_BAR "new bar" +fallback VESPA_QUUX "new quux" + +unset VESPA_FOOBAR +`) + // run it + err := LoadDefaultEnv() + assert.Nil(t, err) + // check results + assert.Equal(t, os.Getenv("VESPA_FOO"), "new foo") + assert.Equal(t, os.Getenv("VESPA_BAR"), "was bar") + assert.Equal(t, os.Getenv("VESPA_FOOBAR"), "") + assert.Equal(t, os.Getenv("VESPA_QUUX"), "new quux") + _, present := os.LookupEnv("VESPA_FOOBAR") + assert.Equal(t, present, false) +} + +func TestLoadEnvWhiteSpace(t *testing.T) { + // note trailing whitespace below! + setup(t, ` +# vespa env vars file +override VESPA_V1 v1 + override VESPA_V2 v2 +override VESPA_V3 spaced v3 v3 +override VESPA_V4 " quoted spaced " +override VESPA_V5 v5 +`) + // run it + err := LoadDefaultEnv() + assert.Nil(t, err) + // check results + assert.Equal(t, os.Getenv("VESPA_V1"), "v1") + assert.Equal(t, os.Getenv("VESPA_V2"), "v2") + assert.Equal(t, os.Getenv("VESPA_V3"), "spaced v3 v3") + assert.Equal(t, os.Getenv("VESPA_V4"), " quoted spaced ") + assert.Equal(t, os.Getenv("VESPA_V5"), "v5") +} + +func TestLoadEnvBadAction(t *testing.T) { + setup(t, ` +# vespa env vars file +override VESPA_V1 v1 +some junk here +override VESPA_V2 v2 +`) + // run it + err := LoadDefaultEnv() + // check results + assert.Equal(t, os.Getenv("VESPA_V1"), "v1") + assert.Equal(t, os.Getenv("VESPA_V2"), "v2") + assert.NotNil(t, err) + assert.Equal(t, err.Error(), "unknown action 'some'") +} + +func TestLoadEnvBadVar(t *testing.T) { + setup(t, ` +# vespa env vars file +override VESPA_V1 v1 +override .A foobar +override VESPA_V2 v2 +`) + // run it + err := LoadDefaultEnv() + // check results + assert.Equal(t, os.Getenv("VESPA_V1"), "v1") + assert.Equal(t, os.Getenv("VESPA_V2"), "v2") + assert.NotNil(t, err) + assert.Equal(t, err.Error(), "Not a valid environment variable name: '.A'") +} |