Skip to content
Snippets Groups Projects
Commit 0048609d authored by vito.he's avatar vito.he
Browse files

Mod:resolve pr review

parent 97882444
No related branches found
No related tags found
No related merge requests found
...@@ -74,3 +74,7 @@ const ( ...@@ -74,3 +74,7 @@ const (
const ( const (
COMMA_SPLIT_PATTERN = "\\s*[,]+\\s*" COMMA_SPLIT_PATTERN = "\\s*[,]+\\s*"
) )
const (
SIMPLE_METADATA_SERVICE_NAME = "MetadataService"
)
...@@ -33,20 +33,20 @@ var ( ...@@ -33,20 +33,20 @@ var (
once sync.Once once sync.Once
) )
// InitMetadataReportInstance will create the metadata report instance by the specified metadata report url // GetMetadataReportInstance will return the instance in lazy mode. Be careful the instance create will only
func InitMetadataReportInstance(url *common.URL) report.MetadataReport { // execute once.
func GetMetadataReportInstance(selectiveUrl ...*common.URL) report.MetadataReport {
once.Do(func() { once.Do(func() {
instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url) var url *common.URL
reportUrl = *url if len(selectiveUrl) > 0 {
url = selectiveUrl[0]
instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url)
reportUrl = *url
}
}) })
return instance return instance
} }
// GetMetadataReportInstance will return the instance
func GetMetadataReportInstance() report.MetadataReport {
return instance
}
// GetMetadataReportUrl will return the report instance url // GetMetadataReportUrl will return the report instance url
func GetMetadataReportUrl() common.URL { func GetMetadataReportUrl() common.URL {
return reportUrl return reportUrl
......
...@@ -101,7 +101,7 @@ func startMetadataReport(metadataType string, metadataReportConfig *MetadataRepo ...@@ -101,7 +101,7 @@ func startMetadataReport(metadataType string, metadataReportConfig *MetadataRepo
} }
if url, err := metadataReportConfig.ToUrl(); err == nil { if url, err := metadataReportConfig.ToUrl(); err == nil {
instance.InitMetadataReportInstance(url) instance.GetMetadataReportInstance(url)
} else { } else {
return perrors.New("MetadataConfig is invalid!") return perrors.New("MetadataConfig is invalid!")
} }
......
...@@ -14,9 +14,7 @@ require ( ...@@ -14,9 +14,7 @@ require (
github.com/dubbogo/go-zookeeper v1.0.0 github.com/dubbogo/go-zookeeper v1.0.0
github.com/dubbogo/gost v1.9.0 github.com/dubbogo/gost v1.9.0
github.com/emicklei/go-restful/v3 v3.0.0 github.com/emicklei/go-restful/v3 v3.0.0
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/go-co-op/gocron v0.1.1 github.com/go-co-op/gocron v0.1.1
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-resty/resty/v2 v2.1.0 github.com/go-resty/resty/v2 v2.1.0
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
github.com/golang/mock v1.3.1 github.com/golang/mock v1.3.1
......
...@@ -35,11 +35,8 @@ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vaj ...@@ -35,11 +35,8 @@ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vaj
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e h1:MSuLXx/mveDbpDNhVrcWTMeV4lbYWKcyO4rH+jAxmX0=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/apache/dubbo-go-hessian2 v1.5.0 h1:fzulDG5G7nX0ccgKdiN9XipJ7tZ4WXKgmk4stdlDS6s= github.com/apache/dubbo-go-hessian2 v1.5.0 h1:fzulDG5G7nX0ccgKdiN9XipJ7tZ4WXKgmk4stdlDS6s=
github.com/apache/dubbo-go-hessian2 v1.5.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= github.com/apache/dubbo-go-hessian2 v1.5.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
...@@ -53,7 +50,6 @@ github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f h1:/8NcnxL6 ...@@ -53,7 +50,6 @@ github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f h1:/8NcnxL6
github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.15.24 h1:xLAdTA/ore6xdPAljzZRed7IGqQgC+nY+ERS5vaj4Ro= github.com/aws/aws-sdk-go v1.15.24 h1:xLAdTA/ore6xdPAljzZRed7IGqQgC+nY+ERS5vaj4Ro=
github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
...@@ -392,8 +388,6 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 ...@@ -392,8 +388,6 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c h1:WoCa3AvgQMVKNs+RIFlWPRgY9QVJwUxJDrGxHs0fcRo=
github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo=
github.com/nacos-group/nacos-sdk-go v0.3.1 h1:MI7bNDAN5m9UFcRRUTSPfJi4dCQo+TYG85qVB1rCHeg= github.com/nacos-group/nacos-sdk-go v0.3.1 h1:MI7bNDAN5m9UFcRRUTSPfJi4dCQo+TYG85qVB1rCHeg=
github.com/nacos-group/nacos-sdk-go v0.3.1/go.mod h1:ESKb6yF0gxSc8GuS+0jaMBe+n8rJ5/k4ya6LyFG2xi8= github.com/nacos-group/nacos-sdk-go v0.3.1/go.mod h1:ESKb6yF0gxSc8GuS+0jaMBe+n8rJ5/k4ya6LyFG2xi8=
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s=
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
) )
import ( import (
...@@ -43,29 +44,26 @@ type ServiceDefinition struct { ...@@ -43,29 +44,26 @@ type ServiceDefinition struct {
func (def ServiceDefinition) ToBytes() ([]byte, error) { func (def ServiceDefinition) ToBytes() ([]byte, error) {
return json.Marshal(def) return json.Marshal(def)
} }
func (def ServiceDefinition) String() string { func (def ServiceDefinition) String() string {
var methodStr string var methodStr strings.Builder
for _, m := range def.Methods { for _, m := range def.Methods {
var paramType string var paramType strings.Builder
for _, p := range m.ParameterTypes { for _, p := range m.ParameterTypes {
paramType = paramType + fmt.Sprintf("{type:%v}", p) paramType.WriteString(fmt.Sprintf("{type:%v}", p))
} }
var param string var param strings.Builder
for _, d := range m.Parameters { for _, d := range m.Parameters {
param = param + fmt.Sprintf("{id:%v,type:%v,builderName:%v}", d.Id, d.Type, d.TypeBuilderName) param.WriteString(fmt.Sprintf("{id:%v,type:%v,builderName:%v}", d.Id, d.Type, d.TypeBuilderName))
} }
methodStr = methodStr + fmt.Sprintf("{name:%v,parameterTypes:[%v],returnType:%v,params:[%v] }", m.Name, paramType, m.ReturnType, param) methodStr.WriteString(fmt.Sprintf("{name:%v,parameterTypes:[%v],returnType:%v,params:[%v] }", m.Name, paramType.String(), m.ReturnType, param.String()))
} }
var types string var types strings.Builder
for _, d := range def.Types { for _, d := range def.Types {
types = types + fmt.Sprintf("{id:%v,type:%v,builderName:%v}", d.Id, d.Type, d.TypeBuilderName) types.WriteString(fmt.Sprintf("{id:%v,type:%v,builderName:%v}", d.Id, d.Type, d.TypeBuilderName))
} }
return fmt.Sprintf("{canonicalName:%v, codeSource:%v, methods:[%v], types:[%v]}", def.CanonicalName, def.CodeSource, methodStr.String(), types.String())
return fmt.Sprintf("{canonicalName:%v, codeSource:%v, methods:[%v], types:[%v]}", def.CanonicalName, def.CodeSource, methodStr, types)
} }
// FullServiceDefinition is the describer of service definition with parameters // FullServiceDefinition is the describer of service definition with parameters
......
...@@ -202,7 +202,6 @@ func (bmr *MetadataReport) storeMetadataTask(role int, identifier *identifier.Me ...@@ -202,7 +202,6 @@ func (bmr *MetadataReport) storeMetadataTask(role int, identifier *identifier.Me
logger.Errorf("storeProviderMetadataTask error in stage call metadata report to StoreProviderMetadata, msg is %v", err) logger.Errorf("storeProviderMetadataTask error in stage call metadata report to StoreProviderMetadata, msg is %v", err)
panic(err) panic(err)
} }
} }
// StoreConsumerMetadata will delegate to call remote metadata's sdk to store consumer side service definition // StoreConsumerMetadata will delegate to call remote metadata's sdk to store consumer side service definition
......
...@@ -25,6 +25,7 @@ import ( ...@@ -25,6 +25,7 @@ import (
import ( import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/atomic"
) )
import ( import (
...@@ -37,10 +38,10 @@ import ( ...@@ -37,10 +38,10 @@ import (
) )
func TestMetadataReport_MetadataReportRetry(t *testing.T) { func TestMetadataReport_MetadataReportRetry(t *testing.T) {
counter := 1 counter := atomic.NewInt64(1)
retry, err := newMetadataReportRetry(1, 10, func() bool { retry, err := newMetadataReportRetry(1, 10, func() bool {
counter++ counter.Add(1)
return true return true
}) })
assert.NoError(t, err) assert.NoError(t, err)
...@@ -49,16 +50,16 @@ func TestMetadataReport_MetadataReportRetry(t *testing.T) { ...@@ -49,16 +50,16 @@ func TestMetadataReport_MetadataReportRetry(t *testing.T) {
select { select {
case <-itsTime: case <-itsTime:
retry.scheduler.Clear() retry.scheduler.Clear()
assert.Equal(t, counter, 3) assert.Equal(t, counter.Load(), int64(3))
logger.Info("over") logger.Info("over")
} }
} }
func TestMetadataReport_MetadataReportRetryWithLimit(t *testing.T) { func TestMetadataReport_MetadataReportRetryWithLimit(t *testing.T) {
counter := 1 counter := atomic.NewInt64(1)
retry, err := newMetadataReportRetry(1, 1, func() bool { retry, err := newMetadataReportRetry(1, 1, func() bool {
counter++ counter.Add(1)
return true return true
}) })
assert.NoError(t, err) assert.NoError(t, err)
...@@ -67,10 +68,9 @@ func TestMetadataReport_MetadataReportRetryWithLimit(t *testing.T) { ...@@ -67,10 +68,9 @@ func TestMetadataReport_MetadataReportRetryWithLimit(t *testing.T) {
select { select {
case <-itsTime: case <-itsTime:
retry.scheduler.Clear() retry.scheduler.Clear()
assert.Equal(t, counter, 2) assert.Equal(t, counter.Load(), int64(2))
logger.Info("over") logger.Info("over")
} }
} }
func mockNewMetadataReport(t *testing.T) *MetadataReport { func mockNewMetadataReport(t *testing.T) *MetadataReport {
......
...@@ -49,7 +49,7 @@ func NewMetadataServiceExporter(metadataService service.MetadataService) exporte ...@@ -49,7 +49,7 @@ func NewMetadataServiceExporter(metadataService service.MetadataService) exporte
func (exporter *MetadataServiceExporter) Export() error { func (exporter *MetadataServiceExporter) Export() error {
if !exporter.IsExported() { if !exporter.IsExported() {
serviceConfig := config.NewServiceConfig("MetadataService", context.Background()) serviceConfig := config.NewServiceConfig(constant.SIMPLE_METADATA_SERVICE_NAME, context.Background())
serviceConfig.Protocol = constant.DEFAULT_PROTOCOL serviceConfig.Protocol = constant.DEFAULT_PROTOCOL
serviceConfig.Protocols = map[string]*config.ProtocolConfig{ serviceConfig.Protocols = map[string]*config.ProtocolConfig{
constant.DEFAULT_PROTOCOL: generateMetadataProtocol(), constant.DEFAULT_PROTOCOL: generateMetadataProtocol(),
......
...@@ -33,6 +33,9 @@ import ( ...@@ -33,6 +33,9 @@ import (
"github.com/apache/dubbo-go/metadata/service" "github.com/apache/dubbo-go/metadata/service"
) )
// version will be used by Version func
const version = "1.0.0"
// MetadataService is store and query the metadata info in memory when each service registry // MetadataService is store and query the metadata info in memory when each service registry
type MetadataService struct { type MetadataService struct {
service.BaseMetadataService service.BaseMetadataService
...@@ -118,7 +121,7 @@ func (mts *MetadataService) getAllService(services *sync.Map) *skip.SkipList { ...@@ -118,7 +121,7 @@ func (mts *MetadataService) getAllService(services *sync.Map) *skip.SkipList {
urls := value.(*skip.SkipList) urls := value.(*skip.SkipList)
for i := uint64(0); i < urls.Len(); i++ { for i := uint64(0); i < urls.Len(); i++ {
url := common.URL(urls.ByPosition(i).(Comparator)) url := common.URL(urls.ByPosition(i).(Comparator))
if url.GetParam(constant.INTERFACE_KEY, url.Path) != "MetadataService" { if url.GetParam(constant.INTERFACE_KEY, url.Path) != constant.SIMPLE_METADATA_SERVICE_NAME {
skipList.Insert(Comparator(url)) skipList.Insert(Comparator(url))
} }
} }
...@@ -227,5 +230,5 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR ...@@ -227,5 +230,5 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR
// Version will return the version of metadata service // Version will return the version of metadata service
func (mts *MetadataService) Version() string { func (mts *MetadataService) Version() string {
return "1.0.0" return version
} }
...@@ -34,6 +34,9 @@ import ( ...@@ -34,6 +34,9 @@ import (
"github.com/apache/dubbo-go/metadata/service/inmemory" "github.com/apache/dubbo-go/metadata/service/inmemory"
) )
// version will be used by Version func
const version = "1.0.0"
// MetadataService is a implement of metadata service which will delegate the remote metadata report // MetadataService is a implement of metadata service which will delegate the remote metadata report
type MetadataService struct { type MetadataService struct {
service.BaseMetadataService service.BaseMetadataService
...@@ -145,7 +148,6 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR ...@@ -145,7 +148,6 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR
logger.Errorf("Error occur when execute remote.MetadataService.RefreshMetadata, error message is %v", err) logger.Errorf("Error occur when execute remote.MetadataService.RefreshMetadata, error message is %v", err)
result = false result = false
} }
} }
} }
...@@ -174,7 +176,7 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR ...@@ -174,7 +176,7 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR
// Version will return the remote service version // Version will return the remote service version
func (MetadataService) Version() string { func (MetadataService) Version() string {
return "1.0.0" return version
} }
// convertUrls will convert the skip list to slice // convertUrls will convert the skip list to slice
......
...@@ -96,7 +96,7 @@ func TestMetadataService(t *testing.T) { ...@@ -96,7 +96,7 @@ func TestMetadataService(t *testing.T) {
u, err := common.NewURL(fmt.Sprintf( u, err := common.NewURL(fmt.Sprintf(
"mock://127.0.0.1:20000/?sync.report=true")) "mock://127.0.0.1:20000/?sync.report=true"))
assert.NoError(t, err) assert.NoError(t, err)
instance.InitMetadataReportInstance(&u) instance.GetMetadataReportInstance(&u)
mts, err := NewMetadataService() mts, err := NewMetadataService()
assert.NoError(t, err) assert.NoError(t, err)
mts.setInMemoryMetadataService(mockInmemoryProc(t)) mts.setInMemoryMetadataService(mockInmemoryProc(t))
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
import ( import (
"github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/config"
) )
...@@ -66,5 +67,5 @@ func (mts *BaseMetadataService) ServiceName() (string, error) { ...@@ -66,5 +67,5 @@ func (mts *BaseMetadataService) ServiceName() (string, error) {
// Version will return the version of metadata service // Version will return the version of metadata service
func (mts *BaseMetadataService) Reference() string { func (mts *BaseMetadataService) Reference() string {
return "MetadataService" return constant.SIMPLE_METADATA_SERVICE_NAME
} }
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment