Skip to content

Commit

Permalink
🐛 [environment] Fix environment variable parsing (#336)
Browse files Browse the repository at this point in the history
<!--
Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors.
All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->
### Description

- Fix the bug happening when parsing environment variables
- exposed some of the underlying function

### Test Coverage

<!--
Please put an `x` in the correct box e.g. `[x]` to indicate the testing
coverage of this change.
-->

- [x]  This change is covered by existing or additional automated tests.
- [ ] Manual testing has been performed (and evidence provided) as
automated testing was not feasible.
- [ ] Additional tests are not required for this change (e.g.
documentation update).
  • Loading branch information
acabarbaye authored Oct 13, 2023
1 parent 7c8e0cf commit 8671555
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 20 deletions.
1 change: 1 addition & 0 deletions changes/20231013160208.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:sparkles: `[environment]` Expose utilities for parsing and finding environment variables
1 change: 1 addition & 0 deletions changes/20231013160247.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:bug: `[environment]` Fix environment variables parsing when an entry is incorrect
19 changes: 2 additions & 17 deletions utils/environment/current.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package environment

import (
"fmt"
"os"
"os/user"

"github.com/joho/godotenv"
"github.com/mitchellh/go-homedir"

"github.com/ARM-software/golang-utils/utils/commonerrors"
"github.com/ARM-software/golang-utils/utils/filesystem"
)

Expand All @@ -35,14 +33,7 @@ func (c *currentEnv) GetEnvironmentVariables(dotEnvFiles ...string) (variables [
_ = godotenv.Load(dotEnvFiles...) // ignore error (specifically on loading .env) consistent with config.LoadFromEnvironment
}

curentEnv := os.Environ()
for i := range curentEnv {
envvar, err := ParseEnvironmentVariable(curentEnv[i])
if err != nil {
return
}
variables = append(variables, envvar)
}
variables = ParseEnvironmentVariables(os.Environ()...)
return
}

Expand All @@ -52,13 +43,7 @@ func (c *currentEnv) GetFilesystem() filesystem.FS {

// GetEnvironmentVariable searches the current environment (and optionally dotEnvFiles) for a specific environment variable `envvar`.
func (c *currentEnv) GetEnvironmentVariable(envvar string, dotEnvFiles ...string) (value IEnvironmentVariable, err error) {
envvars := c.GetEnvironmentVariables(dotEnvFiles...)
for i := range envvars {
if envvars[i].GetKey() == envvar {
return envvars[i], nil
}
}
return nil, fmt.Errorf("%w: environment variable '%v' not set", commonerrors.ErrNotFound, envvar)
return FindEnvironmentVariable(envvar, c.GetEnvironmentVariables(dotEnvFiles...)...)
}

// NewCurrentEnvironment returns system current environment.
Expand Down
37 changes: 34 additions & 3 deletions utils/environment/envvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,18 @@ func isEnvVarKey(value string) bool {
// ParseEnvironmentVariable parses an environment variable definition, in the form "key=value".
func ParseEnvironmentVariable(variable string) (IEnvironmentVariable, error) {
elements := strings.Split(strings.TrimSpace(variable), "=")
if len(elements) != 2 {
return nil, fmt.Errorf("%w: invalid environment variable entry", commonerrors.ErrInvalid)
if len(elements) < 2 {
return nil, fmt.Errorf("%w: invalid environment variable entry as not following key=value", commonerrors.ErrInvalid)
}
envvar := NewEnvironmentVariable(elements[0], elements[1])
value := elements[1]
if len(elements) > 2 {
var valueElems []string
for i := 1; i < len(elements); i++ {
valueElems = append(valueElems, elements[i])
}
value = strings.Join(valueElems, "=")
}
envvar := NewEnvironmentVariable(elements[0], value)
return envvar, envvar.Validate()
}

Expand Down Expand Up @@ -114,3 +122,26 @@ func ValidateEnvironmentVariables(vars ...IEnvironmentVariable) error {
}
return nil
}

// ParseEnvironmentVariables parses a list of key=value entries such as os.Environ() and returns a list of the corresponding environment variables.
// Any entry failing parsing will be ignored.
func ParseEnvironmentVariables(variables ...string) (envVars []IEnvironmentVariable) {
for i := range variables {
envvar, err := ParseEnvironmentVariable(variables[i])
if err != nil {
continue
}
envVars = append(envVars, envvar)
}
return
}

// FindEnvironmentVariable looks for an environment variable in a list. if no environment variable matches, an error is returned
func FindEnvironmentVariable(envvar string, envvars ...IEnvironmentVariable) (IEnvironmentVariable, error) {
for i := range envvars {
if envvars[i].GetKey() == envvar {
return envvars[i], nil
}
}
return nil, fmt.Errorf("%w: environment variable '%v' not set", commonerrors.ErrNotFound, envvar)
}
17 changes: 17 additions & 0 deletions utils/environment/envvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ func TestParseEnvironmentVariable(t *testing.T) {
errortest.AssertError(t, err, commonerrors.ErrInvalid)
_, err = ParseEnvironmentVariable(faker.Word() + "=" + faker.Word())
require.NoError(t, err)
_, err = ParseEnvironmentVariable(faker.Word() + "=" + faker.Word() + "=")
require.NoError(t, err)
_, err = ParseEnvironmentVariable(faker.Word() + "=" + faker.Word() + "=" + faker.Sentence())
require.NoError(t, err)
key := strings.ReplaceAll(strings.ReplaceAll(faker.Sentence(), " ", "_"), ".", "")
value := faker.Sentence()
envTest := NewEnvironmentVariable(key, value)
Expand All @@ -144,3 +148,16 @@ func TestParseEnvironmentVariable(t *testing.T) {
require.NoError(t, env.UnmarshalText(txt))
assert.True(t, envTest.Equal(env))
}

func TestEnvVar_ParseEnvironmentVariables(t *testing.T) {
username := faker.Username()
entries := []string{"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65357/bus", "HOME=/home/josjen01", faker.UUIDHyphenated(), "EDITOR=hx", "LOGNAME=josjen01", "DISPLAY=:0", "SSH_AUTH_SOCK=/tmp/ssh-eBrdhiWnaFYp/agent.4969", "KRB5CCNAME=FILE:/tmp/krb5cc_65357_XLwjEE", "GPG_AGENT_INFO=/run/user/65357/gnupg/S.gpg-agent:0:1", "LANGUAGE=en_US:", "USER=" + username, "XDG_RUNTIME_DIR=/run/user/65357", "WINDOWID=54525966", "KITTY_PID=151539", "CMSIS_PACK_ROOT=/home/josjen01/.cache/arm/packs", "XDG_SESSION_ID=4", "XDG_CONFIG_DIRS=/etc/xdg/xdg-i3:/etc/xdg", faker.Name(), "GDMSESSION=i3", "WINDOWPATH=2", "SHLVL=1", "DESKTOP_SESSION=i3", "GTK_MODULES=gail:atk-bridge", "LANG=en_US.UTF-8", "FZF_DEFAULT_OPTS=--color dark,hl:#d65d08,hl+:#d65d08,fg+:#282828,bg+:#282828,fg+:#b58900,info:#ebdbb2,prompt:#268bd2,pointer:#2aa198,marker:#d33682,spinner:#268bd2 -m", "XDG_SESSION_DESKTOP=i3", "XDG_SESSION_TYPE=x11", "KITTY_PUBLIC_KEY=1:^1R-7)Aw|}io+D^KqaYVJF0R&a!f&dpX}gSSEIH&", "XDG_SEAT=seat0", "TERM=xterm-kitty", "XDG_DATA_DIRS=/usr/share/i3:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop", "DESKTOP_STARTUP_ID=i3/kitty/4969-5-e126332_TIME31895328", "SHELL=/bin/bash", "KITTY_WINDOW_ID=6", "QT_ACCESSIBILITY=1", "COLORTERM=truecolor", "TERMINFO=/home/josjen01/.local/kitty.app/lib/kitty/terminfo", "SSH_AGENT_PID=5033", "XDG_SESSION_CLASS=user", "KITTY_INSTALLATION_DIR=/home/josjen01/.local/kitty.app/lib/kitty", "XDG_CURRENT_DESKTOP=i3", "MANPATH=:/home/josjen01/.local/kitty.app/share/man::/opt/puppetlabs/puppet/share/man", "XAUTHORITY=/run/user/65357/gdm/Xauthority", "CMSIS_COMPILER_ROOT=/etc/cmsis-build", "XDG_VTNR=2", "I3SOCK=/run/user/65357/i3/ipc-socket.4969", "USERNAME=josjen01", "PATH=/usr/local/go/bin:/bin:/home/josjen01/.local/bin:/home/josjen01/go/bin:/home/josjen01/.cargo/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/usr/games:/usr/local/games:/snap/bin:/opt/puppetlabs/bin", "PWD=/home/josjen01/Git/golang-utils/utils/environment", "GOCOVERDIR=/tmp/go-build1026478377/b001/gocoverdir", "test1=Accusantium voluptatem aut sit perferendis consequatur", "test2=Perferendis aut accusantium voluptatem sit consequatur.", faker.Word()}
environmentVariables := ParseEnvironmentVariables(entries...)
assert.NotEmpty(t, environmentVariables)
assert.Len(t, environmentVariables, len(entries)-3)
env, err := FindEnvironmentVariable("USER", environmentVariables...)
require.NoError(t, err)
assert.Equal(t, username, env.GetValue())
_, err = FindEnvironmentVariable("TEST1", environmentVariables...)
errortest.AssertError(t, err, commonerrors.ErrNotFound)
}

0 comments on commit 8671555

Please sign in to comment.