In ReadRemoteConfig, that have no less than one remote providers, but only retrieve the first found configuration. Is there a specific reason?

// Retrieve the first found remote configuration.
func (v *Viper) getKeyValueConfig() error {
    if RemoteConfig == nil {
        return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
    }

    for _, rp := range v.remoteProviders {
        val, err := v.getRemoteConfig(rp)
        if err != nil {
            continue
        }
        v.kvstore = val
        return nil
    }
    return RemoteConfigError("No Files Found")
}

Comment From: kingmorning

maybe should modify the line: v.kvstore = val
merge the old value and new value. v.kvstore should store all val from remoteProviders.

Comment From: gangxie112

So, due to this, there is no away to load multiple remote config. Wonder why it's designed/implemented like this? really unthoughtful.

Comment From: iatoz

I can confirm this issue still exists in v1.20.1. I've just spent some time debugging this exact problem and traced it back to the getKeyValueConfig function in remote.go.

The analysis from the original poster is spot on. The function has two main issues:

  1. Assignment instead of Merging: The line v.kvstore = val overwrites the entire configuration map with the values from the current provider, rather than merging them.
  2. Premature Return: The return nil statement at the end of the loop causes the function to exit after successfully reading the very first provider, ignoring all others in the v.remoteProviders slice. Suggested Fix

A potential fix would involve iterating through all providers, fetching their configuration, and merging the results into v.kvstore.

Here is a conceptual implementation of the fix:

// In getKeyValueConfig:
// ...


for _, rp := range v.remoteProviders {
    ...

    // Merge val into v.kvstore
    for k, v := range val {
        v.kvstore[k] = v
    }
}

return nil // Return after the loop finishes

// In getRemoteConfig:
func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) {
    reader, err := RemoteConfig.Get(provider)
    if err != nil {
        return nil, err
    }

    var newVals map[string]any
    err = v.unmarshalReader(reader, &newVals)
    return newVals, err
}