From 279ddb7e669d56d373507ec5ede8804af747a98f Mon Sep 17 00:00:00 2001 From: tian Date: Mon, 2 Dec 2019 17:09:59 +0800 Subject: [PATCH] migrate go-chassis-config into archaius project --- README.md | 14 +- archaius.go | 7 +- go.mod | 5 +- go.sum | 4 + options.go | 7 +- pkg/configcenter/client.go | 443 ++++++++++++++++++ pkg/configcenter/client_test.go | 10 + pkg/configcenter/options.go | 53 +++ pkg/configcenter/struct.go | 36 ++ pkg/configcenter/util_test.go | 28 ++ pkg/maputil/util.go | 23 - pkg/maputil/util_test.go | 14 - pkg/serializers/json/json.go | 60 +++ pkg/serializers/json/json_test.go | 32 ++ pkg/serializers/types.go | 70 +++ pkg/serializers/types_test.go | 50 ++ source/remote/client.go | 55 +++ source/remote/client_test.go | 16 + source/remote/configcenter/config_center.go | 185 ++++++++ .../remote/configcenter/config_center_test.go | 16 + source/remote/configcenter/struct.go | 14 + source/remote/configcenter/util.go | 63 +++ source/remote/configcenter/util_test.go | 32 ++ source/remote/options.go | 17 + source/remote/remote.go | 5 +- source/remote/remote_test.go | 13 +- 26 files changed, 1211 insertions(+), 61 deletions(-) create mode 100644 pkg/configcenter/client.go create mode 100755 pkg/configcenter/client_test.go create mode 100644 pkg/configcenter/options.go create mode 100644 pkg/configcenter/struct.go create mode 100644 pkg/configcenter/util_test.go delete mode 100644 pkg/maputil/util.go delete mode 100644 pkg/maputil/util_test.go create mode 100755 pkg/serializers/json/json.go create mode 100755 pkg/serializers/json/json_test.go create mode 100755 pkg/serializers/types.go create mode 100755 pkg/serializers/types_test.go create mode 100644 source/remote/client.go create mode 100644 source/remote/client_test.go create mode 100755 source/remote/configcenter/config_center.go create mode 100755 source/remote/configcenter/config_center_test.go create mode 100644 source/remote/configcenter/struct.go create mode 100644 source/remote/configcenter/util.go create mode 100644 source/remote/configcenter/util_test.go create mode 100644 source/remote/options.go diff --git a/README.md b/README.md index ac5fd7cd..e1ba86aa 100755 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ below example also enables env and mem sources. ``` ### Put value into archaius -Notice, key value will be only put into memory source, it could be overwritten by remote config +Notice, key value will be only put into memory source, it could be overwritten by remote config as the precedence list ```go archaius.Set("interval", 30) archaius.Set("ttl", "30s") @@ -109,7 +109,7 @@ v := archaius.GetString("/etc/component/xxx.txt", "") ### Enable remote source import a config client implementation ```go -import _ "github.com/go-chassis/go-chassis-config/servicecomb" +import _ "github.com/go-chassis/go-archaius/source/remote/configcenter" ``` set config client to init config center source ```go @@ -117,7 +117,7 @@ set config client to init config center source //input your remote source config } //create config client - cc,_:=ccclient.NewClient("servicecomb-kie",ccclient.Options{ + cc,_:=remote.NewClient("config_center",ccclient.Options{ ServerURI:"the address of config server endpoint", }) //manage local and remote key value at same time @@ -128,8 +128,12 @@ set config client to init config center source ) ``` -To check config server that archaius supports, -access https://github.com/go-chassis/go-chassis-config +Supported distributed configuration management service: + +| name | import |description | +|----------|----------|:-------------:| +|config_center |github.com/go-chassis/go-chassis-config/configcenter |huawei cloud CSE config center https://www.huaweicloud.com/product/cse.html | +|servicecomb-kie |github.com/apache/servicecomb-kie/client/adaptor |apache servicecomb-kie https://github.com/apache/servicecomb-kie | ### Example: Manage local configurations Complete [example](https://github.com/go-chassis/go-archaius/tree/master/examples/file) diff --git a/archaius.go b/archaius.go index c01f457d..ed79815f 100755 --- a/archaius.go +++ b/archaius.go @@ -15,7 +15,6 @@ import ( "github.com/go-chassis/go-archaius/source/file" "github.com/go-chassis/go-archaius/source/mem" "github.com/go-chassis/go-archaius/source/remote" - "github.com/go-chassis/go-chassis-config" "github.com/go-mesh/openlogging" ) @@ -119,7 +118,7 @@ func CustomInit(sources ...source.ConfigSource) error { //EnableRemoteSource create a remote source singleton //A config center source pull remote config server key values into local memory //so that you can use GetXXX to get value easily -func EnableRemoteSource(ci *RemoteInfo, cc config.Client) error { +func EnableRemoteSource(ci *RemoteInfo, cc remote.Client) error { if ci == nil { return errors.New("RemoteInfo can not be empty") } @@ -130,7 +129,7 @@ func EnableRemoteSource(ci *RemoteInfo, cc config.Client) error { var err error if cc == nil { - opts := config.Options{ + opts := remote.Options{ ServerURI: ci.URL, TenantName: ci.TenantName, EnableSSL: ci.EnableSSL, @@ -139,7 +138,7 @@ func EnableRemoteSource(ci *RemoteInfo, cc config.Client) error { AutoDiscovery: ci.AutoDiscovery, Labels: ci.DefaultDimension, } - cc, err = config.NewClient(ci.ClientType, opts) + cc, err = remote.NewClient(ci.ClientType, opts) if err != nil { return err } diff --git a/go.mod b/go.mod index d9feaaa6..52defb1c 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,13 @@ module github.com/go-chassis/go-archaius require ( github.com/fsnotify/fsnotify v1.4.7 - github.com/go-chassis/go-chassis-config v0.14.0 + github.com/go-chassis/foundation v0.1.1-0.20191113114104-2b05871e9ec4 github.com/go-mesh/openlogging v1.0.1 + github.com/gorilla/websocket v1.4.0 github.com/spf13/cast v1.2.0 github.com/stretchr/testify v1.3.0 golang.org/x/sys v0.0.0-20191018095205-727590c5006e // indirect gopkg.in/yaml.v2 v2.2.1 ) + +go 1.13 diff --git a/go.sum b/go.sum index 1b922800..65604ff1 100644 --- a/go.sum +++ b/go.sum @@ -2,11 +2,15 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-chassis/foundation v0.0.0-20190621030543-c3b63f787f4c h1:p+Y6yq7RwHmYjEr/vwdVYGacBqFCc2lPQfNRIC3vRIs= github.com/go-chassis/foundation v0.0.0-20190621030543-c3b63f787f4c/go.mod h1:21/ajGtgJlWTCeM0TxGJdRhO8bJkKirWyV8Stlh6g6c= +github.com/go-chassis/foundation v0.1.1-0.20191113114104-2b05871e9ec4 h1:wx8JXvg/n4i8acXsBJ5zIkiK7EO2kn/HuEjKK3kSgv8= +github.com/go-chassis/foundation v0.1.1-0.20191113114104-2b05871e9ec4/go.mod h1:21/ajGtgJlWTCeM0TxGJdRhO8bJkKirWyV8Stlh6g6c= github.com/go-chassis/go-chassis-config v0.14.0 h1:OnM9sx2GalDC7vEIhPecRpQlVa8hz10NOB41+9tii5A= github.com/go-chassis/go-chassis-config v0.14.0/go.mod h1:qzvK/aoAv0O/khmF6ehW6RgELrF1JR2F555T9izoo2A= github.com/go-mesh/openlogging v1.0.1 h1:6raaXo8SK+wuQX1VoNi6QJCSf1fTOFWh7f5f6b2ZEmY= github.com/go-mesh/openlogging v1.0.1/go.mod h1:qaKi+amO+hsGin2q1GmW+/NcbZpMPnTufwrWzDmIuuU= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/options.go b/options.go index 278fab6f..7c27f630 100644 --- a/options.go +++ b/options.go @@ -2,9 +2,8 @@ package archaius import ( "crypto/tls" + "github.com/go-chassis/go-archaius/source/remote" "github.com/go-chassis/go-archaius/source/util" - - "github.com/go-chassis/go-chassis-config" ) // RemoteInfo has attribute for config center source initialization @@ -42,7 +41,7 @@ type Options struct { OptionalFiles []string FileHandler util.FileHandler RemoteInfo *RemoteInfo - ConfigClient config.Client + ConfigClient remote.Client UseCLISource bool UseENVSource bool UseMemSource bool @@ -77,7 +76,7 @@ func WithDefaultFileHandler(handler util.FileHandler) Option { //RemoteInfo is required if you want to use config center source //client is optional,if client is nil, archaius will create one based on RemoteInfo //config client will be injected into config source as a client to interact with a config server -func WithRemoteSource(ri *RemoteInfo, c config.Client) Option { +func WithRemoteSource(ri *RemoteInfo, c remote.Client) Option { return func(options *Options) { options.RemoteInfo = ri options.ConfigClient = c diff --git a/pkg/configcenter/client.go b/pkg/configcenter/client.go new file mode 100644 index 00000000..e6692560 --- /dev/null +++ b/pkg/configcenter/client.go @@ -0,0 +1,443 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package configcenter + +import ( + "context" + "errors" + "fmt" + "github.com/go-chassis/foundation/httpclient" + "github.com/go-chassis/go-archaius/pkg/serializers" + "github.com/go-mesh/openlogging" + "github.com/gorilla/websocket" + "io/ioutil" + "math/rand" + "net/http" + "net/url" + "os" + "strings" + "sync" + "time" +) + +const ( + defaultTimeout = 10 * time.Second + //StatusUP is a variable of type string + StatusUP = "UP" + //HeaderContentType is a variable of type string + HeaderContentType = "Content-Type" + //HeaderUserAgent is a variable of type string + HeaderUserAgent = "User-Agent" + //HeaderEnvironment specifies the environment of a service + HeaderEnvironment = "X-Environment" + members = "/configuration/members" + dimensionsInfo = "dimensionsInfo" + dynamicConfigAPI = `/configuration/refresh/items` + getConfigAPI = `/configuration/items` + defaultContentType = "application/json" + envProjectID = "CSE_PROJECT_ID" + packageInitError = "package not initialize successfully" + emptyConfigServerMembers = "empty config server member" + emptyConfigServerConfig = "empty config server passed" + // Name of the Plugin + Name = "config_center" +) + +var ( + //HeaderTenantName is a variable of type string + HeaderTenantName = "X-Tenant-Name" + //ConfigMembersPath is a variable of type string + ConfigMembersPath = "" + //ConfigPath is a variable of type string + ConfigPath = "" + //ConfigRefreshPath is a variable of type string + ConfigRefreshPath = "" + autoDiscoverable = false + apiVersionConfig = "" + environmentConfig = "" +) + +//Client is a struct +type Client struct { + opts Options + sync.RWMutex + c *httpclient.Requests + wsDialer *websocket.Dialer + wsConnection *websocket.Conn +} + +//New create cc client +func New(opts Options) (*Client, error) { + var apiVersion string + apiVersionConfig = opts.APIVersion + switch apiVersionConfig { + case "v2": + apiVersion = "v2" + case "V2": + apiVersion = "v2" + case "v3": + apiVersion = "v3" + case "V3": + apiVersion = "v3" + default: + apiVersion = "v3" + } + updateAPIPath(apiVersion) + + hc, err := httpclient.New(&httpclient.Options{ + SSLEnabled: opts.EnableSSL, + TLSConfig: opts.TLSConfig, + Compressed: false, + }) + if err != nil { + return nil, err + } + c := &Client{ + c: hc, + opts: opts, + wsDialer: &websocket.Dialer{ + TLSClientConfig: opts.TLSConfig, + HandshakeTimeout: defaultTimeout, + }, + } + c.Shuffle() + return c, nil +} + +//Update the Base PATH and HEADERS Based on the version of ConfigCenter used. +func updateAPIPath(apiVersion string) { + //Check for the env Name in Container to get Domain Name + //Default value is "default" + projectID, isExist := os.LookupEnv(envProjectID) + if !isExist { + projectID = "default" + } + switch apiVersion { + case "v3": + ConfigMembersPath = "/v3/" + projectID + members + ConfigPath = "/v3/" + projectID + getConfigAPI + ConfigRefreshPath = "/v3/" + projectID + dynamicConfigAPI + case "v2": + ConfigMembersPath = "/members" + ConfigPath = "/configuration/v2/items" + ConfigRefreshPath = "/configuration/v2/refresh/items" + default: + ConfigMembersPath = "/v3/" + projectID + members + ConfigPath = "/v3/" + projectID + getConfigAPI + ConfigRefreshPath = "/v3/" + projectID + dynamicConfigAPI + } +} + +func (c *Client) call(method string, api string, headers http.Header, body []byte, s interface{}) error { + hosts, err := c.GetConfigServer() + if err != nil { + openlogging.GetLogger().Error("Get config server addr failed:" + err.Error()) + } + index := rand.Int() % len(c.opts.ConfigServerAddresses) + host := hosts[index] + rawURI := host + api + errMsgPrefix := fmt.Sprintf("Call %s failed: ", rawURI) + resp, err := c.HTTPDo(method, rawURI, headers, body) + if err != nil { + openlogging.Error(errMsgPrefix + err.Error()) + return err + + } + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + openlogging.Error(errMsgPrefix + err.Error()) + return err + } + if !isStatusSuccess(resp.StatusCode) { + err = fmt.Errorf("statusCode: %d, resp body: %s", resp.StatusCode, body) + openlogging.GetLogger().Error(errMsgPrefix + err.Error()) + return err + } + contentType := resp.Header.Get("Content-Type") + if len(contentType) > 0 && (len(defaultContentType) > 0 && !strings.Contains(contentType, defaultContentType)) { + err = fmt.Errorf("content type not %s", defaultContentType) + openlogging.GetLogger().Error(errMsgPrefix + err.Error()) + return err + } + err = serializers.Decode(defaultContentType, body, s) + if err != nil { + openlogging.GetLogger().Error("Decode failed:" + err.Error()) + return err + } + return nil +} + +//HTTPDo Use http-client package for rest communication +func (c *Client) HTTPDo(method string, rawURL string, headers http.Header, body []byte) (resp *http.Response, err error) { + if len(headers) == 0 { + headers = make(http.Header) + } + for k, v := range GetDefaultHeaders(c.opts.TenantName) { + headers[k] = v + } + return c.c.Do(context.Background(), method, rawURL, headers, body) +} + +// Flatten pulls all the configuration from config center and merge kv in different dimension +func (c *Client) Flatten(dimensionInfo string) (map[string]interface{}, error) { + config := make(map[string]interface{}) + configAPIResp, err := c.PullGroupByDimension(dimensionInfo) + if err != nil { + openlogging.GetLogger().Error("Flatten config failed:" + err.Error()) + return nil, err + } + for _, v := range configAPIResp { + for key, value := range v { + config[key] = value + + } + } + return config, nil +} + +//PullGroupByDimension pulls all the configuration from Config-Server group by dimesion Info +func (c *Client) PullGroupByDimension(dimensionInfo string) (map[string]map[string]interface{}, error) { + configAPIRes := make(map[string]map[string]interface{}) + parsedDimensionInfo := strings.Replace(dimensionInfo, "#", "%23", -1) + restAPI := ConfigPath + "?" + dimensionsInfo + "=" + parsedDimensionInfo + err := c.call(http.MethodGet, restAPI, nil, nil, &configAPIRes) + if err != nil { + openlogging.GetLogger().Error("Flatten config failed:" + err.Error()) + return nil, err + } + + return configAPIRes, nil +} + +//Do is common http remote call +func (c *Client) Do(method string, data interface{}) (map[string]interface{}, error) { + configAPIS := make(map[string]interface{}) + body, err := serializers.Encode(serializers.JSONEncoder, data) + if err != nil { + openlogging.GetLogger().Errorf("serializer data failed , err :", err.Error()) + return nil, err + } + err = c.call(method, ConfigPath, nil, body, &configAPIS) + if err != nil { + return nil, err + } + return configAPIS, nil +} + +//AddConfig post new config +func (c *Client) AddConfig(data *CreateConfigAPI) (map[string]interface{}, error) { + return c.Do("POST", data) +} + +//DeleteConfig delete configs +func (c *Client) DeleteConfig(data *DeleteConfigAPI) (map[string]interface{}, error) { + return c.Do("DELETE", data) +} + +//Watch use websocket +func (c *Client) Watch(f func(map[string]interface{}), errHandler func(err error)) error { + parsedDimensionInfo := strings.Replace(c.opts.DefaultDimension, "#", "%23", -1) + refreshConfigPath := ConfigRefreshPath + `?` + dimensionsInfo + `=` + parsedDimensionInfo + if c.wsDialer != nil { + /*----------------- + 1. Decide on the URL + 2. Create WebSocket Connection + 3. Call KeepAlive in separate thread + 3. Generate events on Receive Data + */ + baseURL, err := c.getWebSocketURL() + if err != nil { + error := errors.New("error in getting default server info") + return error + } + url := baseURL.String() + refreshConfigPath + c.wsConnection, _, err = c.wsDialer.Dial(url, nil) + if err != nil { + return fmt.Errorf("watching config-center dial catch an exception error:%s", err.Error()) + } + keepAlive(c.wsConnection, 15*time.Second) + go func() error { + for { + messageType, message, err := c.wsConnection.ReadMessage() + if err != nil { + break + } + if messageType == websocket.TextMessage { + m, err := GetConfigs(message) + if err != nil { + errHandler(err) + continue + } + f(m) + } + } + err = c.wsConnection.Close() + if err != nil { + openlogging.Error(err.Error()) + return fmt.Errorf("CC watch Conn close failed error:%s", err.Error()) + } + return nil + }() + } + return nil +} + +func keepAlive(c *websocket.Conn, timeout time.Duration) { + lastResponse := time.Now() + c.SetPongHandler(func(msg string) error { + lastResponse = time.Now() + return nil + }) + go func() { + for { + err := c.WriteMessage(websocket.PingMessage, []byte("keepalive")) + if err != nil { + return + } + time.Sleep(timeout / 2) + if time.Now().Sub(lastResponse) > timeout { + c.Close() + return + } + } + }() +} + +func isStatusSuccess(i int) bool { + return i >= http.StatusOK && i < http.StatusBadRequest +} + +//Shuffle is a method to log error +func (c *Client) Shuffle() error { + if c.opts.ConfigServerAddresses == nil || len(c.opts.ConfigServerAddresses) == 0 { + err := errors.New(emptyConfigServerConfig) + openlogging.GetLogger().Error(emptyConfigServerConfig) + return err + } + + perm := rand.Perm(len(c.opts.ConfigServerAddresses)) + + c.Lock() + defer c.Unlock() + openlogging.GetLogger().Debugf("before shuffled member %s ", c.opts.ConfigServerAddresses) + for i, v := range perm { + openlogging.GetLogger().Debugf("shuffler %d %d", i, v) + tmp := c.opts.ConfigServerAddresses[v] + c.opts.ConfigServerAddresses[v] = c.opts.ConfigServerAddresses[i] + c.opts.ConfigServerAddresses[i] = tmp + } + + openlogging.GetLogger().Debugf("shuffled member %s", c.opts.ConfigServerAddresses) + return nil +} + +//GetConfigServer is a method used for getting server configuration +func (c *Client) GetConfigServer() ([]string, error) { + + if len(c.opts.ConfigServerAddresses) == 0 { + err := errors.New(emptyConfigServerMembers) + openlogging.GetLogger().Error(emptyConfigServerMembers) + return nil, err + } + + tmpConfigAddrs := c.opts.ConfigServerAddresses + for key := range tmpConfigAddrs { + if !strings.Contains(c.opts.ConfigServerAddresses[key], "https") && c.opts.EnableSSL { + c.opts.ConfigServerAddresses[key] = `https://` + c.opts.ConfigServerAddresses[key] + + } else if !strings.Contains(c.opts.ConfigServerAddresses[key], "http") { + c.opts.ConfigServerAddresses[key] = `http://` + c.opts.ConfigServerAddresses[key] + } + } + + err := c.Shuffle() + if err != nil { + openlogging.GetLogger().Error("member shuffle is failed: " + err.Error()) + return nil, err + } + + c.RLock() + defer c.RUnlock() + openlogging.GetLogger().Debugf("member server return %s", c.opts.ConfigServerAddresses[0]) + return c.opts.ConfigServerAddresses, nil +} + +//GetConfigs get KV from a event +func GetConfigs(actionData []byte) (map[string]interface{}, error) { + configCenterEvent := new(Event) + err := serializers.Decode(serializers.JSONEncoder, actionData, &configCenterEvent) + if err != nil { + openlogging.GetLogger().Errorf(fmt.Sprintf("error in unmarshalling data on event receive with error %s", err.Error())) + return nil, err + } + sourceConfig := make(map[string]interface{}) + err = serializers.Decode(serializers.JSONEncoder, []byte(configCenterEvent.Value), &sourceConfig) + if err != nil { + openlogging.GetLogger().Errorf(fmt.Sprintf("error in unmarshalling config values %s", err.Error())) + return nil, err + } + return sourceConfig, nil +} + +func (c *Client) getWebSocketURL() (*url.URL, error) { + var defaultTLS bool + var parsedEndPoint []string + var host string + + configCenterEntryPointList, err := c.GetConfigServer() + if err != nil { + openlogging.GetLogger().Error("error in member discovery:" + err.Error()) + return nil, err + } + for _, server := range configCenterEntryPointList { + parsedEndPoint = strings.Split(server, `://`) + hostArr := strings.Split(parsedEndPoint[1], `:`) + port := c.opts.RefreshPort + if port == "" { + port = "30104" + } + host = hostArr[0] + ":" + port + if host == "" { + host = "localhost" + } + } + + if c.wsDialer.TLSClientConfig != nil { + defaultTLS = true + } + if host == "" { + err := errors.New("host must be a URL or a host:port pair") + openlogging.GetLogger().Error("empty host for watch action:" + err.Error()) + return nil, err + } + hostURL, err := url.Parse(host) + if err != nil || hostURL.Scheme == "" || hostURL.Host == "" { + scheme := "ws://" + if defaultTLS { + scheme = "wss://" + } + hostURL, err = url.Parse(scheme + host) + if err != nil { + return nil, err + } + if hostURL.Path != "" && hostURL.Path != "/" { + return nil, fmt.Errorf("host must be a URL or a host:port pair: %q", host) + } + } + return hostURL, nil +} diff --git a/pkg/configcenter/client_test.go b/pkg/configcenter/client_test.go new file mode 100755 index 00000000..d57e51b7 --- /dev/null +++ b/pkg/configcenter/client_test.go @@ -0,0 +1,10 @@ +package configcenter_test + +import ( + "github.com/go-chassis/go-archaius/pkg/configcenter" + "testing" +) + +func TestNew(t *testing.T) { + configcenter.New(configcenter.Options{}) +} diff --git a/pkg/configcenter/options.go b/pkg/configcenter/options.go new file mode 100644 index 00000000..31d22609 --- /dev/null +++ b/pkg/configcenter/options.go @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package configcenter + +import ( + "crypto/tls" + "net/http" +) + +//Options is remote client option +type Options struct { + DefaultDimension string + Service string + App string + Version string + Env string + + ConfigServerAddresses []string + RefreshPort string + APIVersion string + TLSConfig *tls.Config + TenantName string + EnableSSL bool +} + +//GetDefaultHeaders gets default headers +func GetDefaultHeaders(tenantName string) http.Header { + headers := http.Header{ + HeaderContentType: []string{"application/json"}, + HeaderUserAgent: []string{"cse-configcenter-client/1.0.0"}, + HeaderTenantName: []string{tenantName}, + } + if environmentConfig != "" { + headers.Set(HeaderEnvironment, environmentConfig) + } + + return headers +} diff --git a/pkg/configcenter/struct.go b/pkg/configcenter/struct.go new file mode 100644 index 00000000..e7cc92ea --- /dev/null +++ b/pkg/configcenter/struct.go @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package configcenter + +//DeleteConfigAPI for deleting +type DeleteConfigAPI struct { + DimensionInfo string `json:"dimensionsInfo"` + Keys []string `json:"keys"` +} + +//CreateConfigAPI for new config +type CreateConfigAPI struct { + DimensionInfo string `json:"dimensionsInfo"` + Items map[string]interface{} `json:"items"` +} + +//Event stores info about an config center event +type Event struct { + Action string `json:"action"` + Value string `json:"value"` +} diff --git a/pkg/configcenter/util_test.go b/pkg/configcenter/util_test.go new file mode 100644 index 00000000..8458f14b --- /dev/null +++ b/pkg/configcenter/util_test.go @@ -0,0 +1,28 @@ +package configcenter_test + +import ( + "encoding/json" + "github.com/go-chassis/go-archaius/pkg/configcenter" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetConfigs(t *testing.T) { + m := make(map[string]interface{}) + m["a"] = "b" + m["c"] = "d" + b, err := json.Marshal(m) + assert.NoError(t, err) + value := string(b) + e := configcenter.Event{ + Action: "delete", + Value: value, + } + + b, err = json.MarshalIndent(e, "", " ") + t.Log(string(b)) + assert.NoError(t, err) + m2, err := configcenter.GetConfigs(b) + assert.NoError(t, err) + assert.Equal(t, "b", m2["a"]) +} diff --git a/pkg/maputil/util.go b/pkg/maputil/util.go deleted file mode 100644 index d73d50d0..00000000 --- a/pkg/maputil/util.go +++ /dev/null @@ -1,23 +0,0 @@ -package maputil - -import "sort" - -//Map2String convert map to a sorted string -func Map2String(m map[string]string) string { - result := "" - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - l := len(keys) - for i, k := range keys { - if i != l-1 { - result = result + k + "=" + m[k] + "|" - } else { - result = result + k + "=" + m[k] - } - - } - return result -} diff --git a/pkg/maputil/util_test.go b/pkg/maputil/util_test.go deleted file mode 100644 index b725cd17..00000000 --- a/pkg/maputil/util_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package maputil_test - -import ( - "github.com/go-chassis/go-archaius/pkg/maputil" - "testing" -) - -func TestMap2String(t *testing.T) { - m := make(map[string]string) - m["s"] = "a" - m["c"] = "c" - m["d"] = "b" - t.Log(maputil.Map2String(m)) -} diff --git a/pkg/serializers/json/json.go b/pkg/serializers/json/json.go new file mode 100755 index 00000000..ce74c129 --- /dev/null +++ b/pkg/serializers/json/json.go @@ -0,0 +1,60 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//Created by on 2017/6/22. + +//Package json is used for marshalling and unmarshalling +package json + +import ( + jsonwrapper "encoding/json" + "errors" +) + +//Serializer is a empty struct +type Serializer struct{} + +//Decode - Unmarshal unmarshaling data +func (js Serializer) Decode(data []byte, v interface{}) error { + var err error + defer func() { + if r := recover(); r != nil { + err = errors.New("Invalid request ") + } + + }() + + err = jsonwrapper.Unmarshal(data, v) + return err +} + +//Encode - Marshal marshaling data +func (js Serializer) Encode(v interface{}) ([]byte, error) { + var ( + data []byte + err error + ) + + defer func() { + if r := recover(); r != nil { + err = errors.New("Invalid request ") + } + + }() + + data, err = jsonwrapper.Marshal(v) + + return data, err +} diff --git a/pkg/serializers/json/json_test.go b/pkg/serializers/json/json_test.go new file mode 100755 index 00000000..c773b592 --- /dev/null +++ b/pkg/serializers/json/json_test.go @@ -0,0 +1,32 @@ +package json + +import ( + "testing" +) + +type Test struct { + Team string `json:"team"` +} + +func TestEncode(t *testing.T) { + + testSerilizer := &Serializer{} + test := &Test{Team: "data"} + _, err := testSerilizer.Encode(test) + + if err != nil { + t.Error("error in encoding") + } +} +func TestDecode(t *testing.T) { + + testSerilizer := &Serializer{} + test := &Test{Team: "data"} + + data, _ := testSerilizer.Encode(test) + err := testSerilizer.Decode(data, test) + + if err != nil { + t.Error("error in decoding") + } +} diff --git a/pkg/serializers/types.go b/pkg/serializers/types.go new file mode 100755 index 00000000..3c83cc33 --- /dev/null +++ b/pkg/serializers/types.go @@ -0,0 +1,70 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//Package serializers created on 2017/6/22. +package serializers + +import ( + "errors" + "github.com/go-chassis/go-archaius/pkg/serializers/json" +) + +const ( + //JSONEncoder is a variable of type string + JSONEncoder = `application/json` +) + +var availableSerializers map[string]Serializer + +//Serializer is a interface which declares encode and decode methods +type Serializer interface { + Encode(obj interface{}) ([]byte, error) + Decode(data []byte, obj interface{}) error +} + +var _ Serializer = json.Serializer{} + +func init() { + availableSerializers = make(map[string]Serializer) + availableSerializers[JSONEncoder] = json.Serializer{} +} + +// Encode is a convenience wrapper for encoding to a []byte from an Encoder +func Encode(serializersType string, obj interface{}) ([]byte, error) { + serializer, ok := availableSerializers[serializersType] + if !ok { + errorMsg := "serializer" + serializersType + " not avaliable" + return []byte{}, errors.New(errorMsg) + } + + data, err := serializer.Encode(obj) + if err != nil { + return nil, err + } + return data, nil +} + +// Decode is a convenience wrapper for decoding data into an Object. +func Decode(serializersType string, data []byte, obj interface{}) error { + serializer, ok := availableSerializers[serializersType] + if !ok { + errorMsg := "serializer" + serializersType + " not avaliable" + return errors.New(errorMsg) + } + + err := serializer.Decode(data, obj) + return err +} diff --git a/pkg/serializers/types_test.go b/pkg/serializers/types_test.go new file mode 100755 index 00000000..9448d752 --- /dev/null +++ b/pkg/serializers/types_test.go @@ -0,0 +1,50 @@ +package serializers + +import ( + "github.com/go-chassis/go-archaius/pkg/serializers/json" + "testing" +) + +type Test struct { + Team string `json:"team"` +} + +func Test_Encode1(t *testing.T) { + t.Log("Testing serializer encoding function for valid serializer") + availableSerializers = make(map[string]Serializer) + availableSerializers[JSONEncoder] = json.Serializer{} + + test := &Test{Team: "data"} + data, _ := Encode(JSONEncoder, test) + + stringData := `{"team":"data"}` + if string(data) != stringData { + t.Error("error is encoding the data") + } +} + +func Test_Encode2(t *testing.T) { + t.Log("Testing serializer encoding function for invalid serializer") + availableSerializers = make(map[string]Serializer) + availableSerializers[JSONEncoder] = json.Serializer{} + + test := &Test{Team: "data"} + _, err := Encode("Invalidserializer", test) + if err == nil { + t.Error("Encoder is encoding invalid type of serilizer format") + } +} + +func Test_Decode(t *testing.T) { + t.Log("Testing serializer decode function") + availableSerializers = make(map[string]Serializer) + availableSerializers[JSONEncoder] = json.Serializer{} + test := &Test{Team: "data"} + + data, _ := Encode(JSONEncoder, test) + err := Decode(JSONEncoder, data, test) + + if err != nil { + t.Error("error in decoding data") + } +} diff --git a/source/remote/client.go b/source/remote/client.go new file mode 100644 index 00000000..a777cbe7 --- /dev/null +++ b/source/remote/client.go @@ -0,0 +1,55 @@ +package remote + +import ( + "fmt" + + "github.com/go-mesh/openlogging" +) + +var configClientPlugins = make(map[string]func(options Options) (Client, error)) + +//const +const ( + LabelService = "serviceName" + LabelVersion = "version" + LabelEnvironment = "environment" + LabelApp = "app" +) + +//DefaultClient is config server's client +var DefaultClient Client + +//InstallConfigClientPlugin install a config client plugin +func InstallConfigClientPlugin(name string, f func(options Options) (Client, error)) { + configClientPlugins[name] = f + openlogging.GetLogger().Infof("Installed %s Plugin", name) +} + +//Client is the interface of config server client, it has basic func to interact with config server +type Client interface { + //PullConfigs pull all configs from remote + PullConfigs(labels ...map[string]string) (map[string]interface{}, error) + //PullConfig pull one config from remote + PullConfig(key, contentType string, labels map[string]string) (interface{}, error) + // PushConfigs push config to cc + PushConfigs(data map[string]interface{}, labels map[string]string) (map[string]interface{}, error) + // DeleteConfigsByKeys delete config for cc by keys + DeleteConfigsByKeys(keys []string, labels map[string]string) (map[string]interface{}, error) + //Watch get kv change results, you can compare them with local kv cache and refresh local cache + Watch(f func(map[string]interface{}), errHandler func(err error), labels map[string]string) error + Options() Options +} + +//NewClient create config client implementation +func NewClient(name string, options Options) (Client, error) { + plugins := configClientPlugins[name] + if plugins == nil { + return nil, fmt.Errorf("plugin [%s] not found", name) + } + DefaultClient, err := plugins(options) + if err != nil { + return nil, err + } + openlogging.GetLogger().Infof("%s plugin is enabled", name) + return DefaultClient, nil +} diff --git a/source/remote/client_test.go b/source/remote/client_test.go new file mode 100644 index 00000000..95fb7c1a --- /dev/null +++ b/source/remote/client_test.go @@ -0,0 +1,16 @@ +package remote_test + +import ( + "github.com/go-chassis/go-archaius/source/remote" + "github.com/stretchr/testify/assert" + "testing" + + _ "github.com/go-chassis/go-archaius/source/remote/configcenter" +) + +func TestEnable(t *testing.T) { + _, err := remote.NewClient("config_center", remote.Options{ + ServerURI: "http://127.0.0.1:30100", + }) + assert.Error(t, err) +} diff --git a/source/remote/configcenter/config_center.go b/source/remote/configcenter/config_center.go new file mode 100755 index 00000000..19a7ce5a --- /dev/null +++ b/source/remote/configcenter/config_center.go @@ -0,0 +1,185 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package configcenter created on 2017/6/20. +package configcenter + +import ( + "errors" + "github.com/go-chassis/go-archaius/pkg/configcenter" + "github.com/go-chassis/go-archaius/source/remote" + "strings" + + "github.com/go-mesh/openlogging" + "github.com/gorilla/websocket" +) + +const ( + //HeaderContentType is a variable of type string + HeaderContentType = "Content-Type" + //HeaderUserAgent is a variable of type string + HeaderUserAgent = "User-Agent" + // Name of the Plugin + Name = "config_center" +) + +//errors +var ( + ErrInvalidEP = errors.New("invalid endpoint") +) + +//ConfigCenter is Implementation +type ConfigCenter struct { + c *configcenter.Client + opts remote.Options + wsDialer *websocket.Dialer +} + +//NewConfigCenter is a function +func NewConfigCenter(options remote.Options) (remote.Client, error) { + if options.ServerURI == "" { + return nil, ErrInvalidEP + } + configCenters := strings.Split(options.ServerURI, ",") + cCenters := make([]string, 0) + for _, value := range configCenters { + value = strings.Replace(value, " ", "", -1) + cCenters = append(cCenters, value) + } + d, err := GenerateDimension(options.Labels[remote.LabelService], options.Labels[remote.LabelVersion], options.Labels[remote.LabelApp]) + if err != nil { + return nil, err + } + + c, err := configcenter.New(configcenter.Options{ + ConfigServerAddresses: cCenters, + DefaultDimension: d, + TLSConfig: options.TLSConfig, + TenantName: options.TenantName, + EnableSSL: options.EnableSSL, + RefreshPort: options.RefreshPort, + }) + if err != nil { + return nil, err + } + + cc := &ConfigCenter{ + c: c, + opts: options, + } + openlogging.Info("new config center client", openlogging.WithTags( + openlogging.Tags{ + "dimension": d, + "ws_port": options.RefreshPort, + "ssl": options.EnableSSL, + "ep": cCenters, + })) + return cc, nil +} + +// PullConfigs is the implementation of ConfigCenter to pull all the configurations from Config-Server +func (c *ConfigCenter) PullConfigs(labels ...map[string]string) (map[string]interface{}, error) { + d := "" + var err error + d, err = GenerateDimension(c.opts.Labels[remote.LabelService], c.opts.Labels[remote.LabelVersion], c.opts.Labels[remote.LabelApp]) + if err != nil { + return nil, err + } + configurations, error := c.c.Flatten(d) + if error != nil { + return nil, error + } + return configurations, nil +} + +// PullConfig is the implementation of ConfigCenter to pull specific configurations from Config-Server +func (c *ConfigCenter) PullConfig(key, contentType string, labels map[string]string) (interface{}, error) { + if len(labels) == 0 { + labels = c.opts.Labels + } + d, err := GenerateDimension(c.opts.Labels[remote.LabelService], c.opts.Labels[remote.LabelVersion], c.opts.Labels[remote.LabelApp]) + if err != nil { + return nil, err + } + // TODO use the contentType to return the configurations + configurations, error := c.c.Flatten(d) + if error != nil { + return nil, error + } + configurationsValue, ok := configurations[key] + if !ok { + openlogging.GetLogger().Error("Error in fetching the configurations for particular value,No Key found : " + key) + } + + return configurationsValue, nil +} + +// PushConfigs push configs to ConfigSource cc , success will return { "Result": "Success" } +func (c *ConfigCenter) PushConfigs(items map[string]interface{}, labels map[string]string) (map[string]interface{}, error) { + if len(items) == 0 { + em := "data is empty , which data need to send cc" + openlogging.GetLogger().Error(em) + return nil, errors.New(em) + } + if len(labels) == 0 { + labels = c.opts.Labels + } + d, err := GenerateDimension(c.opts.Labels[remote.LabelService], c.opts.Labels[remote.LabelVersion], c.opts.Labels[remote.LabelApp]) + if err != nil { + return nil, err + } + configAPI := &configcenter.CreateConfigAPI{ + DimensionInfo: d, + Items: items, + } + + return c.c.AddConfig(configAPI) +} + +// DeleteConfigsByKeys delete keys +func (c *ConfigCenter) DeleteConfigsByKeys(keys []string, labels map[string]string) (map[string]interface{}, error) { + if len(keys) == 0 { + em := "not key need to delete for cc, please check keys" + openlogging.GetLogger().Error(em) + return nil, errors.New(em) + } + if len(labels) == 0 { + labels = c.opts.Labels + } + d, err := GenerateDimension(c.opts.Labels[remote.LabelService], c.opts.Labels[remote.LabelVersion], c.opts.Labels[remote.LabelApp]) + if err != nil { + return nil, err + } + configAPI := &configcenter.DeleteConfigAPI{ + DimensionInfo: d, + Keys: keys, + } + + return c.c.DeleteConfig(configAPI) +} + +//Watch use ws +func (c *ConfigCenter) Watch(f func(map[string]interface{}), errHandler func(err error), labels map[string]string) error { + return c.c.Watch(f, errHandler) +} +func init() { + remote.InstallConfigClientPlugin(Name, NewConfigCenter) +} + +//Options return options +func (c *ConfigCenter) Options() remote.Options { + return c.opts +} diff --git a/source/remote/configcenter/config_center_test.go b/source/remote/configcenter/config_center_test.go new file mode 100755 index 00000000..6bbc6129 --- /dev/null +++ b/source/remote/configcenter/config_center_test.go @@ -0,0 +1,16 @@ +package configcenter_test + +import ( + "github.com/go-chassis/go-archaius/source/remote" + "github.com/go-chassis/go-archaius/source/remote/configcenter" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewConfigCenter(t *testing.T) { + c, err := configcenter.NewConfigCenter(remote.Options{ + ServerURI: "http://", + Labels: map[string]string{"app": "default"}}) + assert.NoError(t, err) + assert.Equal(t, "default", c.Options().Labels["app"]) +} diff --git a/source/remote/configcenter/struct.go b/source/remote/configcenter/struct.go new file mode 100644 index 00000000..918304d2 --- /dev/null +++ b/source/remote/configcenter/struct.go @@ -0,0 +1,14 @@ +package configcenter + +//Instance is a struct +type Instance struct { + Status string `json:"status"` + ServiceName string `json:"serviceName"` + IsHTTPS bool `json:"isHttps"` + EntryPoints []string `json:"endpoints"` +} + +//Members is a struct +type Members struct { + Instances []Instance `json:"instances"` +} diff --git a/source/remote/configcenter/util.go b/source/remote/configcenter/util.go new file mode 100644 index 00000000..9df678ee --- /dev/null +++ b/source/remote/configcenter/util.go @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package configcenter + +import ( + "errors" + "fmt" + "regexp" +) + +const ( + maxValue = 256 +) + +//errors +var ( + ErrAppEmpty = errors.New("app can not be empty") + ErrServiceTooLong = errors.New("exceeded max value for service name") +) + +//GenerateDimension create config center dimension string +func GenerateDimension(serviceName, version, appName string) (string, error) { + if appName != "" { + serviceName = serviceName + "@" + appName + } else { + return "", ErrAppEmpty + } + + if version != "" { + serviceName = serviceName + "#" + version + } + + if len(serviceName) > maxValue { + return "", ErrServiceTooLong + } + + dimeExp := `\A([^\$\%\&\+\(/)\[\]\" "\"])*\z` + dimRegexVar, err := regexp.Compile(dimeExp) + if err != nil { + return "", errors.New("not a valid regular expression" + err.Error()) + } + + if !dimRegexVar.Match([]byte(serviceName)) { + return "", fmt.Errorf("invalid value for dimension info, does not satisfy the regular expression for dimInfo:%s", serviceName) + } + + return serviceName, nil +} diff --git a/source/remote/configcenter/util_test.go b/source/remote/configcenter/util_test.go new file mode 100644 index 00000000..a24ee279 --- /dev/null +++ b/source/remote/configcenter/util_test.go @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package configcenter_test + +import ( + "github.com/go-chassis/go-archaius/source/remote/configcenter" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGenerateDimension(t *testing.T) { + d, _ := configcenter.GenerateDimension("cart", "1.0.0", "default") + assert.Equal(t, "cart@default#1.0.0", d) + + d, _ = configcenter.GenerateDimension("cart", "", "default") + assert.Equal(t, "cart@default", d) +} diff --git a/source/remote/options.go b/source/remote/options.go new file mode 100644 index 00000000..8ac5347f --- /dev/null +++ b/source/remote/options.go @@ -0,0 +1,17 @@ +package remote + +import "crypto/tls" + +//Options is client option +type Options struct { + ServerURI string + Endpoint string + TLSConfig *tls.Config + TenantName string + EnableSSL bool + APIVersion string + AutoDiscovery bool + RefreshPort string + + Labels map[string]string +} diff --git a/source/remote/remote.go b/source/remote/remote.go index 51f69694..d2759126 100755 --- a/source/remote/remote.go +++ b/source/remote/remote.go @@ -25,7 +25,6 @@ import ( "github.com/go-chassis/go-archaius/event" "github.com/go-chassis/go-archaius/source" - "github.com/go-chassis/go-chassis-config" "github.com/go-mesh/openlogging" ) @@ -39,7 +38,7 @@ const ( //Source handles configs from config center type Source struct { - cc config.Client + cc Client connsLock sync.Mutex @@ -59,7 +58,7 @@ type Source struct { } //NewConfigCenterSource initializes all components of configuration center -func NewConfigCenterSource(cc config.Client, refreshMode, refreshInterval int) source.ConfigSource { +func NewConfigCenterSource(cc Client, refreshMode, refreshInterval int) source.ConfigSource { s := new(Source) s.dimensions = []map[string]string{cc.Options().Labels} s.priority = configCenterSourcePriority diff --git a/source/remote/remote_test.go b/source/remote/remote_test.go index 2376aa02..f65f3578 100755 --- a/source/remote/remote_test.go +++ b/source/remote/remote_test.go @@ -7,17 +7,16 @@ import ( "time" "errors" - "github.com/go-chassis/go-chassis-config" "testing" ) type mockClient struct { - opts config.Options + opts remote.Options configsInfo map[string]interface{} } // NewClient init the necessary objects needed for seamless communication to Kie Server -func NewClient(options config.Options) (config.Client, error) { +func NewClient(options remote.Options) (remote.Client, error) { kieClient := &mockClient{ opts: options, configsInfo: map[string]interface{}{ @@ -27,7 +26,7 @@ func NewClient(options config.Options) (config.Client, error) { return kieClient, nil } func init() { - config.InstallConfigClientPlugin("mock-client", NewClient) + remote.InstallConfigClientPlugin("mock-client", NewClient) } // PullConfigs is used for pull config from servicecomb-kie @@ -65,7 +64,7 @@ func (c *mockClient) Watch(f func(map[string]interface{}), errHandler func(err e } //Options. -func (c *mockClient) Options() config.Options { +func (c *mockClient) Options() remote.Options { return c.opts } @@ -82,7 +81,7 @@ func (ccenter *EventHandler) OnEvent(event *event.Event) { } func TestNewConfigCenterSource(t *testing.T) { - opts := config.Options{ + opts := remote.Options{ Labels: map[string]string{ "app": "default", "serviceName": "cart", @@ -90,7 +89,7 @@ func TestNewConfigCenterSource(t *testing.T) { TenantName: "default", ServerURI: "http://", } - cc, err := config.NewClient("mock-client", opts) + cc, err := remote.NewClient("mock-client", opts) assert.NoError(t, err) ccs := remote.NewConfigCenterSource(cc, 1, 1)