Skip to content

Commit 272344e

Browse files
committed
refactor: move remote code to separate file
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
1 parent 7162e92 commit 272344e

File tree

2 files changed

+257
-244
lines changed

2 files changed

+257
-244
lines changed

‎remote.go‎

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
package viper
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"reflect"
8+
)
9+
10+
// SupportedRemoteProviders are universally supported remote providers.
11+
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}
12+
13+
func resetRemote() {
14+
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}
15+
}
16+
17+
type remoteConfigFactory interface {
18+
Get(rp RemoteProvider) (io.Reader, error)
19+
Watch(rp RemoteProvider) (io.Reader, error)
20+
WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)
21+
}
22+
23+
type RemoteResponse struct {
24+
Value []byte
25+
Error error
26+
}
27+
28+
// RemoteConfig is optional, see the remote package.
29+
var RemoteConfig remoteConfigFactory
30+
31+
// UnsupportedRemoteProviderError denotes encountering an unsupported remote
32+
// provider. Currently only etcd and Consul are supported.
33+
type UnsupportedRemoteProviderError string
34+
35+
// Error returns the formatted remote provider error.
36+
func (str UnsupportedRemoteProviderError) Error() string {
37+
return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))
38+
}
39+
40+
// RemoteConfigError denotes encountering an error while trying to
41+
// pull the configuration from the remote provider.
42+
type RemoteConfigError string
43+
44+
// Error returns the formatted remote provider error.
45+
func (rce RemoteConfigError) Error() string {
46+
return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
47+
}
48+
49+
type defaultRemoteProvider struct {
50+
provider string
51+
endpoint string
52+
path string
53+
secretKeyring string
54+
}
55+
56+
func (rp defaultRemoteProvider) Provider() string {
57+
return rp.provider
58+
}
59+
60+
func (rp defaultRemoteProvider) Endpoint() string {
61+
return rp.endpoint
62+
}
63+
64+
func (rp defaultRemoteProvider) Path() string {
65+
return rp.path
66+
}
67+
68+
func (rp defaultRemoteProvider) SecretKeyring() string {
69+
return rp.secretKeyring
70+
}
71+
72+
// RemoteProvider stores the configuration necessary
73+
// to connect to a remote key/value store.
74+
// Optional secretKeyring to unencrypt encrypted values
75+
// can be provided.
76+
type RemoteProvider interface {
77+
Provider() string
78+
Endpoint() string
79+
Path() string
80+
SecretKeyring() string
81+
}
82+
83+
// 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:port
87+
// path is the path in the k/v store to retrieve configuration
88+
// To retrieve a config file called myapp.json from /configs/myapp.json
89+
// you should set path to /configs and set config name (SetConfigName()) to
90+
// "myapp".
91+
func AddRemoteProvider(provider, endpoint, path string) error {
92+
return v.AddRemoteProvider(provider, endpoint, path)
93+
}
94+
95+
func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
96+
if !stringInSlice(provider, SupportedRemoteProviders) {
97+
return UnsupportedRemoteProviderError(provider)
98+
}
99+
if provider != "" && endpoint != "" {
100+
v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)
101+
102+
rp := &defaultRemoteProvider{
103+
endpoint: endpoint,
104+
provider: provider,
105+
path: path,
106+
}
107+
if !v.providerPathExists(rp) {
108+
v.remoteProviders = append(v.remoteProviders, rp)
109+
}
110+
}
111+
return nil
112+
}
113+
114+
// 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:port
118+
// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
119+
// path is the path in the k/v store to retrieve configuration
120+
// To retrieve a config file called myapp.json from /configs/myapp.json
121+
// you should set path to /configs and set config name (SetConfigName()) to
122+
// "myapp".
123+
// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt.
124+
func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
125+
return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
126+
}
127+
128+
func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
129+
if !stringInSlice(provider, SupportedRemoteProviders) {
130+
return UnsupportedRemoteProviderError(provider)
131+
}
132+
if provider != "" && endpoint != "" {
133+
v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)
134+
135+
rp := &defaultRemoteProvider{
136+
endpoint: endpoint,
137+
provider: provider,
138+
path: path,
139+
secretKeyring: secretkeyring,
140+
}
141+
if !v.providerPathExists(rp) {
142+
v.remoteProviders = append(v.remoteProviders, rp)
143+
}
144+
}
145+
return nil
146+
}
147+
148+
func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
149+
for _, y := range v.remoteProviders {
150+
if reflect.DeepEqual(y, p) {
151+
return true
152+
}
153+
}
154+
return false
155+
}
156+
157+
// ReadRemoteConfig attempts to get configuration from a remote source
158+
// and read it in the remote configuration registry.
159+
func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
160+
161+
func (v *Viper) ReadRemoteConfig() error {
162+
return v.getKeyValueConfig()
163+
}
164+
165+
func WatchRemoteConfig() error { return v.WatchRemoteConfig() }
166+
func (v *Viper) WatchRemoteConfig() error {
167+
return v.watchKeyValueConfig()
168+
}
169+
170+
func (v *Viper) WatchRemoteConfigOnChannel() error {
171+
return v.watchKeyValueConfigOnChannel()
172+
}
173+
174+
// Retrieve the first found remote configuration.
175+
func (v *Viper) getKeyValueConfig() error {
176+
if RemoteConfig == nil {
177+
return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
178+
}
179+
180+
if len(v.remoteProviders) == 0 {
181+
return RemoteConfigError("No Remote Providers")
182+
}
183+
184+
for _, rp := range v.remoteProviders {
185+
val, err := v.getRemoteConfig(rp)
186+
if err != nil {
187+
v.logger.Error(fmt.Errorf("get remote config: %w", err).Error())
188+
189+
continue
190+
}
191+
192+
v.kvstore = val
193+
194+
return nil
195+
}
196+
return RemoteConfigError("No Files Found")
197+
}
198+
199+
func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) {
200+
reader, err := RemoteConfig.Get(provider)
201+
if err != nil {
202+
return nil, err
203+
}
204+
err = v.unmarshalReader(reader, v.kvstore)
205+
return v.kvstore, err
206+
}
207+
208+
// Retrieve the first found remote configuration.
209+
func (v *Viper) watchKeyValueConfigOnChannel() error {
210+
if len(v.remoteProviders) == 0 {
211+
return RemoteConfigError("No Remote Providers")
212+
}
213+
214+
for _, rp := range v.remoteProviders {
215+
respc, _ := RemoteConfig.WatchChannel(rp)
216+
// Todo: Add quit channel
217+
go func(rc <-chan *RemoteResponse) {
218+
for {
219+
b := <-rc
220+
reader := bytes.NewReader(b.Value)
221+
v.unmarshalReader(reader, v.kvstore)
222+
}
223+
}(respc)
224+
return nil
225+
}
226+
return RemoteConfigError("No Files Found")
227+
}
228+
229+
// Retrieve the first found remote configuration.
230+
func (v *Viper) watchKeyValueConfig() error {
231+
if len(v.remoteProviders) == 0 {
232+
return RemoteConfigError("No Remote Providers")
233+
}
234+
235+
for _, rp := range v.remoteProviders {
236+
val, err := v.watchRemoteConfig(rp)
237+
if err != nil {
238+
v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error())
239+
240+
continue
241+
}
242+
v.kvstore = val
243+
return nil
244+
}
245+
return RemoteConfigError("No Files Found")
246+
}
247+
248+
func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) {
249+
reader, err := RemoteConfig.Watch(provider)
250+
if err != nil {
251+
return nil, err
252+
}
253+
err = v.unmarshalReader(reader, v.kvstore)
254+
return v.kvstore, err
255+
}

0 commit comments

Comments
 (0)