Mutating Function
farrukhny opened this issue · 3 comments
I think it will be great to implement mutator func. It will be useful if need to modify environment variable values before processing so developer can specify a custom mutator func.
One of use case can be to send a request to secret storage (like HashiCorp Vault) to retrieve secrets and update the env var before process it.
So we can create new type
type MutatorFunc func(key, value string) (string, error)
and new ProcessWithMutator func
func ProcessWithMutator(args []string, namespace string, cfgStruct interface{}, mutators ...MutatorFunc) error { return Parse(args, namespace, cfgStruct, mutators) }
and need to update Parse func to work with mutator
Why would a custom Sourcer not provide a solution?
// Sourcer provides the ability to source data from a configuration source.
// Consider the use of lazy-loading for sourcing large datasets or systems.
type Sourcer interface {
// Source takes the field key and attempts to locate that key in its
// configuration data. Returns true if found with the value.
Source(fld Field) (string, bool)
}
// Parse parses configuration into the provided struct.
func Parse(args []string, namespace string, cfgStruct interface{}, sources ...Sourcer) error {
I think using Sourcer to implement solution will require more work than simple writing small custom func which will be used to implement specific use case so for example I importing conf pkg and my Source is environment variables but I want to update the value of VAULT_KEY1=secret/db-password-vault so I can just create custom func for example like this
func getSecretFromVault(key, value string) (string, error) {
if strings.HasPrefix(key, "VAULT") {
return someSecretPkg.Retrive(value)
}
return value, nil
}
conf.ProcessWithMutator(os.Args[1:], app, &cfg, getSecretFromVault)
I don't think that API design is right. It's best to have conf.Parse do it all in that one call. With a Sourcer, that is possible.
type Vault struct {
// Field Set
}
func (v Vault) Source(fld Field) (string, bool) {
if strings.HasPrefix(fld.Name, "VAULT") {
return someSecretPkg.Retrive(value), true
}
return "", false
}
func main() {
v := Vault{
/*Configure*/
}
if err := conf.Parse(os.Args[1:], "SALES", &cfg, v); err != nil {
}
}