Skip to content

Commit d704ee6

Browse files
authored
Merge pull request #12 from chai2010/dev
refactoring module struct
2 parents 73b56ad + 3de9d7b commit d704ee6

27 files changed

+297
-11
lines changed

.travis.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,11 @@ go:
1111
go_import_path: openpitrix.io/libconfd
1212

1313
script:
14-
- env GO111MODULE=on go test ./...
15-
- env GO111MODULE=on go vet ./...
14+
- env GO111MODULE=on go test openpitrix.io/libconfd/...
15+
- env GO111MODULE=on go vet openpitrix.io/libconfd/...
16+
17+
- env GO111MODULE=on go test openpitrix.io/libconfd/etcdv3/...
18+
- env GO111MODULE=on go vet openpitrix.io/libconfd/etcdv3/...
19+
20+
- env GO111MODULE=on go test openpitrix.io/libconfd/metad/...
21+
- env GO111MODULE=on go vet openpitrix.io/libconfd/metad/...

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ func main() {
3333
$ go run miniconfd.go -h
3434
```
3535

36-
See [miniconfd.go](miniconfd.go)
36+
See [etcdv3/miniconfd-etcdv3/miniconfd.go](etcdv3/miniconfd-etcdv3/miniconfd.go)

confd-backend.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type = "libconfd-backend-toml"
77

88
# backend address
99
host = [
10-
"./confd/backend-file.toml",
10+
"./testdata/confd/backend-file.toml",
1111
]
1212

1313
# user name/password

confd.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#
88
# abspath = filepath.Join(ConfigPath, Config.ConfDir)
99
#
10-
confdir = "./confd"
10+
confdir = "./testdata/confd"
1111

1212
# The backend polling interval in seconds. (10)
1313
interval = 10

etcdv3/backends/etcdv3.go renamed to etcdv3/backend/etcdv3.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a Apache license
33
// that can be found in the LICENSE file.
44

5-
package backends
5+
package backend_etcdv3
66

77
import (
88
"context"

etcdv3/miniconfd-etcdv3/miniconfd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/urfave/cli"
1313

1414
"openpitrix.io/libconfd"
15-
_ "openpitrix.io/libconfd/etcdv3/backends"
15+
_ "openpitrix.io/libconfd/etcdv3/backend"
1616
)
1717

1818
func main() {

metad/backend/metad.go

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
// Copyright 2018 The OpenPitrix Authors. All rights reserved.
2+
// Use of this source code is governed by a Apache license
3+
// that can be found in the LICENSE file.
4+
5+
// Copyright 2018 Yunify Inc. All rights reserved.
6+
// Use of this source code is governed by a Apache license
7+
// that can be found in the LICENSE file.
8+
9+
// copy from metad/pkg/client/client.go
10+
11+
package backend_metad
12+
13+
import (
14+
"container/ring"
15+
"context"
16+
"encoding/json"
17+
"errors"
18+
"fmt"
19+
"io/ioutil"
20+
"math/rand"
21+
"net"
22+
"net/http"
23+
"reflect"
24+
"strconv"
25+
"strings"
26+
"sync/atomic"
27+
"time"
28+
29+
"openpitrix.io/libconfd"
30+
)
31+
32+
var (
33+
_ libconfd.BackendClient = (*MetadClient)(nil)
34+
logger = libconfd.GetLogger()
35+
)
36+
37+
const MetadBackendType = "libconfd-backend-metad"
38+
39+
func init() {
40+
libconfd.RegisterBackendClient(
41+
MetadBackendType,
42+
func(cfg *libconfd.BackendConfig) (libconfd.BackendClient, error) {
43+
return NewMetadClient(cfg.Host)
44+
},
45+
)
46+
}
47+
48+
type MetadClient struct {
49+
connections *ring.Ring
50+
current *MetadConnection
51+
}
52+
53+
type MetadConnection struct {
54+
url string
55+
httpClient *http.Client
56+
waitIndex uint64
57+
errTimes uint32
58+
}
59+
60+
func (c *MetadClient) Type() string { return MetadBackendType }
61+
func (c *MetadClient) WatchEnabled() bool { return true }
62+
func (c *MetadClient) Close() error { return nil }
63+
64+
func NewMetadClient(backendNodes []string) (*MetadClient, error) {
65+
connections := ring.New(len(backendNodes))
66+
for _, backendNode := range backendNodes {
67+
url := "http://" + backendNode
68+
connection := &MetadConnection{
69+
url: url,
70+
httpClient: &http.Client{
71+
Transport: &http.Transport{
72+
Proxy: http.ProxyFromEnvironment,
73+
DialContext: (&net.Dialer{
74+
KeepAlive: 1 * time.Second,
75+
DualStack: true,
76+
}).DialContext,
77+
},
78+
},
79+
}
80+
connections.Value = connection
81+
connections = connections.Next()
82+
}
83+
84+
client := &MetadClient{
85+
connections: connections,
86+
}
87+
88+
err := client.selectConnection()
89+
90+
return client, err
91+
}
92+
93+
func (c *MetadClient) selectConnection() error {
94+
maxTime := 15 * time.Second
95+
i := 1 * time.Second
96+
for ; i < maxTime; i *= time.Duration(2) {
97+
if conn, err := c.testConnection(); err == nil {
98+
//found available conn
99+
if c.current != nil {
100+
atomic.StoreUint32(&c.current.errTimes, 0)
101+
}
102+
c.current = conn
103+
break
104+
}
105+
time.Sleep(i)
106+
}
107+
if i >= maxTime {
108+
return fmt.Errorf("fail to connect any backend.")
109+
}
110+
logger.Info("Using Metad URL: " + c.current.url)
111+
return nil
112+
}
113+
114+
func (c *MetadClient) testConnection() (*MetadConnection, error) {
115+
//random start
116+
if c.current == nil {
117+
rand.Seed(time.Now().Unix())
118+
r := rand.Intn(c.connections.Len())
119+
c.connections = c.connections.Move(r)
120+
}
121+
c.connections = c.connections.Next()
122+
conn := c.connections.Value.(*MetadConnection)
123+
startConn := conn
124+
_, err := conn.makeMetaDataRequest("/")
125+
for err != nil {
126+
logger.Error("connection to [%s], error: [%v]", conn.url, err)
127+
c.connections = c.connections.Next()
128+
conn = c.connections.Value.(*MetadConnection)
129+
if conn == startConn {
130+
break
131+
}
132+
_, err = conn.makeMetaDataRequest("/")
133+
}
134+
return conn, err
135+
}
136+
137+
func (c *MetadClient) GetValues(keys []string) (map[string]string, error) {
138+
vars := map[string]string{}
139+
140+
for _, key := range keys {
141+
body, err := c.current.makeMetaDataRequest(key)
142+
if err != nil {
143+
atomic.AddUint32(&c.current.errTimes, 1)
144+
return vars, err
145+
}
146+
147+
var jsonResponse interface{}
148+
if err = json.Unmarshal(body, &jsonResponse); err != nil {
149+
return vars, err
150+
}
151+
152+
if err = treeWalk(key, jsonResponse, vars); err != nil {
153+
return vars, err
154+
}
155+
}
156+
return vars, nil
157+
}
158+
159+
func treeWalk(root string, val interface{}, vars map[string]string) error {
160+
switch val.(type) {
161+
case map[string]interface{}:
162+
for k := range val.(map[string]interface{}) {
163+
treeWalk(strings.Join([]string{root, k}, "/"), val.(map[string]interface{})[k], vars)
164+
}
165+
case []interface{}:
166+
for i, item := range val.([]interface{}) {
167+
idx := strconv.Itoa(i)
168+
if i, isMap := item.(map[string]interface{}); isMap {
169+
if name, exists := i["name"]; exists {
170+
idx = name.(string)
171+
}
172+
}
173+
174+
treeWalk(strings.Join([]string{root, idx}, "/"), item, vars)
175+
}
176+
case bool:
177+
vars[root] = strconv.FormatBool(val.(bool))
178+
case string:
179+
vars[root] = val.(string)
180+
case float64:
181+
vars[root] = strconv.FormatFloat(val.(float64), 'f', -1, 64)
182+
case nil:
183+
vars[root] = "null"
184+
default:
185+
logger.Error("Unknown type: " + reflect.TypeOf(val).Name())
186+
}
187+
return nil
188+
}
189+
190+
func (c *MetadClient) WatchPrefix(prefix string, keys []string, waitIndex uint64, stopChan chan bool) (uint64, error) {
191+
if c.current.errTimes >= 3 {
192+
c.selectConnection()
193+
}
194+
195+
conn := c.current
196+
197+
// return something > 0 to trigger a key retrieval from the store
198+
if waitIndex == 0 {
199+
conn.waitIndex = 1
200+
return conn.waitIndex, nil
201+
}
202+
// when switch to anther server, so set waitIndex 0, and let server response current version.
203+
if conn.waitIndex == 0 {
204+
waitIndex = 0
205+
}
206+
207+
done := make(chan struct{})
208+
defer close(done)
209+
ctx, cancel := context.WithCancel(context.Background())
210+
req, err := http.NewRequest("GET", fmt.Sprintf("%s%s?wait=true&prev_version=%d", conn.url, prefix, waitIndex), nil)
211+
if err != nil {
212+
return conn.waitIndex, err
213+
}
214+
215+
req.Header.Set("Accept", "application/json")
216+
req = req.WithContext(ctx)
217+
go func() {
218+
select {
219+
case <-stopChan:
220+
cancel()
221+
case <-done:
222+
return
223+
}
224+
}()
225+
226+
// just ignore resp, notify confd to reload metadata from metad
227+
resp, err := conn.httpClient.Do(req)
228+
if resp != nil {
229+
defer resp.Body.Close()
230+
}
231+
if err != nil {
232+
logger.Error("failed to connect to metad when watch prefix")
233+
atomic.AddUint32(&conn.errTimes, 1)
234+
return conn.waitIndex, err
235+
}
236+
if resp.StatusCode != 200 {
237+
return conn.waitIndex, errors.New(fmt.Sprintf("metad response status [%v], requestID: [%s]", resp.StatusCode, resp.Header.Get("X-Metad-RequestID")))
238+
}
239+
versionStr := resp.Header.Get("X-Metad-OpVersion")
240+
if versionStr != "" {
241+
v, err := strconv.ParseUint(versionStr, 10, 64)
242+
if err != nil {
243+
logger.Error("Parse X-Metad-OpVersion %s error:%s", versionStr, err.Error())
244+
}
245+
conn.waitIndex = v
246+
} else {
247+
logger.Warning("Metad response miss X-Metad-OpVersion header.")
248+
conn.waitIndex = conn.waitIndex + 1
249+
}
250+
return conn.waitIndex, nil
251+
}
252+
253+
func (c *MetadConnection) makeMetaDataRequest(path string) ([]byte, error) {
254+
req, err := http.NewRequest("GET", strings.Join([]string{c.url, path}, ""), nil)
255+
if err != nil {
256+
return nil, err
257+
}
258+
req.Header.Set("Accept", "application/json")
259+
260+
resp, err := c.httpClient.Do(req)
261+
if err != nil {
262+
return nil, err
263+
}
264+
defer resp.Body.Close()
265+
266+
return ioutil.ReadAll(resp.Body)
267+
}

metad/dummy.txt

Whitespace-only changes.

metad/go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2018 The OpenPitrix Authors. All rights reserved.
2+
// Use of this source code is governed by a Apache license
3+
// that can be found in the LICENSE file.
4+
5+
module openpitrix.io/libconfd/metad
6+
7+
require openpitrix.io/libconfd v0.1.0

metad/go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
2+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3+
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e h1:IzypfodbhbnViNUO/MEh0FzCUooG97cIGfdggUrUSyU=
4+
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
5+
openpitrix.io/libconfd v0.1.0 h1:r3jK8TNCQ9jmp796xzBzQ8vQH9astI4gCPPXKNR9Z40=
6+
openpitrix.io/libconfd v0.1.0/go.mod h1:LfoWsMYAvA/Xw4XOldge+cwt5Zux1+y4ch8T80OkONQ=

0 commit comments

Comments
 (0)