Skip to content

Commit 820dac1

Browse files
✨ [environment] Add GetEnvironmentVariable() to try and fetch specific environment variable (#328)
<!-- Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved. SPDX-License-Identifier: Apache-2.0 --> ### Description <!-- Please add any detail or context that would be useful to a reviewer. --> - Add `GetEnvironmentVariable()` to try and fetch specific environment variable - Support `.env` files ### 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). --------- Co-authored-by: Adrien CABARBAYE <adrien.cabarbaye@arm.com>
1 parent 010e00a commit 820dac1

File tree

6 files changed

+191
-14
lines changed

6 files changed

+191
-14
lines changed

changes/20231010151706.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:sparkles: [`environment`] Add `GetEnvironmentVariable()` to fetch a specific environment variable

changes/20231010151811.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:sparkles: [`environment`] Extend `GetEnvironmentVariables()` to accept optional ...dotEnvFiles so that [.env](https://github.com/bkeepers/dotenv) files can be loaded into the current environment

utils/environment/current.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package environment
22

33
import (
4+
"fmt"
45
"os"
56
"os/user"
67

8+
"github.com/joho/godotenv"
79
"github.com/mitchellh/go-homedir"
810

11+
"github.com/ARM-software/golang-utils/utils/commonerrors"
912
"github.com/ARM-software/golang-utils/utils/filesystem"
1013
)
1114

@@ -25,7 +28,13 @@ func (c *currentEnv) GetCurrentUser() (currentUser *user.User) {
2528
return
2629
}
2730

28-
func (c *currentEnv) GetEnvironmentVariables() (variables []IEnvironmentVariable) {
31+
// GetEnvironmentVariables returns the current environment variable (and optionally those in the supplied in `dotEnvFiles`)
32+
// `dotEnvFiles` corresponds to `.env` files present on the machine and follows the mechanism described by https://github.com/bkeepers/dotenv
33+
func (c *currentEnv) GetEnvironmentVariables(dotEnvFiles ...string) (variables []IEnvironmentVariable) {
34+
if len(dotEnvFiles) > 0 { // if no args, then it will attempt to load .env
35+
_ = godotenv.Load(dotEnvFiles...) // ignore error (specifically on loading .env) consistent with config.LoadFromEnvironment
36+
}
37+
2938
curentEnv := os.Environ()
3039
for i := range curentEnv {
3140
envvar, err := ParseEnvironmentVariable(curentEnv[i])
@@ -41,6 +50,17 @@ func (c *currentEnv) GetFilesystem() filesystem.FS {
4150
return filesystem.NewStandardFileSystem()
4251
}
4352

53+
// GetEnvironmentVariable searches the current environment (and optionally dotEnvFiles) for a specific environment variable `envvar`.
54+
func (c *currentEnv) GetEnvironmentVariable(envvar string, dotEnvFiles ...string) (value IEnvironmentVariable, err error) {
55+
envvars := c.GetEnvironmentVariables(dotEnvFiles...)
56+
for i := range envvars {
57+
if envvars[i].GetKey() == envvar {
58+
return envvars[i], nil
59+
}
60+
}
61+
return nil, fmt.Errorf("%w: environment variable '%v' not set", commonerrors.ErrNotFound, envvar)
62+
}
63+
4464
// NewCurrentEnvironment returns system current environment.
4565
func NewCurrentEnvironment() IEnvironment {
4666
return &currentEnv{}

utils/environment/current_test.go

Lines changed: 134 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
package environment
22

33
import (
4+
"fmt"
45
"os"
56
"testing"
67

78
"github.com/bxcodec/faker/v3"
89
"github.com/stretchr/testify/assert"
910
"github.com/stretchr/testify/require"
11+
12+
"github.com/ARM-software/golang-utils/utils/commonerrors"
13+
"github.com/ARM-software/golang-utils/utils/commonerrors/errortest"
14+
"github.com/ARM-software/golang-utils/utils/filesystem"
15+
)
16+
17+
const (
18+
dotEnvPattern = ".env.*"
1019
)
1120

1221
func TestNewCurrentEnvironment(t *testing.T) {
@@ -20,11 +29,129 @@ func TestNewCurrentEnvironment(t *testing.T) {
2029
}
2130

2231
func Test_currentEnv_GetEnvironmentVariables(t *testing.T) {
23-
os.Clearenv()
24-
require.NoError(t, os.Setenv("test1", faker.Sentence()))
25-
require.NoError(t, os.Setenv("test2", faker.Sentence()))
26-
current := NewCurrentEnvironment()
27-
envVars := current.GetEnvironmentVariables()
28-
assert.Len(t, envVars, 2)
29-
assert.False(t, envVars[0].Equal(envVars[1]))
32+
t.Run("No dotenv files", func(t *testing.T) {
33+
os.Clearenv()
34+
require.NoError(t, os.Setenv("test1", faker.Sentence()))
35+
require.NoError(t, os.Setenv("test2", faker.Sentence()))
36+
current := NewCurrentEnvironment()
37+
envVars := current.GetEnvironmentVariables()
38+
assert.Len(t, envVars, 2)
39+
assert.False(t, envVars[0].Equal(envVars[1]))
40+
})
41+
42+
t.Run("With dotenv files", func(t *testing.T) {
43+
os.Clearenv()
44+
require.NoError(t, os.Setenv("test1", faker.Sentence()))
45+
require.NoError(t, os.Setenv("test2", faker.Sentence()))
46+
47+
dotenv1, err := filesystem.TempFileInTempDir(dotEnvPattern)
48+
require.NoError(t, err)
49+
defer func() { _ = dotenv1.Close() }()
50+
test3 := NewEnvironmentVariable("test3", faker.Sentence())
51+
_, err = dotenv1.WriteString(test3.String())
52+
require.NoError(t, err)
53+
err = dotenv1.Close()
54+
require.NoError(t, err)
55+
56+
dotenv2, err := filesystem.TempFileInTempDir(dotEnvPattern)
57+
require.NoError(t, err)
58+
defer func() { _ = dotenv2.Close() }()
59+
test4 := NewEnvironmentVariable("test4", faker.Sentence())
60+
_, err = dotenv2.WriteString(fmt.Sprintf("%v\n", test4.String()))
61+
require.NoError(t, err)
62+
test5 := NewEnvironmentVariable("test5", faker.Sentence())
63+
_, err = dotenv2.WriteString(fmt.Sprintf("%v\n", test5.String()))
64+
require.NoError(t, err)
65+
err = dotenv2.Close()
66+
require.NoError(t, err)
67+
68+
current := NewCurrentEnvironment()
69+
envVars := current.GetEnvironmentVariables(dotenv1.Name(), dotenv2.Name())
70+
assert.Len(t, envVars, 5)
71+
assert.False(t, envVars[0].Equal(envVars[1]))
72+
assert.True(t, envVars[2].Equal(test3))
73+
assert.True(t, envVars[3].Equal(test4))
74+
assert.True(t, envVars[4].Equal(test5))
75+
})
76+
}
77+
78+
func Test_currentenv_GetEnvironmentVariable(t *testing.T) {
79+
t.Run("Env var exists", func(t *testing.T) {
80+
os.Clearenv()
81+
test := NewEnvironmentVariable(faker.Word(), faker.Sentence())
82+
require.NoError(t, os.Setenv(test.GetKey(), test.GetValue()))
83+
84+
current := NewCurrentEnvironment()
85+
86+
actual, err := current.GetEnvironmentVariable(test.GetKey())
87+
assert.NoError(t, err)
88+
assert.Equal(t, test, actual)
89+
})
90+
91+
t.Run("Env var not exists", func(t *testing.T) {
92+
os.Clearenv()
93+
test := NewEnvironmentVariable(faker.Word(), faker.Sentence())
94+
current := NewCurrentEnvironment()
95+
96+
actual, err := current.GetEnvironmentVariable(faker.Word())
97+
errortest.AssertError(t, err, commonerrors.ErrNotFound)
98+
assert.NotEqual(t, test, actual)
99+
})
100+
101+
t.Run("With dotenv files", func(t *testing.T) {
102+
os.Clearenv()
103+
test1 := NewEnvironmentVariable("test1", faker.Sentence())
104+
test2 := NewEnvironmentVariable("test2", faker.Sentence())
105+
106+
require.NoError(t, os.Setenv(test1.GetKey(), test1.GetValue()))
107+
require.NoError(t, os.Setenv(test2.GetKey(), test2.GetValue()))
108+
109+
dotenv1, err := filesystem.TempFileInTempDir(dotEnvPattern)
110+
require.NoError(t, err)
111+
defer func() { _ = dotenv1.Close() }()
112+
test3 := NewEnvironmentVariable("test3", faker.Sentence())
113+
_, err = dotenv1.WriteString(test3.String())
114+
require.NoError(t, err)
115+
err = dotenv1.Close()
116+
require.NoError(t, err)
117+
118+
dotenv2, err := filesystem.TempFileInTempDir(dotEnvPattern)
119+
require.NoError(t, err)
120+
defer func() { _ = dotenv2.Close() }()
121+
test4 := NewEnvironmentVariable("test4", faker.Sentence())
122+
_, err = dotenv2.WriteString(fmt.Sprintf("%v\n", test4.String()))
123+
require.NoError(t, err)
124+
test5 := NewEnvironmentVariable("test5", faker.Sentence())
125+
_, err = dotenv2.WriteString(fmt.Sprintf("%v\n", test5.String()))
126+
require.NoError(t, err)
127+
err = dotenv2.Close()
128+
require.NoError(t, err)
129+
130+
current := NewCurrentEnvironment()
131+
test1Actual, err := current.GetEnvironmentVariable(test1.GetKey(), dotenv1.Name(), dotenv2.Name())
132+
assert.NoError(t, err)
133+
assert.Equal(t, test1, test1Actual)
134+
test2Actual, err := current.GetEnvironmentVariable(test2.GetKey(), dotenv1.Name(), dotenv2.Name())
135+
assert.NoError(t, err)
136+
assert.Equal(t, test2, test2Actual)
137+
test3Actual, err := current.GetEnvironmentVariable(test3.GetKey(), dotenv1.Name(), dotenv2.Name())
138+
assert.NoError(t, err)
139+
assert.Equal(t, test3, test3Actual)
140+
test4Actual, err := current.GetEnvironmentVariable(test4.GetKey(), dotenv1.Name(), dotenv2.Name())
141+
assert.NoError(t, err)
142+
assert.Equal(t, test4, test4Actual)
143+
test5Actual, err := current.GetEnvironmentVariable(test5.GetKey(), dotenv1.Name(), dotenv2.Name())
144+
assert.NoError(t, err)
145+
assert.Equal(t, test5, test5Actual)
146+
147+
os.Clearenv()
148+
149+
test3Missing, err := current.GetEnvironmentVariable(test3.GetKey(), dotenv2.Name())
150+
errortest.AssertError(t, err, commonerrors.ErrNotFound)
151+
assert.NotEqual(t, test3, test3Missing)
152+
153+
testMissing, err := current.GetEnvironmentVariable(faker.Word(), dotenv1.Name(), dotenv2.Name())
154+
errortest.AssertError(t, err, commonerrors.ErrNotFound)
155+
assert.Nil(t, testMissing)
156+
})
30157
}

utils/environment/interfaces.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ type IEnvironmentVariable interface {
3030
type IEnvironment interface {
3131
// GetCurrentUser returns the environment current user.
3232
GetCurrentUser() *user.User
33-
// GetEnvironmentVariables returns the variables defining the environment.
34-
GetEnvironmentVariables() []IEnvironmentVariable
33+
// GetEnvironmentVariables returns the variables defining the environment (and optionally those supplied in `dotEnvFiles`)
34+
// `dotEnvFiles` corresponds to `.env` files present on the machine and follows the mechanism described by https://github.com/bkeepers/dotenv
35+
GetEnvironmentVariables(dotEnvFiles ...string) []IEnvironmentVariable
3536
// GetFilesystem returns the filesystem associated with the current environment
3637
GetFilesystem() filesystem.FS
38+
// GetEnvironmentVariable returns the environment variable corresponding to `envvar` or an error if it not set. optionally it searches `dotEnvFiles` files too
39+
// `dotEnvFiles` corresponds to `.env` files present on the machine and follows the mechanism described by https://github.com/bkeepers/dotenv
40+
GetEnvironmentVariable(envvar string, dotEnvFiles ...string) (IEnvironmentVariable, error)
3741
}

utils/mocks/mock_environment.go

Lines changed: 28 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)