Feature - Add env variable support for alpha struct (#2375)
* added envsubstring package and added simple test cases.imple tests. * added documentation * added changelog entry * added documentation to wrong file . * changed tests to ginkgo format * update project to use better maintained library * use defer to clear test variable after tests finished * updated docs for the new package documentation and fixed bad english * refactored function to "reduce" complexity. * updated changelog for new version updated readme * minor formatting --------- Co-authored-by: Haydn Evans <h.evans@douglas.de> Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>
This commit is contained in:
parent
ee3e9b8841
commit
2f3c811e6a
|
|
@ -28,6 +28,7 @@
|
||||||
- [#2295](https://github.com/oauth2-proxy/oauth2-proxy/pull/2295) Change base-image to [GoogleContainerTools/distroless](https://github.com/GoogleContainerTools/distroless) (@kvanzuijlen)
|
- [#2295](https://github.com/oauth2-proxy/oauth2-proxy/pull/2295) Change base-image to [GoogleContainerTools/distroless](https://github.com/GoogleContainerTools/distroless) (@kvanzuijlen)
|
||||||
- [#2356](https://github.com/oauth2-proxy/oauth2-proxy/pull/2356) Update go-jose dependency (@dasvh)
|
- [#2356](https://github.com/oauth2-proxy/oauth2-proxy/pull/2356) Update go-jose dependency (@dasvh)
|
||||||
- [#2357](https://github.com/oauth2-proxy/oauth2-proxy/pull/2357) Update ojg to latest release (@bitfehler)
|
- [#2357](https://github.com/oauth2-proxy/oauth2-proxy/pull/2357) Update ojg to latest release (@bitfehler)
|
||||||
|
- [#1922](https://github.com/oauth2-proxy/oauth2-proxy/pull/1922) Added support for env variables in the alpha struct (@hevans-dglcom)
|
||||||
|
|
||||||
# V7.5.1
|
# V7.5.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,20 @@ the new config.
|
||||||
oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
|
oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Using ENV variables in the alpha configuration
|
||||||
|
|
||||||
|
The alpha package supports the use of environment variables in place of yaml keys, allowing sensitive values to be pulled from somewhere other than the yaml file.
|
||||||
|
When using environment variables, your yaml will look like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
providers:
|
||||||
|
- provider: azure
|
||||||
|
clientSecret: ${CLIENT_SECRET}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
Where CLIENT_SECRET is an environment variable.
|
||||||
|
More information and available patterns can be found [here](https://github.com/a8m/envsubst#docs)
|
||||||
|
|
||||||
## Removed options
|
## Removed options
|
||||||
|
|
||||||
The following flags/options and their respective environment variables are no
|
The following flags/options and their respective environment variables are no
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,20 @@ the new config.
|
||||||
oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
|
oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Using ENV variables in the alpha configuration
|
||||||
|
|
||||||
|
The alpha package supports the use of environment variables in place of yaml keys, allowing sensitive values to be pulled from somewhere other than the yaml file.
|
||||||
|
When using environment variables, your yaml will look like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
providers:
|
||||||
|
- provider: azure
|
||||||
|
clientSecret: ${CLIENT_SECRET}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
Where CLIENT_SECRET is an environment variable.
|
||||||
|
More information and available patterns can be found [here](https://github.com/a8m/envsubst#docs)
|
||||||
|
|
||||||
## Removed options
|
## Removed options
|
||||||
|
|
||||||
The following flags/options and their respective environment variables are no
|
The following flags/options and their respective environment variables are no
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -5,6 +5,7 @@ go 1.19
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute/metadata v0.2.3
|
cloud.google.com/go/compute/metadata v0.2.3
|
||||||
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
|
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
|
||||||
|
github.com/a8m/envsubst v1.4.2
|
||||||
github.com/alicebob/miniredis/v2 v2.23.0
|
github.com/alicebob/miniredis/v2 v2.23.0
|
||||||
github.com/benbjohnson/clock v1.3.0
|
github.com/benbjohnson/clock v1.3.0
|
||||||
github.com/bitly/go-simplejson v0.5.1
|
github.com/bitly/go-simplejson v0.5.1
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -47,6 +47,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||||
github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
|
github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
|
||||||
github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
|
github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
|
||||||
|
github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/a8m/envsubst"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
@ -140,25 +141,37 @@ func isUnexported(name string) bool {
|
||||||
|
|
||||||
// LoadYAML will load a YAML based configuration file into the options interface provided.
|
// LoadYAML will load a YAML based configuration file into the options interface provided.
|
||||||
func LoadYAML(configFileName string, into interface{}) error {
|
func LoadYAML(configFileName string, into interface{}) error {
|
||||||
v := viper.New()
|
buffer, err := loadAndParseYaml(configFileName)
|
||||||
v.SetConfigFile(configFileName)
|
|
||||||
v.SetConfigType("yaml")
|
|
||||||
v.SetTypeByDefaultValue(true)
|
|
||||||
|
|
||||||
if configFileName == "" {
|
|
||||||
return errors.New("no configuration file provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := os.ReadFile(configFileName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to load config file: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalStrict will return an error if the config includes options that are
|
// UnmarshalStrict will return an error if the config includes options that are
|
||||||
// not mapped to felds of the into struct
|
// not mapped to fields of the into struct
|
||||||
if err := yaml.UnmarshalStrict(data, into, yaml.DisallowUnknownFields); err != nil {
|
if err := yaml.UnmarshalStrict(buffer, into, yaml.DisallowUnknownFields); err != nil {
|
||||||
return fmt.Errorf("error unmarshalling config: %w", err)
|
return fmt.Errorf("error unmarshalling config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Performs the heavy lifting of the LoadYaml function
|
||||||
|
func loadAndParseYaml(configFileName string) ([]byte, error) {
|
||||||
|
if configFileName == "" {
|
||||||
|
return nil, errors.New("no configuration file provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
unparsedBuffer, err := os.ReadFile(configFileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We now parse over the yaml with env substring, and fill in the ENV's
|
||||||
|
buffer, err := envsubst.Bytes(unparsedBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error in substituting env variables : %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -386,8 +386,13 @@ sub:
|
||||||
|
|
||||||
DescribeTable("LoadYAML",
|
DescribeTable("LoadYAML",
|
||||||
func(in loadYAMLTableInput) {
|
func(in loadYAMLTableInput) {
|
||||||
var configFileName string
|
// Set the required environment variables before running the test
|
||||||
|
os.Setenv("TESTUSER", "Alice")
|
||||||
|
|
||||||
|
// Unset the environment variables after running the test
|
||||||
|
defer os.Unsetenv("TESTUSER")
|
||||||
|
|
||||||
|
var configFileName string
|
||||||
if in.configFile != nil {
|
if in.configFile != nil {
|
||||||
By("Creating a config file")
|
By("Creating a config file")
|
||||||
configFile, err := os.CreateTemp("", "oauth2-proxy-test-config-file")
|
configFile, err := os.CreateTemp("", "oauth2-proxy-test-config-file")
|
||||||
|
|
@ -407,12 +412,14 @@ sub:
|
||||||
} else {
|
} else {
|
||||||
input = &TestOptions{}
|
input = &TestOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := LoadYAML(configFileName, input)
|
err := LoadYAML(configFileName, input)
|
||||||
if in.expectedErr != nil {
|
if in.expectedErr != nil {
|
||||||
Expect(err).To(MatchError(in.expectedErr.Error()))
|
Expect(err).To(MatchError(in.expectedErr.Error()))
|
||||||
} else {
|
} else {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(input).To(Equal(in.expectedOutput))
|
Expect(input).To(Equal(in.expectedOutput))
|
||||||
},
|
},
|
||||||
Entry("with a valid input", loadYAMLTableInput{
|
Entry("with a valid input", loadYAMLTableInput{
|
||||||
|
|
@ -466,6 +473,20 @@ sub:
|
||||||
expectedOutput: &TestOptions{},
|
expectedOutput: &TestOptions{},
|
||||||
expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go struct field TestOptions.StringSliceOption of type []string"),
|
expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go struct field TestOptions.StringSliceOption of type []string"),
|
||||||
}),
|
}),
|
||||||
|
Entry("with a config file containing environment variable references", loadYAMLTableInput{
|
||||||
|
configFile: []byte("stringOption: ${TESTUSER}"),
|
||||||
|
input: &TestOptions{},
|
||||||
|
expectedOutput: &TestOptions{
|
||||||
|
StringOption: "Alice",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Entry("with a config file containing env variable references, with a fallback value", loadYAMLTableInput{
|
||||||
|
configFile: []byte("stringOption: ${TESTUSER2=Bob}"),
|
||||||
|
input: &TestOptions{},
|
||||||
|
expectedOutput: &TestOptions{
|
||||||
|
StringOption: "Bob",
|
||||||
|
},
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue