Path: blob/main/vendor/github.com/spf13/viper/remote.go
2875 views
package viper12import (3"bytes"4"fmt"5"io"6"reflect"7"slices"8)910// SupportedRemoteProviders are universally supported remote providers.11var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}1213func resetRemote() {14SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}15}1617type remoteConfigFactory interface {18Get(rp RemoteProvider) (io.Reader, error)19Watch(rp RemoteProvider) (io.Reader, error)20WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)21}2223type RemoteResponse struct {24Value []byte25Error error26}2728// RemoteConfig is optional, see the remote package.29var RemoteConfig remoteConfigFactory3031// UnsupportedRemoteProviderError denotes encountering an unsupported remote32// provider. Currently only etcd and Consul are supported.33type UnsupportedRemoteProviderError string3435// Error returns the formatted remote provider error.36func (str UnsupportedRemoteProviderError) Error() string {37return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))38}3940// RemoteConfigError denotes encountering an error while trying to41// pull the configuration from the remote provider.42type RemoteConfigError string4344// Error returns the formatted remote provider error.45func (rce RemoteConfigError) Error() string {46return fmt.Sprintf("Remote Configurations Error: %s", string(rce))47}4849type defaultRemoteProvider struct {50provider string51endpoint string52path string53secretKeyring string54}5556func (rp defaultRemoteProvider) Provider() string {57return rp.provider58}5960func (rp defaultRemoteProvider) Endpoint() string {61return rp.endpoint62}6364func (rp defaultRemoteProvider) Path() string {65return rp.path66}6768func (rp defaultRemoteProvider) SecretKeyring() string {69return rp.secretKeyring70}7172// RemoteProvider stores the configuration necessary73// to connect to a remote key/value store.74// Optional secretKeyring to unencrypt encrypted values75// can be provided.76type RemoteProvider interface {77Provider() string78Endpoint() string79Path() string80SecretKeyring() string81}8283// AddRemoteProvider adds a remote configuration source.84// Remote Providers are searched in the order they are added.85// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.86// endpoint is the url. etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port87// path is the path in the k/v store to retrieve configuration88// To retrieve a config file called myapp.json from /configs/myapp.json89// you should set path to /configs and set config name (SetConfigName()) to90// "myapp".91func AddRemoteProvider(provider, endpoint, path string) error {92return v.AddRemoteProvider(provider, endpoint, path)93}9495func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {96if !slices.Contains(SupportedRemoteProviders, provider) {97return UnsupportedRemoteProviderError(provider)98}99if provider != "" && endpoint != "" {100v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)101102rp := &defaultRemoteProvider{103endpoint: endpoint,104provider: provider,105path: path,106}107if !v.providerPathExists(rp) {108v.remoteProviders = append(v.remoteProviders, rp)109}110}111return nil112}113114// AddSecureRemoteProvider adds a remote configuration source.115// Secure Remote Providers are searched in the order they are added.116// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.117// endpoint is the url. etcd requires http://ip:port consul requires ip:port118// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg119// path is the path in the k/v store to retrieve configuration120// To retrieve a config file called myapp.json from /configs/myapp.json121// you should set path to /configs and set config name (SetConfigName()) to122// "myapp".123// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt.124func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {125return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)126}127128func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {129if !slices.Contains(SupportedRemoteProviders, provider) {130return UnsupportedRemoteProviderError(provider)131}132if provider != "" && endpoint != "" {133v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)134135rp := &defaultRemoteProvider{136endpoint: endpoint,137provider: provider,138path: path,139secretKeyring: secretkeyring,140}141if !v.providerPathExists(rp) {142v.remoteProviders = append(v.remoteProviders, rp)143}144}145return nil146}147148func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {149for _, y := range v.remoteProviders {150if reflect.DeepEqual(y, p) {151return true152}153}154return false155}156157// ReadRemoteConfig attempts to get configuration from a remote source158// and read it in the remote configuration registry.159func ReadRemoteConfig() error { return v.ReadRemoteConfig() }160161func (v *Viper) ReadRemoteConfig() error {162return v.getKeyValueConfig()163}164165func WatchRemoteConfig() error { return v.WatchRemoteConfig() }166func (v *Viper) WatchRemoteConfig() error {167return v.watchKeyValueConfig()168}169170func (v *Viper) WatchRemoteConfigOnChannel() error {171return v.watchKeyValueConfigOnChannel()172}173174// Retrieve the first found remote configuration.175func (v *Viper) getKeyValueConfig() error {176if RemoteConfig == nil {177return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")178}179180if len(v.remoteProviders) == 0 {181return RemoteConfigError("No Remote Providers")182}183184for _, rp := range v.remoteProviders {185val, err := v.getRemoteConfig(rp)186if err != nil {187v.logger.Error(fmt.Errorf("get remote config: %w", err).Error())188189continue190}191192v.kvstore = val193194return nil195}196return RemoteConfigError("No Files Found")197}198199func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) {200reader, err := RemoteConfig.Get(provider)201if err != nil {202return nil, err203}204err = v.unmarshalReader(reader, v.kvstore)205return v.kvstore, err206}207208// Retrieve the first found remote configuration.209func (v *Viper) watchKeyValueConfigOnChannel() error {210if len(v.remoteProviders) == 0 {211return RemoteConfigError("No Remote Providers")212}213214for _, rp := range v.remoteProviders {215respc, _ := RemoteConfig.WatchChannel(rp)216// Todo: Add quit channel217go func(rc <-chan *RemoteResponse) {218for {219b := <-rc220reader := bytes.NewReader(b.Value)221err := v.unmarshalReader(reader, v.kvstore)222if err != nil {223v.logger.Error(fmt.Errorf("failed to unmarshal remote config: %w", err).Error())224}225}226}(respc)227return nil228}229return RemoteConfigError("No Files Found")230}231232// Retrieve the first found remote configuration.233func (v *Viper) watchKeyValueConfig() error {234if len(v.remoteProviders) == 0 {235return RemoteConfigError("No Remote Providers")236}237238for _, rp := range v.remoteProviders {239val, err := v.watchRemoteConfig(rp)240if err != nil {241v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error())242243continue244}245v.kvstore = val246return nil247}248return RemoteConfigError("No Files Found")249}250251func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) {252reader, err := RemoteConfig.Watch(provider)253if err != nil {254return nil, err255}256err = v.unmarshalReader(reader, v.kvstore)257return v.kvstore, err258}259260261