diff options
-rw-r--r-- | msb2pilot/src/msb2pilot/conf/consul.yml | 2 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/consul/controller.go | 98 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/consul/controller_test.go | 89 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/consul/monitor.go | 78 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/log/log_test.go | 10 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/main.go | 17 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/models/config.go | 16 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/models/conversion.go | 117 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/models/conversion_test.go | 115 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/models/msb.go | 65 | ||||
-rw-r--r-- | msb2pilot/src/msb2pilot/util/common.go | 1 |
11 files changed, 602 insertions, 6 deletions
diff --git a/msb2pilot/src/msb2pilot/conf/consul.yml b/msb2pilot/src/msb2pilot/conf/consul.yml new file mode 100644 index 0000000..3c30cba --- /dev/null +++ b/msb2pilot/src/msb2pilot/conf/consul.yml @@ -0,0 +1,2 @@ +# consul api address, default to be http://localhost:8500
+address: http://127.0.0.1:8500
diff --git a/msb2pilot/src/msb2pilot/consul/controller.go b/msb2pilot/src/msb2pilot/consul/controller.go new file mode 100644 index 0000000..b6e798f --- /dev/null +++ b/msb2pilot/src/msb2pilot/consul/controller.go @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2018 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial Project + */ +package consul + +import ( + "msb2pilot/log" + "msb2pilot/models" + "msb2pilot/util" + "os" + "path/filepath" + + "github.com/hashicorp/consul/api" +) + +var client *api.Client +var consulAddress string + +var ( + cfgFilePath = filepath.Join(util.GetCfgPath(), "consul.yml") +) + +const ( + defaultAddress = "http://localhost:8500" +) + +func init() { + consulAddress = getConsulAddress(cfgFilePath) + + conf := api.DefaultConfig() + conf.Address = consulAddress + var err error + client, err = api.NewClient(conf) + + if err != nil { + log.Log.Error("failed to init consul client", err) + } +} + +func getConsulAddress(path string) string { + res := os.Getenv(models.EnvConsulAddress) + if res != "" { + return res + } + + cfg, err := loadCfgInfo(path) + if err != nil { + log.Log.Error("load consul config info error", err) + return defaultAddress + } else { + if addr, exist := cfg["address"]; exist { + return addr.(string) + } else { + return defaultAddress + } + } +} + +func loadCfgInfo(path string) (map[interface{}]interface{}, error) { + log.Log.Informational("consul config path is:" + path) + cfg, err := util.Read(path) + if err != nil { + return nil, err + } + + result := make(map[interface{}]interface{}) + err = util.UnmarshalYaml(cfg, result) + if err != nil { + return nil, err + } + return result, nil +} + +func GetServices() (map[string][]string, error) { + data, _, err := client.Catalog().Services(nil) + + if err != nil { + return nil, err + } + return data, nil +} + +func GetInstances(serviceName string) ([]*api.CatalogService, error) { + endpoints, _, err := client.Catalog().Service(serviceName, "", nil) + if err != nil { + log.Log.Error("can not get endpoints of ", serviceName) + return nil, err + } + return endpoints, nil +} diff --git a/msb2pilot/src/msb2pilot/consul/controller_test.go b/msb2pilot/src/msb2pilot/consul/controller_test.go new file mode 100644 index 0000000..a6569bd --- /dev/null +++ b/msb2pilot/src/msb2pilot/consul/controller_test.go @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2018 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial Project + */ +package consul + +import ( + "msb2pilot/models" + "os" + "testing" +) + +func TestSetConsulAddress(t *testing.T) { + cases := []struct { + env, path, want string + }{ + { + env: "testEnv", + path: "", + want: `testEnv`, + }, + { + env: "", + path: cfgFilePath, + want: `http://127.0.0.1:8500`, + }, + { + env: "testEnvWithPath", + path: cfgFilePath, + want: `testEnvWithPath`, + }, + { + env: "", + path: ``, + want: `http://localhost:8500`, + }, + { + env: "", + path: `controller.go`, + want: `http://localhost:8500`, + }, + } + + oldEnv := os.Getenv(models.EnvConsulAddress) + + for _, cas := range cases { + os.Setenv(models.EnvConsulAddress, cas.env) + + res := getConsulAddress(cas.path) + if res != cas.want { + t.Errorf("getConsulAddress() => want %s, got %s", cas.want, res) + } + } + + os.Setenv(models.EnvConsulAddress, oldEnv) +} + +func TestLoadCfgInfo(t *testing.T) { + cases := []struct { + path, status string + }{ + { + path: cfgFilePath, + status: `success`, + }, + { + path: ``, + status: `path is empty`, + }, + { + path: `controller.go`, + status: `yaml format error`, + }, + } + + for _, cas := range cases { + _, err := loadCfgInfo(cas.path) + if (cas.status == "success" && err != nil) || (cas.status != "success" && err == nil) { + t.Errorf("loadCfgInfo() => want %s, got %v", cas.status, err) + } + } +} diff --git a/msb2pilot/src/msb2pilot/consul/monitor.go b/msb2pilot/src/msb2pilot/consul/monitor.go new file mode 100644 index 0000000..c3adde4 --- /dev/null +++ b/msb2pilot/src/msb2pilot/consul/monitor.go @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2018 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial Project + */ +package consul + +import ( + "msb2pilot/log" + "msb2pilot/models" + "time" + + "github.com/hashicorp/consul/api" +) + +type Monitor interface { + Start(<-chan struct{}) +} + +type ServiceHandler func(newServices []*models.MsbService) + +type consulMonitor struct { + discovery *api.Client + period time.Duration + serviceHandler ServiceHandler +} + +func NewConsulMonitor(client *api.Client, period time.Duration, serviceHandler ServiceHandler) Monitor { + return &consulMonitor{ + discovery: client, + period: period, + serviceHandler: serviceHandler, + } +} + +func (this *consulMonitor) Start(stop <-chan struct{}) { + this.run(stop) +} + +func (this *consulMonitor) run(stop <-chan struct{}) { + ticker := time.NewTicker(this.period) + for { + select { + case <-stop: + ticker.Stop() + return + case <-ticker.C: + this.updateServiceRecord() + } + } + +} + +func (this *consulMonitor) updateServiceRecord() { + data, err := GetServices() + if err != nil { + log.Log.Error("failed to get services from consul", err) + return + } + + newRecords := make([]*models.MsbService, 0, len(data)) + for name := range data { + endpoints, err := GetInstances(name) + if err != nil { + log.Log.Error("failed to get service instance of "+name, err) + continue + } + newRecords = append(newRecords, models.ConvertService(endpoints)) + } + + this.serviceHandler(newRecords) +} diff --git a/msb2pilot/src/msb2pilot/log/log_test.go b/msb2pilot/src/msb2pilot/log/log_test.go index 816e10f..9ed192a 100644 --- a/msb2pilot/src/msb2pilot/log/log_test.go +++ b/msb2pilot/src/msb2pilot/log/log_test.go @@ -12,14 +12,13 @@ package log import ( - "apiroute/util" + "msb2pilot/util" "os" "strings" "testing" ) func TestCheckLogDir(t *testing.T) { - pathSep := string(os.PathSeparator) cases := []struct { path string exist bool @@ -33,7 +32,7 @@ func TestCheckLogDir(t *testing.T) { exist: false, }, { - path: `.` + pathSep + `test` + pathSep + `test.log`, + path: `.` + util.PathSep + `test` + util.PathSep + `test.log`, exist: true, }, } @@ -41,7 +40,7 @@ func TestCheckLogDir(t *testing.T) { for _, cas := range cases { checkLogDir(cas.path) - index := strings.LastIndex(cas.path, pathSep) + index := strings.LastIndex(cas.path, util.PathSep) if cas.exist && !util.FileExists(cas.path[0:index]) { t.Errorf("checkLogDir() => dir not exist, want %s", cas.path) } @@ -52,13 +51,12 @@ func TestCheckLogDir(t *testing.T) { } func TestLoadCustom(t *testing.T) { - pathSep := string(os.PathSeparator) cases := []struct { path string want string }{ { - path: `..` + pathSep + "conf" + pathSep + cfgFileName, + path: `..` + util.PathSep + "conf" + util.PathSep + cfgFileName, want: "success", }, { diff --git a/msb2pilot/src/msb2pilot/main.go b/msb2pilot/src/msb2pilot/main.go index c19000a..7ed762c 100644 --- a/msb2pilot/src/msb2pilot/main.go +++ b/msb2pilot/src/msb2pilot/main.go @@ -12,13 +12,30 @@ package main import ( + "fmt" + "msb2pilot/consul" "msb2pilot/log" + "msb2pilot/models" _ "msb2pilot/routers" + "time" "github.com/astaxie/beego" ) func main() { log.Log.Informational("**************** init msb2pilot ************************") + // start sync msb data + go syncConsulData() + beego.Run() } + +func syncConsulData() { + stop := make(chan struct{}) + monitor := consul.NewConsulMonitor(nil, 20*time.Second, syncMsbData) + monitor.Start(stop) +} + +func syncMsbData(newServices []*models.MsbService) { + fmt.Println(len(newServices), "services updated", time.Now()) +} diff --git a/msb2pilot/src/msb2pilot/models/config.go b/msb2pilot/src/msb2pilot/models/config.go new file mode 100644 index 0000000..7f34514 --- /dev/null +++ b/msb2pilot/src/msb2pilot/models/config.go @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2018 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial Project + */ +package models + +const ( + EnvConsulAddress = "ConsulAddress" //http://localhost:8500 +) diff --git a/msb2pilot/src/msb2pilot/models/conversion.go b/msb2pilot/src/msb2pilot/models/conversion.go new file mode 100644 index 0000000..144d05b --- /dev/null +++ b/msb2pilot/src/msb2pilot/models/conversion.go @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2018 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial Project + */ +package models + +import ( + "encoding/json" + "msb2pilot/log" + "strings" + + "github.com/hashicorp/consul/api" +) + +func ConvertService(endpoints []*api.CatalogService) *MsbService { + if len(endpoints) == 0 { + return nil + } + + endpoint := endpoints[0] + service := &MsbService{ + ServiceName: endpoint.ServiceName, + ModifyIndex: endpoint.ModifyIndex, + ConsulLabels: &ConsulLabels{}, + } + + convertMsbLabels(service.ConsulLabels, endpoint.ServiceTags) + return service +} + +func convertBaseInfo(baseString string) (baseInfo *BaseInfo, err error) { + baseInfo = new(BaseInfo) + err = json.Unmarshal([]byte(baseString), baseInfo) + + return +} + +func convertNameSpace(ns string) (nameSpace *NameSpace, err error) { + nameSpace = new(NameSpace) + err = json.Unmarshal([]byte(ns), nameSpace) + return +} + +func LoadMsbServiceFromMap(services map[string][]string) []*MsbService { + result := make([]*MsbService, 0, len(services)) + + for k, v := range services { + service := &MsbService{ + ServiceName: k, + ConsulLabels: new(ConsulLabels), + } + convertMsbLabels(service.ConsulLabels, v) + result = append(result, service) + } + return result +} + +func ConsulService2MsbService(consulService *api.CatalogService) *MsbService { + msbService := &MsbService{ + ServiceName: consulService.ServiceName, + ServiceAddress: consulService.ServiceAddress, + ServicePort: consulService.ServicePort, + ConsulLabels: new(ConsulLabels), + } + + convertMsbLabels(msbService.ConsulLabels, consulService.ServiceTags) + + return msbService +} + +func convertMsbLabel(label, labelstr string) interface{} { + var result interface{} + var err error + + labelPrefix := "\"" + label + "\":" + + if strings.HasPrefix(labelstr, labelPrefix) { + kvp := strings.Split(labelstr, labelPrefix) + value := kvp[1] + + switch label { + case "base": + result, err = convertBaseInfo(value) + case "ns": + result, err = convertNameSpace(value) + } + + if err != nil { + log.Log.Error("parse msb label error", err) + return nil + } + + return result + } + + return nil +} +func convertMsbLabels(consulLabels *ConsulLabels, labels []string) { + for _, label := range labels { + baseInfo := convertMsbLabel("base", label) + if baseInfo != nil { + consulLabels.BaseInfo = baseInfo.(*BaseInfo) + } + + ns := convertMsbLabel("ns", label) + if ns != nil { + consulLabels.NameSpace = ns.(*NameSpace) + } + } +} diff --git a/msb2pilot/src/msb2pilot/models/conversion_test.go b/msb2pilot/src/msb2pilot/models/conversion_test.go new file mode 100644 index 0000000..212e994 --- /dev/null +++ b/msb2pilot/src/msb2pilot/models/conversion_test.go @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2018 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial Project + */ +package models + +import ( + "reflect" + "testing" +) + +func TestConvertBaseInfo(t *testing.T) { + cases := []struct{ in, want string }{ + { + in: `{"enable_ssl":"true", "version":"v1","protocol":"REST","publish_port":"28012|28013","url":"/api/itm-pmadaptor/v1","is_manual":"false","visualRange":"0","appversion":"v1.18.20.04"}`, + want: `v1`, + }, + { + in: `{"enable_ssl":"true", "version":"v2","protocol":"UI", "status":"1", "url":"/api/itm-pmadaptor/v1","is_manual":"false"}`, + want: `v2`, + }, + { + in: `{"others":"other"}`, + want: ``, + }, + { + in: ``, + want: ``, + }, + } + + for _, cas := range cases { + got, _ := convertBaseInfo(cas.in) + if got.Version != cas.want { + t.Errorf("convertBaseInfo(%s) => got %s, want %s", cas.in, got.AppVersion, cas.want) + } + } + +} + +func TestConvertNameSpace(t *testing.T) { + cases := []struct{ in, want string }{ + { + in: `{"namespace":"test"}`, + want: `test`, + }, + { + in: `{"namespace":"testwithother", "others":"other"}`, + want: `testwithother`, + }, + { + in: `{"others":"other"}`, + want: ``, + }, + { + in: ``, + want: ``, + }, + } + + for _, cas := range cases { + got, _ := convertNameSpace(cas.in) + if got.NameSpace != cas.want { + t.Errorf("convertNameSpace(%s) => got %s, want %s", cas.in, got.NameSpace, cas.want) + } + } + +} + +func TestConvertMsbLabel(t *testing.T) { + cases := []struct{ label, in, want string }{ + { + label: "base", + in: `"base":{"enable_ssl":"true", "version":"v1","protocol":"REST","publish_port":"28012|28013","url":"/api/itm-pmadaptor/v1","is_manual":"false","visualRange":"0","appversion":"v1.18.20.04"}`, + want: `*models.BaseInfo`, + }, + { + label: "ns", + in: `"ns":{"namespace":"test"}`, + want: `*models.NameSpace`, + }, + { + label: "others", + in: `{"others":"other"}`, + want: ``, + }, + { + label: "", + in: ``, + want: ``, + }, + } + + for _, cas := range cases { + got := convertMsbLabel(cas.label, cas.in) + + if got == nil { + if cas.want != "" { + t.Errorf("convertMsbLabel(%s, %s) => got nil, want %s", cas.label, cas.in, cas.want) + } + } else { + if reflect.TypeOf(got).String() != cas.want { + t.Errorf("convertMsbLabel(%s, %s) => got %v, want %s", cas.label, cas.in, reflect.TypeOf(got), cas.want) + } + } + } + +} diff --git a/msb2pilot/src/msb2pilot/models/msb.go b/msb2pilot/src/msb2pilot/models/msb.go new file mode 100644 index 0000000..09418e7 --- /dev/null +++ b/msb2pilot/src/msb2pilot/models/msb.go @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2018 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial Project + */ +package models + +type Protocol string + +const ( + Protocol_UI Protocol = "UI" + Protocol_REST Protocol = "REST" + Protocol_HTTP Protocol = "HTTP" + Protocol_MQ Protocol = "MQ" + Protocol_FTP Protocol = "FTP" + Protocol_SNMP Protocol = "SNMP" + Protocol_TCP Protocol = "TCP" + Protocol_UDP Protocol = "UDP" +) + +type BaseInfo struct { + Path string `json:"path",omitempty` + VisualRange string `json:"visualRange"` + AppVersion string `json:"appversion"` + PublishPort string `json:"publish_port"` + EnableSSL string `json:"enable_ssl",omitempty` + IsManual string `json:"is_manual"` + Protocol Protocol `json:"protocol"` + ServiceStatus string `json:"status,omitempty"` + Version string `json:"version"` + Url string `json:"url"` +} + +type NameSpace struct { + NameSpace string `json:"namespace"` +} + +type ConsulLabels struct { + NameSpace *NameSpace + BaseInfo *BaseInfo +} + +type MsbService struct { + ConsulLabels *ConsulLabels + ServiceName string + ServiceAddress string + ServicePort int + ModifyIndex uint64 +} + +type PublishService struct { + ServiceName string `json:"serviceName"` + Version string `json:"version",omitempty` + PublishPort string `json:"publish_port",omitempty` + Protocol string `json:"protocol",omitempty` + NameSpace string `json:"namespace",omitempty` + PublishUrl string `json:"publish_url",omitempty` + PublishProtocol string `json:"publish_protocol",omitempty` +} diff --git a/msb2pilot/src/msb2pilot/util/common.go b/msb2pilot/src/msb2pilot/util/common.go index 1bb10df..d46bb6f 100644 --- a/msb2pilot/src/msb2pilot/util/common.go +++ b/msb2pilot/src/msb2pilot/util/common.go @@ -19,6 +19,7 @@ import ( const ( ConfigPath = "conf" + PathSep = string(os.PathSeparator) ) func GetCfgPath() string { |