add metadata unit test (#2665)
diff --git a/global/application_config.go b/global/application_config.go
index dbb35a9..9778997 100644
--- a/global/application_config.go
+++ b/global/application_config.go
@@ -44,14 +44,15 @@
}
return &ApplicationConfig{
- Organization: c.Organization,
- Name: c.Name,
- Module: c.Module,
- Group: c.Group,
- Version: c.Version,
- Owner: c.Owner,
- Environment: c.Environment,
- MetadataType: c.MetadataType,
- Tag: c.Tag,
+ Organization: c.Organization,
+ Name: c.Name,
+ Module: c.Module,
+ Group: c.Group,
+ Version: c.Version,
+ Owner: c.Owner,
+ Environment: c.Environment,
+ MetadataType: c.MetadataType,
+ Tag: c.Tag,
+ MetadataServicePort: c.MetadataServicePort,
}
}
diff --git a/metadata/client.go b/metadata/client.go
index 6b9120e..a3e7dd4 100644
--- a/metadata/client.go
+++ b/metadata/client.go
@@ -20,7 +20,6 @@
import (
"context"
"encoding/json"
- "time"
)
import (
@@ -37,49 +36,35 @@
"dubbo.apache.org/dubbo-go/v3/registry"
)
-const metadataProxyDefaultTimeout = 5000
+const defaultTimeout = "5s" // s
-// GetMetadataFromMetadataReport test depends on dubbo protocol, if dubbo not dependent on config package, can move to metadata dir
func GetMetadataFromMetadataReport(revision string, instance registry.ServiceInstance) (*info.MetadataInfo, error) {
report := GetMetadataReport()
+ if report == nil {
+ return nil, perrors.New("no metadata report instance found,please check ")
+ }
return report.GetAppMetadata(instance.GetServiceName(), revision)
}
func GetMetadataFromRpc(revision string, instance registry.ServiceInstance) (*info.MetadataInfo, error) {
- service, destroy, err := createRpcClient(instance)
- if err != nil {
- return nil, err
+ params := getMetadataServiceUrlParams(instance.GetMetadata()[constant.MetadataServiceURLParamsPropertyName])
+ url := buildMetadataServiceURL(instance.GetServiceName(), instance.GetHost(), params)
+ url.SetParam(constant.TimeoutKey, defaultTimeout)
+ rpcService := &remoteMetadataService{}
+ invoker := extension.GetProtocol(constant.Dubbo).Refer(url)
+ if invoker == nil {
+ return nil, perrors.New("create invoker error, can not connect to the metadata report server: " + url.Ip + ":" + url.Port)
}
- ctx, cancel := context.WithTimeout(context.Background(), time.Duration(metadataProxyDefaultTimeout))
- defer cancel()
- defer destroy()
- return service.GetMetadataInfo(ctx, revision)
+ proxy := extension.GetProxyFactory(constant.DefaultKey).GetProxy(invoker, url)
+ proxy.Implement(rpcService)
+ defer invoker.Destroy()
+ return rpcService.GetMetadataInfo(context.TODO(), revision)
}
type remoteMetadataService struct {
GetMetadataInfo func(context context.Context, revision string) (*info.MetadataInfo, error) `dubbo:"getMetadataInfo"`
}
-func createRpcClient(instance registry.ServiceInstance) (*remoteMetadataService, func(), error) {
- params := getMetadataServiceUrlParams(instance.GetMetadata()[constant.MetadataServiceURLParamsPropertyName])
- url := buildMetadataServiceURL(instance.GetServiceName(), instance.GetHost(), params)
- return createRpcClientByUrl(url)
-}
-
-func createRpcClientByUrl(url *common.URL) (*remoteMetadataService, func(), error) {
- rpcService := &remoteMetadataService{}
- invoker := extension.GetProtocol(constant.Dubbo).Refer(url)
- if invoker == nil {
- return nil, nil, perrors.New("create invoker error, can not connect to the metadata report server: " + url.Ip + ":" + url.Port)
- }
- proxy := extension.GetProxyFactory(constant.DefaultKey).GetProxy(invoker, url)
- proxy.Implement(rpcService)
- destroy := func() {
- invoker.Destroy()
- }
- return rpcService, destroy, nil
-}
-
// buildMetadataServiceURL will use standard format to build the metadata service url.
func buildMetadataServiceURL(serviceName string, host string, params map[string]string) *common.URL {
if params[constant.ProtocolKey] == "" {
@@ -108,7 +93,7 @@
if len(jsonStr) > 0 {
err := json.Unmarshal([]byte(jsonStr), &res)
if err != nil {
- logger.Errorf("could not parse the metadata service url parameters to map", err)
+ logger.Errorf("could not parse the metadata service url parameters '%s' to map", jsonStr)
}
}
return res
diff --git a/metadata/client_test.go b/metadata/client_test.go
new file mode 100644
index 0000000..aa9fe76
--- /dev/null
+++ b/metadata/client_test.go
@@ -0,0 +1,280 @@
+/*
+ * 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 metadata
+
+import (
+ "context"
+ "testing"
+)
+
+import (
+ "github.com/pkg/errors"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/common"
+ "dubbo.apache.org/dubbo-go/v3/common/constant"
+ "dubbo.apache.org/dubbo-go/v3/common/extension"
+ "dubbo.apache.org/dubbo-go/v3/metadata/info"
+ "dubbo.apache.org/dubbo-go/v3/protocol"
+ _ "dubbo.apache.org/dubbo-go/v3/proxy/proxy_factory"
+ "dubbo.apache.org/dubbo-go/v3/registry"
+)
+
+var (
+ ins = ®istry.DefaultServiceInstance{
+ ID: "1",
+ Metadata: map[string]string{
+ constant.MetadataServiceURLParamsPropertyName: `{
+ "application": "dubbo-go",
+ "group": "BDTService",
+ "port": "64658",
+ "protocol": "dubbo",
+ "version": "1.0.0"
+ }`,
+ },
+ Host: "dubbo.io",
+ ServiceName: "dubbo-app",
+ }
+ metadataInfo = &info.MetadataInfo{
+ App: "dubbo-app",
+ }
+)
+
+func TestGetMetadataFromMetadataReport(t *testing.T) {
+ t.Run("no report instance", func(t *testing.T) {
+ _, err := GetMetadataFromMetadataReport("1", ins)
+ assert.NotNil(t, err)
+ })
+ mockReport := new(mockMetadataReport)
+ defer mockReport.AssertExpectations(t)
+ instances["default"] = mockReport
+ t.Run("normal", func(t *testing.T) {
+ mockReport.On("GetAppMetadata").Return(metadataInfo, nil).Once()
+ got, err := GetMetadataFromMetadataReport("1", ins)
+ assert.Nil(t, err)
+ assert.Equal(t, metadataInfo, got)
+ })
+ t.Run("error", func(t *testing.T) {
+ mockReport.On("GetAppMetadata").Return(metadataInfo, errors.New("mock error")).Once()
+ _, err := GetMetadataFromMetadataReport("1", ins)
+ assert.NotNil(t, err)
+ })
+}
+
+func TestGetMetadataFromRpc(t *testing.T) {
+ mockInvoker := new(mockInvoker)
+ defer mockInvoker.AssertExpectations(t)
+ mockProtocol := new(mockProtocol)
+ defer mockProtocol.AssertExpectations(t)
+ extension.SetProtocol("dubbo", func() protocol.Protocol {
+ return mockProtocol
+ })
+
+ result := &protocol.RPCResult{
+ Attrs: map[string]interface{}{},
+ Err: nil,
+ Rest: metadataInfo,
+ }
+ t.Run("normal", func(t *testing.T) {
+ mockProtocol.On("Refer").Return(mockInvoker).Once()
+ mockInvoker.On("Invoke").Return(result).Once()
+ mockInvoker.On("Destroy").Once()
+ metadata, err := GetMetadataFromRpc("111", ins)
+ assert.Nil(t, err)
+ assert.Equal(t, metadata, result.Rest)
+ })
+ t.Run("refer error", func(t *testing.T) {
+ mockProtocol.On("Refer").Return(nil).Once()
+ _, err := GetMetadataFromRpc("111", ins)
+ assert.NotNil(t, err)
+ })
+ t.Run("invoke timeout", func(t *testing.T) {
+ mockProtocol.On("Refer").Return(mockInvoker).Once()
+ mockInvoker.On("Invoke").Return(&protocol.RPCResult{
+ Attrs: map[string]interface{}{},
+ Err: errors.New("timeout error"),
+ Rest: metadataInfo,
+ }).Once()
+ mockInvoker.On("Destroy").Once()
+ _, err := GetMetadataFromRpc("111", ins)
+ assert.NotNil(t, err)
+ })
+}
+
+func Test_buildMetadataServiceURL(t *testing.T) {
+ type args struct {
+ serviceName string
+ host string
+ params map[string]string
+ }
+ tests := []struct {
+ name string
+ args args
+ want *common.URL
+ }{
+ {
+ name: "normal",
+ args: args{
+ serviceName: "dubbo-app",
+ host: "dubbo.io",
+ params: map[string]string{
+ constant.ProtocolKey: "dubbo",
+ constant.PortKey: "3000",
+ },
+ },
+ want: common.NewURLWithOptions(
+ common.WithIp("dubbo.io"),
+ common.WithProtocol("dubbo"),
+ common.WithPath(constant.MetadataServiceName),
+ common.WithProtocol("dubbo"),
+ common.WithPort("3000"),
+ common.WithParams(map[string][]string{
+ constant.ProtocolKey: {"dubbo"},
+ constant.PortKey: {"3000"},
+ }),
+ common.WithParamsValue(constant.GroupKey, "dubbo-app"),
+ common.WithParamsValue(constant.InterfaceKey, constant.MetadataServiceName),
+ ),
+ },
+ {
+ name: "no protocol",
+ args: args{
+ serviceName: "dubbo-app",
+ host: "dubbo.io",
+ params: map[string]string{},
+ },
+ want: nil,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, buildMetadataServiceURL(tt.args.serviceName, tt.args.host, tt.args.params), "buildMetadataServiceURL(%v, %v, %v)", tt.args.serviceName, tt.args.host, tt.args.params)
+ })
+ }
+}
+
+func Test_getMetadataServiceUrlParams(t *testing.T) {
+ type args struct {
+ jsonStr string
+ }
+ tests := []struct {
+ name string
+ args args
+ want map[string]string
+ }{
+ {
+ name: "normal",
+ args: args{
+ jsonStr: `{
+ "application": "BDTService",
+ "group": "BDTService",
+ "port": "64658",
+ "protocol": "dubbo",
+ "release": "dubbo-golang-3.0.0",
+ "timestamp": "1713432877",
+ "version": "1.0.0"
+ }`,
+ },
+ want: map[string]string{
+ "application": "BDTService",
+ "group": "BDTService",
+ "port": "64658",
+ "protocol": "dubbo",
+ "release": "dubbo-golang-3.0.0",
+ "timestamp": "1713432877",
+ "version": "1.0.0",
+ },
+ },
+ {
+ name: "wrong format",
+ args: args{
+ jsonStr: "xxx",
+ },
+ want: map[string]string{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, getMetadataServiceUrlParams(tt.args.jsonStr), "getMetadataServiceUrlParams(%v)", tt.args.jsonStr)
+ })
+ }
+}
+
+type mockProtocol struct {
+ mock.Mock
+}
+
+func (m *mockProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+ args := m.Called()
+ return args.Get(0).(protocol.Exporter)
+}
+
+func (m *mockProtocol) Refer(url *common.URL) protocol.Invoker {
+ args := m.Called()
+ if args.Get(0) == nil {
+ return nil
+ }
+ return args.Get(0).(protocol.Invoker)
+}
+
+func (m *mockProtocol) Destroy() {
+}
+
+type mockInvoker struct {
+ mock.Mock
+}
+
+func (m *mockInvoker) GetURL() *common.URL {
+ return nil
+}
+
+func (m *mockInvoker) IsAvailable() bool {
+ return true
+}
+
+func (m *mockInvoker) Destroy() {
+ m.Called()
+}
+
+func (m *mockInvoker) Invoke(ctx context.Context, inv protocol.Invocation) protocol.Result {
+ args := m.Mock.Called()
+ meta := args.Get(0).(protocol.Result).Result().(*info.MetadataInfo)
+ reply := inv.Reply().(*info.MetadataInfo)
+ reply.App = meta.App
+ reply.Tag = meta.Tag
+ reply.Revision = meta.Revision
+ reply.Services = meta.Services
+ return args.Get(0).(protocol.Result)
+}
+
+type mockExporter struct {
+ mock.Mock
+}
+
+func (m *mockExporter) GetInvoker() protocol.Invoker {
+ args := m.Called()
+ return args.Get(0).(protocol.Invoker)
+}
+
+func (m *mockExporter) UnExport() {
+ m.Called()
+}
diff --git a/metadata/info/metadata_info.go b/metadata/info/metadata_info.go
index 471a97d..126d27c 100644
--- a/metadata/info/metadata_info.go
+++ b/metadata/info/metadata_info.go
@@ -22,6 +22,7 @@
"hash/crc32"
"net/url"
"sort"
+ "strconv"
"strings"
)
@@ -66,8 +67,18 @@
subscribedServiceURLs map[string][]*common.URL `hessian:"-"` // client subscribed service urls
}
+func NewAppMetadataInfo(app string) *MetadataInfo {
+ return NewMetadataInfo(app, "")
+}
+
func NewMetadataInfo(app, tag string) *MetadataInfo {
- return NewMetadataInfoWithParams(app, "", make(map[string]*ServiceInfo))
+ return &MetadataInfo{
+ App: app,
+ Tag: tag,
+ Services: make(map[string]*ServiceInfo),
+ exportedServiceURLs: make(map[string][]*common.URL),
+ subscribedServiceURLs: make(map[string][]*common.URL),
+ }
}
func NewMetadataInfoWithParams(app string, revision string, services map[string]*ServiceInfo) *MetadataInfo {
@@ -207,9 +218,9 @@
URL *common.URL `json:"-" hessian:"-"`
}
-// nolint
func NewServiceInfoWithURL(url *common.URL) *ServiceInfo {
service := NewServiceInfo(url.Service(), url.Group(), url.Version(), url.Protocol, url.Path, nil)
+ service.Port, _ = strconv.Atoi(url.Port)
service.URL = url
// TODO includeKeys load dynamic
p := make(map[string]string, 8)
@@ -231,7 +242,6 @@
return service
}
-// nolint
func NewServiceInfo(name, group, version, protocol, path string, params map[string]string) *ServiceInfo {
serviceKey := common.ServiceKey(name, group, version)
matchKey := common.MatchKey(serviceKey, protocol)
@@ -256,16 +266,13 @@
return strings.Split(s, ",")
}
-// nolint
func (si *ServiceInfo) GetParams() url.Values {
v := url.Values{}
- methodNames := si.Params[constant.MethodsKey]
- if len(methodNames) == 0 {
- return v
- }
methods := gxset.NewSet()
- for _, method := range strings.Split(si.Params[constant.MethodsKey], ",") {
- methods.Add(method)
+ if methodNames, ok := si.Params[constant.MethodsKey]; ok {
+ for _, method := range strings.Split(methodNames, ",") {
+ methods.Add(method)
+ }
}
for k, p := range si.Params {
ms := strings.Index(k, ".")
@@ -278,7 +285,6 @@
return v
}
-// nolint
func (si *ServiceInfo) GetMatchKey() string {
if si.MatchKey != "" {
return si.MatchKey
@@ -288,7 +294,6 @@
return si.MatchKey
}
-// nolint
func (si *ServiceInfo) GetServiceKey() string {
if si.ServiceKey != "" {
return si.ServiceKey
diff --git a/metadata/info/metadata_info_test.go b/metadata/info/metadata_info_test.go
index 41889bf..94cd6e0 100644
--- a/metadata/info/metadata_info_test.go
+++ b/metadata/info/metadata_info_test.go
@@ -19,6 +19,8 @@
import (
"encoding/json"
+ "strconv"
+ "strings"
"testing"
)
@@ -32,6 +34,19 @@
"dubbo.apache.org/dubbo-go/v3/common"
)
+var (
+ serviceUrl = common.NewURLWithOptions(
+ common.WithProtocol("tri"),
+ common.WithIp("127.0.0.1"),
+ common.WithPort("20035"),
+ common.WithPath("/org.apache.dubbo.samples.proto.GreetService"),
+ common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+ common.WithMethods([]string{"Greet", "SayHello"}),
+ common.WithParamsValue("loadbalance", "random"),
+ common.WithParamsValue("methods.Greet.timeout", "1000"),
+ )
+)
+
func TestMetadataInfoAddService(t *testing.T) {
metadataInfo := &MetadataInfo{
Services: make(map[string]*ServiceInfo),
@@ -42,11 +57,11 @@
url, _ := common.NewURL("dubbo://127.0.0.1:20000?application=foo&category=providers&check=false&dubbo=dubbo-go+v1.5.0&interface=com.foo.Bar&methods=GetPetByID%2CGetPetTypes&organization=Apache&owner=foo&revision=1.0.0&side=provider&version=1.0.0")
metadataInfo.AddService(url)
assert.True(t, len(metadataInfo.Services) > 0)
- assert.True(t, len(metadataInfo.exportedServiceURLs) > 0)
+ assert.True(t, len(metadataInfo.GetExportedServiceURLs()) > 0)
metadataInfo.RemoveService(url)
assert.True(t, len(metadataInfo.Services) == 0)
- assert.True(t, len(metadataInfo.exportedServiceURLs) == 0)
+ assert.True(t, len(metadataInfo.GetExportedServiceURLs()) == 0)
}
func TestHessian(t *testing.T) {
@@ -67,3 +82,78 @@
metaJson, _ := json.Marshal(metadataInfo)
assert.Equal(t, objJson, metaJson)
}
+
+func TestMetadataInfoAddSubscribeURL(t *testing.T) {
+ info := NewMetadataInfo("dubbo", "tag")
+ info.AddSubscribeURL(serviceUrl)
+ assert.True(t, len(info.GetSubscribedURLs()) > 0)
+ info.RemoveSubscribeURL(serviceUrl)
+ assert.True(t, len(info.GetSubscribedURLs()) == 0)
+}
+
+func TestMetadataInfoCalAndGetRevision(t *testing.T) {
+ metadata := NewAppMetadataInfo("dubbo")
+ assert.Equalf(t, "0", metadata.CalAndGetRevision(), "CalAndGetRevision()")
+ metadata.AddService(serviceUrl)
+ assert.True(t, metadata.CalAndGetRevision() != "0")
+
+ v := metadata.Revision
+ assert.Equal(t, v, metadata.CalAndGetRevision(), "CalAndGetRevision() test cache")
+
+ metadata = NewAppMetadataInfo("dubbo")
+ url1 := serviceUrl.Clone()
+ url1.Methods = []string{}
+ metadata.AddService(url1)
+ assert.True(t, metadata.CalAndGetRevision() != "0", "CalAndGetRevision() test empty methods")
+}
+
+func TestNewMetadataInfo(t *testing.T) {
+ info := NewMetadataInfo("dubbo", "tag")
+ assert.Equal(t, info.App, "dubbo")
+ assert.Equal(t, info.Tag, "tag")
+}
+
+func TestNewMetadataInfoWithParams(t *testing.T) {
+ info := NewMetadataInfoWithParams("dubbo", "",
+ map[string]*ServiceInfo{"org.apache.dubbo.samples.proto.GreetService": NewServiceInfoWithURL(serviceUrl)})
+ assert.Equal(t, info.App, "dubbo")
+ assert.Equal(t, info.Revision, "")
+ assert.Equal(t, info.Services, map[string]*ServiceInfo{"org.apache.dubbo.samples.proto.GreetService": NewServiceInfoWithURL(serviceUrl)})
+}
+
+func TestNewServiceInfoWithURL(t *testing.T) {
+ info := NewServiceInfoWithURL(serviceUrl)
+ assert.True(t, info.URL == serviceUrl)
+ assert.Equal(t, info.Protocol, serviceUrl.Protocol)
+ assert.Equal(t, info.Name, serviceUrl.Interface())
+ assert.Equal(t, info.Group, serviceUrl.Group())
+ assert.Equal(t, info.Version, serviceUrl.Version())
+ assert.Equal(t, strconv.Itoa(info.Port), serviceUrl.Port)
+ assert.Equal(t, info.Path, strings.TrimPrefix(serviceUrl.Path, "/"))
+ assert.Equal(t, info.Params["Greet.timeout"], "1000")
+}
+
+func TestServiceInfoGetMethods(t *testing.T) {
+ service := NewServiceInfoWithURL(serviceUrl)
+ assert.Equal(t, service.GetMethods(), []string{"Greet", "SayHello"})
+}
+
+func TestServiceInfoGetParams(t *testing.T) {
+ service := NewServiceInfoWithURL(serviceUrl)
+ assert.Equal(t, service.GetParams()["loadbalance"], []string{"random"})
+}
+
+func TestServiceInfoGetMatchKey(t *testing.T) {
+ si := NewServiceInfoWithURL(serviceUrl)
+ matchKey := si.MatchKey
+ assert.Equal(t, si.GetMatchKey(), matchKey)
+ si.MatchKey = ""
+ assert.True(t, si.GetMatchKey() != "")
+ si.MatchKey = ""
+ si.ServiceKey = ""
+ assert.True(t, si.GetMatchKey() != "")
+}
+
+func TestServiceInfoJavaClassName(t *testing.T) {
+ assert.Equalf(t, "org.apache.dubbo.metadata.MetadataInfo", NewAppMetadataInfo("dubbo").JavaClassName(), "JavaClassName()")
+}
diff --git a/metadata/mapping/metadata/service_name_mapping.go b/metadata/mapping/metadata/service_name_mapping.go
index 95e588e..1346436 100644
--- a/metadata/mapping/metadata/service_name_mapping.go
+++ b/metadata/mapping/metadata/service_name_mapping.go
@@ -23,7 +23,6 @@
import (
gxset "github.com/dubbogo/gost/container/set"
- "github.com/dubbogo/gost/log/logger"
perrors "github.com/pkg/errors"
)
@@ -81,7 +80,6 @@
}
}
if err != nil {
- logger.Errorf("Failed registering mapping to remote, &v", err)
return err
}
}
diff --git a/metadata/mapping/metadata/service_name_mapping_test.go b/metadata/mapping/metadata/service_name_mapping_test.go
new file mode 100644
index 0000000..01b25f5
--- /dev/null
+++ b/metadata/mapping/metadata/service_name_mapping_test.go
@@ -0,0 +1,182 @@
+/*
+ * 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 metadata
+
+import (
+ "errors"
+ "testing"
+)
+
+import (
+ gxset "github.com/dubbogo/gost/container/set"
+ "github.com/dubbogo/gost/gof/observer"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/common"
+ "dubbo.apache.org/dubbo-go/v3/common/constant"
+ "dubbo.apache.org/dubbo-go/v3/common/extension"
+ "dubbo.apache.org/dubbo-go/v3/metadata"
+ "dubbo.apache.org/dubbo-go/v3/metadata/info"
+ "dubbo.apache.org/dubbo-go/v3/metadata/mapping"
+ "dubbo.apache.org/dubbo-go/v3/metadata/report"
+)
+
+func TestGetNameMappingInstance(t *testing.T) {
+ ins := GetNameMappingInstance()
+ assert.NotNil(t, ins)
+}
+
+func TestNoReportInstance(t *testing.T) {
+ ins := GetNameMappingInstance()
+ lis := &listener{}
+ serviceUrl := common.NewURLWithOptions(
+ common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+ common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+ )
+ _, err := ins.Get(serviceUrl, lis)
+ assert.NotNil(t, err, "test Get no report instance")
+ err = ins.Map(serviceUrl)
+ assert.NotNil(t, err, "test Map with no report instance")
+ err = ins.Remove(serviceUrl)
+ assert.NotNil(t, err, "test Remove with no report instance")
+}
+
+func TestServiceNameMappingGet(t *testing.T) {
+ ins := GetNameMappingInstance()
+ lis := &listener{}
+ serviceUrl := common.NewURLWithOptions(
+ common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+ common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+ )
+ mockReport, err := initMock()
+ assert.Nil(t, err)
+ t.Run("test normal", func(t *testing.T) {
+ mockReport.On("GetServiceAppMapping").Return(gxset.NewSet("dubbo"), nil).Once()
+ apps, er := ins.Get(serviceUrl, lis)
+ assert.Nil(t, er)
+ assert.True(t, !apps.Empty())
+ })
+ t.Run("test error", func(t *testing.T) {
+ mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), errors.New("mock error")).Once()
+ _, err = ins.Get(serviceUrl, lis)
+ assert.NotNil(t, err)
+ })
+ mockReport.AssertExpectations(t)
+}
+
+func TestServiceNameMappingMap(t *testing.T) {
+ ins := GetNameMappingInstance()
+ serviceUrl := common.NewURLWithOptions(
+ common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+ common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+ )
+ mockReport, err := initMock()
+ assert.Nil(t, err)
+ t.Run("test normal", func(t *testing.T) {
+ mockReport.On("RegisterServiceAppMapping").Return(nil).Once()
+ err = ins.Map(serviceUrl)
+ assert.Nil(t, err)
+ })
+ t.Run("test error", func(t *testing.T) {
+ mockReport.On("RegisterServiceAppMapping").Return(errors.New("mock error")).Times(retryTimes)
+ err = ins.Map(serviceUrl)
+ assert.NotNil(t, err, "test mapping error")
+ })
+ mockReport.AssertExpectations(t)
+}
+
+func TestServiceNameMappingRemove(t *testing.T) {
+ ins := GetNameMappingInstance()
+ serviceUrl := common.NewURLWithOptions(
+ common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+ common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+ )
+ mockReport, err := initMock()
+ assert.Nil(t, err)
+ t.Run("test normal", func(t *testing.T) {
+ mockReport.On("RemoveServiceAppMappingListener").Return(nil).Once()
+ err = ins.Remove(serviceUrl)
+ assert.Nil(t, err)
+ })
+ t.Run("test error", func(t *testing.T) {
+ mockReport.On("RemoveServiceAppMappingListener").Return(errors.New("mock error")).Once()
+ err = ins.Remove(serviceUrl)
+ assert.NotNil(t, err)
+ })
+ mockReport.AssertExpectations(t)
+}
+
+func initMock() (*mockMetadataReport, error) {
+ metadataReport := new(mockMetadataReport)
+ extension.SetMetadataReportFactory("mock", func() report.MetadataReportFactory {
+ return metadataReport
+ })
+ opts := metadata.NewReportOptions(
+ metadata.WithProtocol("mock"),
+ metadata.WithAddress("127.0.0.1"),
+ )
+ err := opts.Init()
+ return metadataReport, err
+}
+
+type listener struct {
+}
+
+func (l listener) OnEvent(e observer.Event) error {
+ return nil
+}
+
+func (l listener) Stop() {
+}
+
+type mockMetadataReport struct {
+ mock.Mock
+}
+
+func (m *mockMetadataReport) CreateMetadataReport(*common.URL) report.MetadataReport {
+ return m
+}
+
+func (m *mockMetadataReport) GetAppMetadata(string, string) (*info.MetadataInfo, error) {
+ args := m.Called()
+ return args.Get(0).(*info.MetadataInfo), args.Error(1)
+}
+
+func (m *mockMetadataReport) PublishAppMetadata(string, string, *info.MetadataInfo) error {
+ args := m.Called()
+ return args.Error(0)
+}
+
+func (m *mockMetadataReport) RegisterServiceAppMapping(string, string, string) error {
+ args := m.Called()
+ return args.Error(0)
+}
+
+func (m *mockMetadataReport) GetServiceAppMapping(string, string, mapping.MappingListener) (*gxset.HashSet, error) {
+ args := m.Called()
+ return args.Get(0).(*gxset.HashSet), args.Error(1)
+}
+
+func (m *mockMetadataReport) RemoveServiceAppMappingListener(string, string) error {
+ args := m.Called()
+ return args.Error(0)
+}
diff --git a/metadata/metadata.go b/metadata/metadata.go
index 6fbbfcd..03077bb 100644
--- a/metadata/metadata.go
+++ b/metadata/metadata.go
@@ -25,8 +25,8 @@
)
var (
- metadataService MetadataService = &DefaultMetadataService{}
- appMetadataInfoMap = make(map[string]*info.MetadataInfo)
+ registryMetadataInfo = make(map[string]*info.MetadataInfo)
+ metadataService MetadataService = &DefaultMetadataService{metadataMap: registryMetadataInfo}
)
func GetMetadataService() MetadataService {
@@ -34,25 +34,25 @@
}
func GetMetadataInfo(registryId string) *info.MetadataInfo {
- return appMetadataInfoMap[registryId]
+ return registryMetadataInfo[registryId]
}
func AddService(registryId string, url *common.URL) {
- if _, exist := appMetadataInfoMap[registryId]; !exist {
- appMetadataInfoMap[registryId] = info.NewMetadataInfo(
+ if _, exist := registryMetadataInfo[registryId]; !exist {
+ registryMetadataInfo[registryId] = info.NewMetadataInfo(
url.GetParam(constant.ApplicationKey, ""),
url.GetParam(constant.ApplicationTagKey, ""),
)
}
- appMetadataInfoMap[registryId].AddService(url)
+ registryMetadataInfo[registryId].AddService(url)
}
func AddSubscribeURL(registryId string, url *common.URL) {
- if _, exist := appMetadataInfoMap[registryId]; !exist {
- appMetadataInfoMap[registryId] = info.NewMetadataInfo(
+ if _, exist := registryMetadataInfo[registryId]; !exist {
+ registryMetadataInfo[registryId] = info.NewMetadataInfo(
url.GetParam(constant.ApplicationKey, ""),
url.GetParam(constant.ApplicationTagKey, ""),
)
}
- appMetadataInfoMap[registryId].AddSubscribeURL(url)
+ registryMetadataInfo[registryId].AddSubscribeURL(url)
}
diff --git a/metadata/metadata_service.go b/metadata/metadata_service.go
index 5a3591c..80eb159 100644
--- a/metadata/metadata_service.go
+++ b/metadata/metadata_service.go
@@ -40,8 +40,8 @@
// version will be used by Version func
const (
- version = "1.0.0"
- allServiceInterfaces = "*"
+ version = "1.0.0"
+ allMatch = "*"
)
// MetadataService is used to define meta data related behaviors
@@ -58,12 +58,11 @@
GetMetadataInfo(revision string) (*info.MetadataInfo, error)
// GetMetadataServiceURL will return the url of metadata service
GetMetadataServiceURL() (*common.URL, error)
- // SetMetadataServiceURL exporter to set url of metadata service, will not be exported by exporter,cause no error return
- SetMetadataServiceURL(*common.URL)
}
// DefaultMetadataService is store and query the metadata info in memory when each service registry
type DefaultMetadataService struct {
+ metadataMap map[string]*info.MetadataInfo
metadataUrl *common.URL
}
@@ -73,19 +72,16 @@
// GetExportedURLs get all exported urls
func (mts *DefaultMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]*common.URL, error) {
- if allServiceInterfaces == serviceInterface {
- return mts.GetExportedServiceURLs()
- }
all, err := mts.GetExportedServiceURLs()
if err != nil {
return nil, err
}
urls := make([]*common.URL, 0)
for _, url := range all {
- if url.GetParam(constant.InterfaceKey, "") == serviceInterface &&
- url.GetParam(constant.GroupKey, "") == group &&
- url.GetParam(constant.ProtocolKey, "") == protocol &&
- url.GetParam(constant.VersionKey, "") == version {
+ if (url.Interface() == serviceInterface || serviceInterface == allMatch) &&
+ (url.Group() == group || group == allMatch) &&
+ (url.Protocol == protocol || protocol == allMatch) &&
+ (url.Version() == version || version == allMatch) {
urls = append(urls, url)
}
}
@@ -97,7 +93,7 @@
if revision == "" {
return nil, nil
}
- for _, metadataInfo := range appMetadataInfoMap {
+ for _, metadataInfo := range mts.metadataMap {
if metadataInfo.Revision == revision {
return metadataInfo, nil
}
@@ -109,7 +105,7 @@
// GetExportedServiceURLs get exported service urls
func (mts *DefaultMetadataService) GetExportedServiceURLs() ([]*common.URL, error) {
urls := make([]*common.URL, 0)
- for _, metadataInfo := range appMetadataInfoMap {
+ for _, metadataInfo := range mts.metadataMap {
urls = append(urls, metadataInfo.GetExportedServiceURLs()...)
}
return urls, nil
@@ -127,7 +123,7 @@
func (mts *DefaultMetadataService) GetSubscribedURLs() ([]*common.URL, error) {
urls := make([]*common.URL, 0)
- for _, metadataInfo := range appMetadataInfoMap {
+ for _, metadataInfo := range mts.metadataMap {
urls = append(urls, metadataInfo.GetSubscribedURLs()...)
}
return urls, nil
@@ -141,15 +137,15 @@
}
}
-// ServiceExporter is the ConfigurableMetadataServiceExporter which implement MetadataServiceExporter interface
-type ServiceExporter struct {
+// serviceExporter export MetadataService with dubbo protocol
+type serviceExporter struct {
opts *Options
service MetadataService
protocolExporter protocol.Exporter
}
// Export will export the metadataService
-func (e *ServiceExporter) Export() error {
+func (e *serviceExporter) Export() error {
version, _ := e.service.Version()
var port string
if e.opts.port == 0 {
@@ -181,7 +177,7 @@
proxyFactory := extension.GetProxyFactory("")
invoker := proxyFactory.GetInvoker(ivkURL)
e.protocolExporter = extension.GetProtocol(ivkURL.Protocol).Export(invoker)
- e.service.SetMetadataServiceURL(ivkURL)
+ e.service.(*DefaultMetadataService).SetMetadataServiceURL(ivkURL)
logger.Infof("[Metadata Service] The MetadataService exports urls : %v ", ivkURL)
return nil
}
@@ -199,6 +195,6 @@
}
// UnExport will unExport the metadataService
-func (e *ServiceExporter) UnExport() {
+func (e *serviceExporter) UnExport() {
e.protocolExporter.UnExport()
}
diff --git a/metadata/metadata_service_test.go b/metadata/metadata_service_test.go
new file mode 100644
index 0000000..0939955
--- /dev/null
+++ b/metadata/metadata_service_test.go
@@ -0,0 +1,377 @@
+/*
+ * 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 metadata
+
+import (
+ "strconv"
+ "testing"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/common"
+ "dubbo.apache.org/dubbo-go/v3/common/constant"
+ "dubbo.apache.org/dubbo-go/v3/common/extension"
+ "dubbo.apache.org/dubbo-go/v3/metadata/info"
+ "dubbo.apache.org/dubbo-go/v3/protocol"
+ _ "dubbo.apache.org/dubbo-go/v3/proxy/proxy_factory"
+)
+
+var (
+ url, _ = common.NewURL("dubbo://127.0.0.1:20000?application=foo&category=providers&check=false&dubbo=dubbo-go+v1.5.0&interface=com.foo.Bar&methods=GetPetByID%2CGetPetTypes&organization=Apache&owner=foo&revision=1.0.0&side=provider&version=1.0.0")
+)
+
+func newMetadataMap() map[string]*info.MetadataInfo {
+ metadataInfo := info.NewAppMetadataInfo("dubbo-app")
+ metadataInfo.Revision = "1"
+ metadataInfo.AddService(url)
+ metadataInfo.AddSubscribeURL(url)
+ registryMetadataInfo["default"] = metadataInfo
+ return map[string]*info.MetadataInfo{
+ "default": metadataInfo,
+ }
+}
+
+func TestDefaultMetadataServiceGetExportedServiceURLs(t *testing.T) {
+ mts := &DefaultMetadataService{
+ metadataMap: newMetadataMap(),
+ }
+ got, err := mts.GetExportedServiceURLs()
+ assert.Nil(t, err)
+ assert.True(t, len(got) == 1)
+ assert.Equal(t, url, got[0])
+}
+
+func TestDefaultMetadataServiceGetExportedURLs(t *testing.T) {
+ type args struct {
+ serviceInterface string
+ group string
+ version string
+ protocol string
+ }
+ tests := []struct {
+ name string
+ args args
+ want []*common.URL
+ }{
+ {
+ name: "all exact",
+ args: args{
+ serviceInterface: url.Interface(),
+ group: url.Group(),
+ version: url.Version(),
+ protocol: url.Protocol,
+ },
+ want: []*common.URL{url},
+ },
+ {
+ name: "interface *",
+ args: args{
+ serviceInterface: "*",
+ group: url.Group(),
+ version: url.Version(),
+ protocol: url.Protocol,
+ },
+ want: []*common.URL{url},
+ },
+ {
+ name: "group *",
+ args: args{
+ serviceInterface: url.Interface(),
+ group: "*",
+ version: url.Version(),
+ protocol: url.Protocol,
+ },
+ want: []*common.URL{url},
+ },
+ {
+ name: "version *",
+ args: args{
+ serviceInterface: url.Interface(),
+ group: url.Group(),
+ version: "*",
+ protocol: url.Protocol,
+ },
+ want: []*common.URL{url},
+ },
+ {
+ name: "protocol *",
+ args: args{
+ serviceInterface: url.Interface(),
+ group: url.Group(),
+ version: url.Version(),
+ protocol: "*",
+ },
+ want: []*common.URL{url},
+ },
+ {
+ name: "all *",
+ args: args{
+ serviceInterface: "*",
+ group: "*",
+ version: "*",
+ protocol: "*",
+ },
+ want: []*common.URL{url},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ mts := &DefaultMetadataService{
+ metadataMap: newMetadataMap(),
+ }
+ got, err := mts.GetExportedURLs(tt.args.serviceInterface, tt.args.group, tt.args.version, tt.args.protocol)
+ assert.Nil(t, err)
+ assert.Equalf(t, tt.want, got, "GetExportedURLs(%v, %v, %v, %v)", tt.args.serviceInterface, tt.args.group, tt.args.version, tt.args.protocol)
+ })
+ }
+}
+
+func TestDefaultMetadataServiceGetMetadataInfo(t *testing.T) {
+ type args struct {
+ revision string
+ }
+ tests := []struct {
+ name string
+ args args
+ want *info.MetadataInfo
+ }{
+ {
+ name: "normal",
+ args: args{
+ revision: "1",
+ },
+ want: newMetadataMap()["default"],
+ },
+ {
+ name: "empty revision",
+ args: args{
+ revision: "",
+ },
+ want: nil,
+ },
+ {
+ name: "revision not match",
+ args: args{
+ revision: "2",
+ },
+ want: nil,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ mts := &DefaultMetadataService{
+ metadataMap: newMetadataMap(),
+ }
+ got, err := mts.GetMetadataInfo(tt.args.revision)
+ assert.Nil(t, err)
+ assert.Equalf(t, tt.want, got, "GetMetadataInfo(%v)", tt.args.revision)
+ })
+ }
+}
+
+func TestDefaultMetadataServiceGetMetadataServiceURL(t *testing.T) {
+ type fields struct {
+ metadataUrl *common.URL
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want *common.URL
+ }{
+ {
+ name: "normal",
+ fields: fields{
+ metadataUrl: url,
+ },
+ want: url,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ mts := &DefaultMetadataService{
+ metadataUrl: tt.fields.metadataUrl,
+ }
+ got, err := mts.GetMetadataServiceURL()
+ assert.Nil(t, err)
+ assert.Equalf(t, tt.want, got, "GetMetadataServiceURL()")
+ })
+ }
+}
+
+func TestDefaultMetadataServiceGetSubscribedURLs(t *testing.T) {
+ tests := []struct {
+ name string
+ want []*common.URL
+ }{
+ {
+ name: "normal",
+ want: []*common.URL{url},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ mts := &DefaultMetadataService{
+ metadataMap: newMetadataMap(),
+ }
+ got, err := mts.GetSubscribedURLs()
+ assert.Nil(t, err)
+ assert.Equalf(t, tt.want, got, "GetSubscribedURLs()")
+ })
+ }
+}
+
+func TestDefaultMetadataServiceMethodMapper(t *testing.T) {
+ tests := []struct {
+ name string
+ want map[string]string
+ }{
+ {
+ name: "normal",
+ want: map[string]string{
+ "GetExportedURLs": "getExportedURLs",
+ "GetMetadataInfo": "getMetadataInfo",
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ mts := &DefaultMetadataService{
+ metadataMap: newMetadataMap(),
+ }
+ assert.Equalf(t, tt.want, mts.MethodMapper(), "MethodMapper()")
+ })
+ }
+}
+
+func TestDefaultMetadataServiceSetMetadataServiceURL(t *testing.T) {
+ type args struct {
+ url *common.URL
+ }
+ tests := []struct {
+ name string
+ args args
+ want *common.URL
+ }{
+ {
+ name: "normal",
+ args: args{
+ url: url,
+ },
+ want: url,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ mts := &DefaultMetadataService{
+ metadataMap: map[string]*info.MetadataInfo{},
+ }
+ mts.SetMetadataServiceURL(tt.args.url)
+ assert.Equal(t, tt.want, mts.metadataUrl)
+ })
+ }
+}
+
+func TestDefaultMetadataServiceVersion(t *testing.T) {
+ mts := &DefaultMetadataService{}
+ got, err := mts.Version()
+ assert.Nil(t, err)
+ assert.Equal(t, version, got)
+}
+
+func Test_randomPort(t *testing.T) {
+ port := randomPort()
+ assert.True(t, port != "")
+}
+
+func Test_serviceExporterExport(t *testing.T) {
+ mockExporter := new(mockExporter)
+ defer mockExporter.AssertExpectations(t)
+ mockProtocol := new(mockProtocol)
+ defer mockProtocol.AssertExpectations(t)
+ extension.SetProtocol("dubbo", func() protocol.Protocol {
+ return mockProtocol
+ })
+ t.Run("normal", func(t *testing.T) {
+ port := randomPort()
+ p, err := strconv.Atoi(port)
+ assert.Nil(t, err)
+ opts := &Options{
+ appName: "dubbo-app",
+ metadataType: constant.RemoteMetadataStorageType,
+ port: p,
+ }
+ mockProtocol.On("Export").Return(mockExporter).Once()
+ mockExporter.On("UnExport").Once()
+ e := &serviceExporter{
+ opts: opts,
+ service: &DefaultMetadataService{},
+ }
+ err = e.Export()
+ assert.Nil(t, err)
+ e.UnExport()
+ })
+ // first t.Run has called commom.ServiceMap.Register ,second will fail
+ t.Run("get methods error", func(t *testing.T) {
+ port := randomPort()
+ p, err := strconv.Atoi(port)
+ assert.Nil(t, err)
+ opts := &Options{
+ appName: "dubbo-app",
+ metadataType: constant.RemoteMetadataStorageType,
+ port: p,
+ }
+ e := &serviceExporter{
+ opts: opts,
+ service: &DefaultMetadataService{},
+ }
+ err = e.Export()
+ assert.NotNil(t, err)
+ })
+ t.Run("port == 0", func(t *testing.T) {
+ opts := &Options{
+ appName: "dubbo-app",
+ metadataType: constant.RemoteMetadataStorageType,
+ port: 0,
+ }
+ // UnRegister first otherwise will fail
+ err := common.ServiceMap.UnRegister(constant.MetadataServiceName, constant.DefaultProtocol,
+ common.ServiceKey(constant.MetadataServiceName, opts.appName, version))
+ assert.Nil(t, err)
+ mockProtocol.On("Export").Return(mockExporter).Once()
+ mockExporter.On("UnExport").Once()
+ e := &serviceExporter{
+ opts: opts,
+ service: &DefaultMetadataService{},
+ }
+ err = e.Export()
+ assert.Nil(t, err)
+ e.UnExport()
+ })
+}
+
+func Test_serviceExporterUnExport(t *testing.T) {
+ mockExporter := new(mockExporter)
+ defer mockExporter.AssertExpectations(t)
+ serviceExporter := &serviceExporter{protocolExporter: mockExporter}
+ mockExporter.On("UnExport").Once()
+ serviceExporter.UnExport()
+}
diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go
new file mode 100644
index 0000000..ae89077
--- /dev/null
+++ b/metadata/metadata_test.go
@@ -0,0 +1,141 @@
+/*
+ * 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 metadata
+
+import (
+ "reflect"
+ "testing"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/common"
+ "dubbo.apache.org/dubbo-go/v3/common/constant"
+ "dubbo.apache.org/dubbo-go/v3/metadata/info"
+)
+
+func TestAddService(t *testing.T) {
+ type args struct {
+ registryId string
+ url *common.URL
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ {
+ name: "add",
+ args: args{
+ registryId: "reg1",
+ url: common.NewURLWithOptions(
+ common.WithProtocol("dubbo"),
+ common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+ common.WithParamsValue(constant.ApplicationTagKey, "v1"),
+ ),
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ AddService(tt.args.registryId, tt.args.url)
+ assert.True(t, registryMetadataInfo[tt.args.registryId] != nil)
+ meta := registryMetadataInfo[tt.args.registryId]
+ meta.App = tt.args.url.GetParam(constant.ApplicationKey, "")
+ meta.Tag = tt.args.url.GetParam(constant.ApplicationTagKey, "")
+ assert.True(t, reflect.DeepEqual(meta.GetExportedServiceURLs()[0], tt.args.url))
+ })
+ }
+}
+
+func TestAddSubscribeURL(t *testing.T) {
+ type args struct {
+ registryId string
+ url *common.URL
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ {
+ name: "addSub",
+ args: args{
+ registryId: "reg2",
+ url: common.NewURLWithOptions(
+ common.WithProtocol("dubbo"),
+ common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+ common.WithParamsValue(constant.ApplicationTagKey, "v1"),
+ ),
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ AddSubscribeURL(tt.args.registryId, tt.args.url)
+ assert.True(t, registryMetadataInfo[tt.args.registryId] != nil)
+ meta := registryMetadataInfo[tt.args.registryId]
+ meta.App = tt.args.url.GetParam(constant.ApplicationKey, "")
+ meta.Tag = tt.args.url.GetParam(constant.ApplicationTagKey, "")
+ assert.True(t, reflect.DeepEqual(meta.GetSubscribedURLs()[0], tt.args.url))
+ })
+ }
+}
+
+func TestGetMetadataInfo(t *testing.T) {
+ type args struct {
+ registryId string
+ }
+ tests := []struct {
+ name string
+ args args
+ want *info.MetadataInfo
+ }{
+ {
+ name: "get",
+ args: args{
+ registryId: "reg3",
+ },
+ want: info.NewMetadataInfo("dubbo", "v2"),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ registryMetadataInfo[tt.args.registryId] = tt.want
+ assert.Equalf(t, tt.want, GetMetadataInfo(tt.args.registryId), "GetMetadataInfo(%v)", tt.args.registryId)
+ })
+ }
+}
+
+func TestGetMetadataService(t *testing.T) {
+ tests := []struct {
+ name string
+ want MetadataService
+ }{
+ {
+ name: "getMetadataService",
+ want: metadataService,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, GetMetadataService(), "GetMetadataService()")
+ })
+ }
+}
diff --git a/metadata/options.go b/metadata/options.go
index 30e866a..5a5eb5b 100644
--- a/metadata/options.go
+++ b/metadata/options.go
@@ -26,11 +26,13 @@
import (
"github.com/dubbogo/gost/log/logger"
+
+ perrors "github.com/pkg/errors"
)
import (
+ "dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
- "dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/global"
)
@@ -62,7 +64,7 @@
var err error
exportOnce.Do(func() {
if opts.metadataType != constant.RemoteMetadataStorageType {
- exporter := &ServiceExporter{service: metadataService, opts: opts}
+ exporter := &serviceExporter{service: metadataService, opts: opts}
defer func() {
// TODO remove this recover func,this just to avoid some unit test failed,this will not happen in user side mostly
// config test -> metadata exporter -> dubbo protocol/remoting -> config,cycle import will occur
@@ -103,18 +105,33 @@
}
func (opts *ReportOptions) Init() error {
- fac := extension.GetMetadataReportFactory(opts.Protocol)
- if fac == nil {
- logger.Errorf("no metadata report factory of protocol %s found!", opts.Protocol)
- return nil
- }
- url, err := toUrl(opts)
+ url, err := opts.toUrl()
if err != nil {
logger.Errorf("metadata report create error %v", err)
return err
}
- instances[opts.registryId] = &DelegateMetadataReport{instance: fac.CreateMetadataReport(url)}
- return nil
+ return addMetadataReport(opts.registryId, url)
+}
+
+func (opts *ReportOptions) toUrl() (*common.URL, error) {
+ res, err := common.NewURL(opts.Address,
+ common.WithUsername(opts.Username),
+ common.WithPassword(opts.Password),
+ common.WithLocation(opts.Address),
+ common.WithProtocol(opts.Protocol),
+ common.WithParamsValue(constant.TimeoutKey, opts.Timeout),
+ common.WithParamsValue(constant.MetadataReportGroupKey, opts.Group),
+ common.WithParamsValue(constant.MetadataReportNamespaceKey, opts.Namespace),
+ common.WithParamsValue(constant.ClientNameKey, strings.Join([]string{constant.MetadataReportPrefix, opts.Protocol, opts.Address}, "-")),
+ )
+ if err != nil || len(res.Protocol) == 0 {
+ return nil, perrors.New("Invalid MetadataReport Config.")
+ }
+ res.SetParam("metadata", res.Protocol)
+ for key, val := range opts.Params {
+ res.SetParam(key, val)
+ }
+ return res, nil
}
func defaultReportOptions() *ReportOptions {
diff --git a/metadata/report/report_factory.go b/metadata/report/report_factory.go
index 2c2af73..cd2cf7c 100644
--- a/metadata/report/report_factory.go
+++ b/metadata/report/report_factory.go
@@ -25,5 +25,3 @@
type MetadataReportFactory interface {
CreateMetadataReport(*common.URL) MetadataReport
}
-
-type BaseMetadataReportFactory struct{}
diff --git a/metadata/report_instance.go b/metadata/report_instance.go
index 2238144..f7bceb4 100644
--- a/metadata/report_instance.go
+++ b/metadata/report_instance.go
@@ -18,19 +18,18 @@
package metadata
import (
- "strings"
"time"
)
import (
"github.com/dubbogo/gost/container/set"
-
- perrors "github.com/pkg/errors"
+ "github.com/dubbogo/gost/log/logger"
)
import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
+ "dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/metadata/info"
"dubbo.apache.org/dubbo-go/v3/metadata/mapping"
"dubbo.apache.org/dubbo-go/v3/metadata/report"
@@ -42,25 +41,14 @@
instances = make(map[string]report.MetadataReport)
)
-func toUrl(opts *ReportOptions) (*common.URL, error) {
- res, err := common.NewURL(opts.Address,
- common.WithUsername(opts.Username),
- common.WithPassword(opts.Password),
- common.WithLocation(opts.Address),
- common.WithProtocol(opts.Protocol),
- common.WithParamsValue(constant.TimeoutKey, opts.Timeout),
- common.WithParamsValue(constant.MetadataReportGroupKey, opts.Group),
- common.WithParamsValue(constant.MetadataReportNamespaceKey, opts.Namespace),
- common.WithParamsValue(constant.ClientNameKey, strings.Join([]string{constant.MetadataReportPrefix, opts.Protocol, opts.Address}, "-")),
- )
- if err != nil || len(res.Protocol) == 0 {
- return nil, perrors.New("Invalid MetadataReport Config.")
+func addMetadataReport(registryId string, url *common.URL) error {
+ fac := extension.GetMetadataReportFactory(url.Protocol)
+ if fac == nil {
+ logger.Warnf("no metadata report factory of protocol %s found, please check if the metadata report factory is imported", url.Protocol)
+ return nil
}
- res.SetParam("metadata", res.Protocol)
- for key, val := range opts.Params {
- res.SetParam(key, val)
- }
- return res, nil
+ instances[registryId] = &DelegateMetadataReport{instance: fac.CreateMetadataReport(url)}
+ return nil
}
func GetMetadataReport() report.MetadataReport {
@@ -91,6 +79,9 @@
}
func GetMetadataType() string {
+ if metadataOptions == nil || metadataOptions.metadataType == "" {
+ return constant.DefaultMetadataStorageType
+ }
return metadataOptions.metadataType
}
diff --git a/metadata/report_instance_test.go b/metadata/report_instance_test.go
new file mode 100644
index 0000000..4b89cde
--- /dev/null
+++ b/metadata/report_instance_test.go
@@ -0,0 +1,264 @@
+/*
+ * 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 metadata
+
+import (
+ "reflect"
+ "testing"
+)
+
+import (
+ gxset "github.com/dubbogo/gost/container/set"
+ "github.com/dubbogo/gost/gof/observer"
+
+ "github.com/pkg/errors"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/common"
+ "dubbo.apache.org/dubbo-go/v3/common/constant"
+ "dubbo.apache.org/dubbo-go/v3/common/extension"
+ "dubbo.apache.org/dubbo-go/v3/metadata/info"
+ "dubbo.apache.org/dubbo-go/v3/metadata/mapping"
+ "dubbo.apache.org/dubbo-go/v3/metadata/report"
+ "dubbo.apache.org/dubbo-go/v3/metrics"
+ metricsMetadata "dubbo.apache.org/dubbo-go/v3/metrics/metadata"
+)
+
+func TestDelegateMetadataReportGetAppMetadata(t *testing.T) {
+ mockReport := new(mockMetadataReport)
+ defer mockReport.AssertExpectations(t)
+ delegate := &DelegateMetadataReport{instance: mockReport}
+ metadataInfo := info.NewAppMetadataInfo("dubbo")
+ var ch = make(chan metrics.MetricsEvent, 10)
+ metrics.Subscribe(constant.MetricsMetadata, ch)
+ defer close(ch)
+ t.Run("normal", func(t *testing.T) {
+ mockReport.On("GetAppMetadata").Return(metadataInfo, nil).Once()
+ got, err := delegate.GetAppMetadata("dubbo", "1")
+ assert.Nil(t, err)
+ if !reflect.DeepEqual(got, metadataInfo) {
+ t.Errorf("GetAppMetadata() got = %v, want %v", got, metadataInfo)
+ }
+ assert.True(t, len(ch) == 1)
+ metricEvent := <-ch
+ assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+ event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+ assert.True(t, ok)
+ assert.NotNil(t, event.Name, metricsMetadata.MetadataSub)
+ assert.NotNil(t, event.Start)
+ assert.NotNil(t, event.End)
+ assert.True(t, event.Succ)
+ })
+ t.Run("error", func(t *testing.T) {
+ mockReport.On("GetAppMetadata").Return(info.NewAppMetadataInfo("dubbo"), errors.New("mock error")).Once()
+ _, err := delegate.GetAppMetadata("dubbo", "1111")
+ assert.NotNil(t, err)
+ assert.True(t, len(ch) == 1)
+ metricEvent := <-ch
+ assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+ event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+ assert.True(t, ok)
+ assert.NotNil(t, event.Name, metricsMetadata.MetadataSub)
+ assert.NotNil(t, event.Start)
+ assert.NotNil(t, event.End)
+ assert.True(t, !event.Succ)
+ })
+}
+
+func TestDelegateMetadataReportPublishAppMetadata(t *testing.T) {
+ mockReport := new(mockMetadataReport)
+ defer mockReport.AssertExpectations(t)
+ delegate := &DelegateMetadataReport{instance: mockReport}
+ metadataInfo := info.NewAppMetadataInfo("dubbo")
+ var ch = make(chan metrics.MetricsEvent, 10)
+ metrics.Subscribe(constant.MetricsMetadata, ch)
+ defer close(ch)
+ t.Run("normal", func(t *testing.T) {
+ mockReport.On("PublishAppMetadata").Return(nil).Once()
+ err := delegate.PublishAppMetadata("application", "revision", metadataInfo)
+ assert.Nil(t, err)
+ assert.True(t, len(ch) == 1)
+ metricEvent := <-ch
+ assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+ event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+ assert.True(t, ok)
+ assert.NotNil(t, event.Name, metricsMetadata.MetadataPush)
+ assert.NotNil(t, event.Start)
+ assert.NotNil(t, event.End)
+ assert.True(t, event.Succ)
+ })
+ t.Run("error", func(t *testing.T) {
+ mockReport.On("PublishAppMetadata").Return(errors.New("mock error")).Once()
+ err := delegate.PublishAppMetadata("application", "revision", metadataInfo)
+ assert.NotNil(t, err)
+ assert.True(t, len(ch) == 1)
+ metricEvent := <-ch
+ assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+ event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+ assert.True(t, ok)
+ assert.NotNil(t, event.Name, metricsMetadata.MetadataPush)
+ assert.NotNil(t, event.Start)
+ assert.NotNil(t, event.End)
+ assert.True(t, !event.Succ)
+ })
+}
+
+func TestDelegateMetadataReportGetServiceAppMapping(t *testing.T) {
+ mockReport := new(mockMetadataReport)
+ defer mockReport.AssertExpectations(t)
+ delegate := &DelegateMetadataReport{instance: mockReport}
+ t.Run("normal", func(t *testing.T) {
+ mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), nil).Once()
+ got, err := delegate.GetServiceAppMapping("dubbo", "dev", &listener{})
+ assert.Nil(t, err)
+ assert.True(t, got.Empty())
+ })
+ t.Run("error", func(t *testing.T) {
+ mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), errors.New("mock error")).Once()
+ _, err := delegate.GetServiceAppMapping("dubbo", "dev", &listener{})
+ assert.NotNil(t, err)
+ })
+}
+
+func TestDelegateMetadataReportRegisterServiceAppMapping(t *testing.T) {
+ mockReport := new(mockMetadataReport)
+ defer mockReport.AssertExpectations(t)
+ delegate := &DelegateMetadataReport{instance: mockReport}
+ t.Run("normal", func(t *testing.T) {
+ mockReport.On("RegisterServiceAppMapping").Return(nil).Once()
+ err := delegate.RegisterServiceAppMapping("interfaceName", "group", "application")
+ assert.Nil(t, err)
+ })
+ t.Run("error", func(t *testing.T) {
+ mockReport.On("RegisterServiceAppMapping").Return(errors.New("mock error")).Once()
+ err := delegate.RegisterServiceAppMapping("interfaceName", "group", "application")
+ assert.NotNil(t, err)
+ })
+}
+
+func TestDelegateMetadataReportRemoveServiceAppMappingListener(t *testing.T) {
+ mockReport := new(mockMetadataReport)
+ defer mockReport.AssertExpectations(t)
+ delegate := &DelegateMetadataReport{instance: mockReport}
+ t.Run("normal", func(t *testing.T) {
+ mockReport.On("RemoveServiceAppMappingListener").Return(nil).Once()
+ err := delegate.RemoveServiceAppMappingListener("interfaceName", "group")
+ assert.Nil(t, err)
+ })
+ t.Run("error", func(t *testing.T) {
+ mockReport.On("RemoveServiceAppMappingListener").Return(errors.New("mock error")).Once()
+ err := delegate.RemoveServiceAppMappingListener("interfaceName", "group")
+ assert.NotNil(t, err)
+ })
+}
+
+func TestGetMetadataReport(t *testing.T) {
+ instances = make(map[string]report.MetadataReport)
+ assert.Nil(t, GetMetadataReport())
+ instances["default"] = new(mockMetadataReport)
+ assert.NotNil(t, GetMetadataReport())
+}
+
+func TestGetMetadataReportByRegistry(t *testing.T) {
+ instances = make(map[string]report.MetadataReport)
+ assert.Nil(t, GetMetadataReportByRegistry("reg"))
+ instances["default"] = new(mockMetadataReport)
+ assert.NotNil(t, GetMetadataReportByRegistry("default"))
+ assert.NotNil(t, GetMetadataReportByRegistry("reg"))
+ assert.NotNil(t, GetMetadataReportByRegistry(""))
+}
+
+func TestGetMetadataReports(t *testing.T) {
+ instances = make(map[string]report.MetadataReport)
+ assert.True(t, len(GetMetadataReports()) == 0)
+ instances["default"] = new(mockMetadataReport)
+ assert.True(t, len(GetMetadataReports()) == 1)
+}
+
+func TestGetMetadataType(t *testing.T) {
+ assert.Equal(t, GetMetadataType(), constant.DefaultMetadataStorageType)
+ metadataOptions = &Options{}
+ assert.Equal(t, GetMetadataType(), constant.DefaultMetadataStorageType)
+ metadataOptions = &Options{
+ metadataType: constant.RemoteMetadataStorageType,
+ }
+ assert.Equal(t, GetMetadataType(), constant.RemoteMetadataStorageType)
+}
+
+func TestAddMetadataReport(t *testing.T) {
+ url := common.NewURLWithOptions(
+ common.WithProtocol("registryId"),
+ )
+ err := addMetadataReport("registryId", url)
+ assert.Nil(t, err)
+ assert.True(t, instances["registryId"] == nil)
+ mockReport := new(mockMetadataReport)
+ extension.SetMetadataReportFactory("registryId", func() report.MetadataReportFactory {
+ return mockReport
+ })
+ err = addMetadataReport("registryId", url)
+ assert.Nil(t, err)
+ assert.True(t, instances["registryId"] != nil)
+}
+
+type mockMetadataReport struct {
+ mock.Mock
+}
+
+func (m *mockMetadataReport) CreateMetadataReport(*common.URL) report.MetadataReport {
+ return m
+}
+
+func (m *mockMetadataReport) GetAppMetadata(string, string) (*info.MetadataInfo, error) {
+ args := m.Called()
+ return args.Get(0).(*info.MetadataInfo), args.Error(1)
+}
+
+func (m *mockMetadataReport) PublishAppMetadata(string, string, *info.MetadataInfo) error {
+ args := m.Called()
+ return args.Error(0)
+}
+
+func (m *mockMetadataReport) RegisterServiceAppMapping(string, string, string) error {
+ args := m.Called()
+ return args.Error(0)
+}
+
+func (m *mockMetadataReport) GetServiceAppMapping(string, string, mapping.MappingListener) (*gxset.HashSet, error) {
+ args := m.Called()
+ return args.Get(0).(*gxset.HashSet), args.Error(1)
+}
+
+func (m *mockMetadataReport) RemoveServiceAppMappingListener(string, string) error {
+ args := m.Called()
+ return args.Error(0)
+}
+
+type listener struct {
+}
+
+func (l *listener) OnEvent(e observer.Event) error {
+ return nil
+}
+
+func (l *listener) Stop() {
+}
diff --git a/options.go b/options.go
index b4bff1d..5fd162d 100644
--- a/options.go
+++ b/options.go
@@ -94,7 +94,6 @@
log.Infof("[Config Center] Config center doesn't start")
log.Debugf("config center doesn't start because %s", err)
} else {
- compatInstanceOptions(rcCompat, rc)
if err = rcCompat.Logger.Init(); err != nil { // init logger using config from config center again
return err
}
@@ -110,15 +109,8 @@
}
// init protocol
- protocols := rcCompat.Protocols
- if len(protocols) <= 0 {
- protocol := &config.ProtocolConfig{}
- protocols = make(map[string]*config.ProtocolConfig, 1)
- protocols[constant.Dubbo] = protocol
- rcCompat.Protocols = protocols
- }
- for _, protocol := range protocols {
- if err := protocol.Init(); err != nil {
+ for _, protocolConfig := range rcCompat.Protocols {
+ if err := protocolConfig.Init(); err != nil {
return err
}
}
@@ -169,6 +161,7 @@
return err
}
+ compatInstanceOptions(rcCompat, rc) // overrider options config because some config are changed after init
return nil
}
diff --git a/registry/servicediscovery/customizer/service_instance_host_port_customizer.go b/registry/servicediscovery/customizer/service_instance_host_port_customizer.go
index f33023f..bd918d6 100644
--- a/registry/servicediscovery/customizer/service_instance_host_port_customizer.go
+++ b/registry/servicediscovery/customizer/service_instance_host_port_customizer.go
@@ -39,7 +39,7 @@
// Customize calculate the revision for exported urls and then put it into instance metadata
func (e *hostPortCustomizer) Customize(instance registry.ServiceInstance) {
- if instance.GetPort() > 0 {
+ if instance.GetPort() > 0 { // has set, avoid reset
return
}
if instance.GetServiceMetadata() == nil || len(instance.GetServiceMetadata().GetExportedServiceURLs()) == 0 {