This is an automated email from the ASF dual-hosted git repository.

tianxiaoliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new e8d5139  【SCB-2094】Fix: open Mongo's pkg, server and syncer test cases 
(#809)
e8d5139 is described below

commit e8d5139c3749d39c78cd5d46e94b9781d6268f09
Author: robotLJW <[email protected]>
AuthorDate: Thu Jan 14 14:09:17 2021 +0800

    【SCB-2094】Fix: open Mongo's pkg, server and syncer test cases (#809)
---
 datasource/etcd/account.go                         |   2 +-
 datasource/etcd/ms.go                              |   2 +-
 datasource/etcd/role.go                            |  10 +-
 datasource/mongo/account.go                        |  34 ++-
 datasource/mongo/database.go                       |  84 +++---
 datasource/mongo/dep.go                            |  45 ++-
 datasource/mongo/dependency_query.go               |   2 +-
 .../mongo/heartbeat/heartbeatchecker/heartbeat.go  |   5 +-
 .../heartbeat/heartbeatchecker/heartbeat_test.go   |   6 +-
 .../heartbeat/heartbeatchecker/heartbeatchecker.go |   2 +-
 datasource/mongo/ms.go                             | 315 ++++++++++++++-------
 datasource/mongo/ms_test.go                        |  16 +-
 datasource/mongo/role.go                           |  96 ++++++-
 datasource/mongo/role_test.go                      |  90 ++++++
 datasource/mongo/util.go                           |  14 +
 go.mod                                             |   2 +-
 scripts/ut_test_in_docker.sh                       |   6 +-
 server/service/rbac/decision_test.go               |  31 +-
 server/service/rbac/rbac_test.go                   |   5 +-
 19 files changed, 556 insertions(+), 211 deletions(-)

diff --git a/datasource/etcd/account.go b/datasource/etcd/account.go
index 6eaf548..897196c 100644
--- a/datasource/etcd/account.go
+++ b/datasource/etcd/account.go
@@ -125,7 +125,7 @@ func (ds *DataSource) DeleteAccount(ctx context.Context, 
key string) (bool, erro
        if err != nil {
                return false, err
        }
-       return resp.Count != 0, nil
+       return resp.Succeeded, nil
 }
 func (ds *DataSource) UpdateAccount(ctx context.Context, key string, account 
*rbacframe.Account) error {
        value, err := json.Marshal(account)
diff --git a/datasource/etcd/ms.go b/datasource/etcd/ms.go
index 3e650c7..ed9981a 100644
--- a/datasource/etcd/ms.go
+++ b/datasource/etcd/ms.go
@@ -883,7 +883,7 @@ func (ds *DataSource) FindInstances(ctx context.Context, 
request *pb.FindInstanc
                Environment: request.Environment,
                AppId:       request.AppId,
                ServiceName: request.ServiceName,
-               Alias:       request.ServiceName,
+               Alias:       request.Alias,
                Version:     request.VersionRule,
        }
 
diff --git a/datasource/etcd/role.go b/datasource/etcd/role.go
index ac0dd6e..e34f0ff 100644
--- a/datasource/etcd/role.go
+++ b/datasource/etcd/role.go
@@ -39,13 +39,13 @@ func (ds *DataSource) CreateRole(ctx context.Context, r 
*rbacframe.Role) error {
        defer func() {
                err := lock.Unlock()
                if err != nil {
-                       log.Errorf(err, "can not release role lock")
+                       log.Error("can not release role lock", err)
                }
        }()
        key := path.GenerateRBACRoleKey(r.Name)
        exist, err := datasource.Instance().RoleExist(ctx, r.Name)
        if err != nil {
-               log.Errorf(err, "can not save role info")
+               log.Error("can not save role info", err)
                return err
        }
        if exist {
@@ -54,12 +54,12 @@ func (ds *DataSource) CreateRole(ctx context.Context, r 
*rbacframe.Role) error {
        r.ID = util.GenerateUUID()
        value, err := json.Marshal(r)
        if err != nil {
-               log.Errorf(err, "role info is invalid")
+               log.Error("role info is invalid", err)
                return err
        }
        err = client.PutBytes(ctx, key, value)
        if err != nil {
-               log.Errorf(err, "can not save account info")
+               log.Error("can not save account info", err)
                return err
        }
        log.Info("create new role: " + r.ID)
@@ -119,7 +119,7 @@ func (ds *DataSource) DeleteRole(ctx context.Context, name 
string) (bool, error)
        if err != nil {
                return false, err
        }
-       return resp.Count != 0, nil
+       return resp.Succeeded, nil
 }
 func (ds *DataSource) UpdateRole(ctx context.Context, name string, role 
*rbacframe.Role) error {
        value, err := json.Marshal(role)
diff --git a/datasource/mongo/account.go b/datasource/mongo/account.go
index ccc30c3..58338c2 100644
--- a/datasource/mongo/account.go
+++ b/datasource/mongo/account.go
@@ -19,6 +19,7 @@ package mongo
 
 import (
        "context"
+       "errors"
 
        "github.com/apache/servicecomb-service-center/datasource"
        "github.com/apache/servicecomb-service-center/datasource/mongo/client"
@@ -59,7 +60,7 @@ func (ds *DataSource) CreateAccount(ctx context.Context, a 
*rbacframe.Account) e
 
 func (ds *DataSource) AccountExist(ctx context.Context, key string) (bool, 
error) {
        filter := bson.M{
-               AccountName: key,
+               ColumnAccountName: key,
        }
        count, err := client.GetMongoClient().Count(ctx, CollectionAccount, 
filter)
        if err != nil {
@@ -73,19 +74,20 @@ func (ds *DataSource) AccountExist(ctx context.Context, key 
string) (bool, error
 
 func (ds *DataSource) GetAccount(ctx context.Context, key string) 
(*rbacframe.Account, error) {
        filter := bson.M{
-               AccountName: key,
+               ColumnAccountName: key,
        }
        result, err := client.GetMongoClient().FindOne(ctx, CollectionAccount, 
filter)
        if err != nil {
                return nil, err
        }
        if result.Err() != nil {
-               return nil, result.Err()
+               log.Error("failed to get account: ", result.Err())
+               return nil, errors.New("failed to get account")
        }
        var account rbacframe.Account
        err = result.Decode(&account)
        if err != nil {
-               log.Error("Decode account failed: ", err)
+               log.Error("decode account failed: ", err)
                return nil, err
        }
        return &account, nil
@@ -93,7 +95,10 @@ func (ds *DataSource) GetAccount(ctx context.Context, key 
string) (*rbacframe.Ac
 
 func (ds *DataSource) ListAccount(ctx context.Context, key string) 
([]*rbacframe.Account, int64, error) {
        filter := bson.M{
-               AccountName: bson.M{"$regex": key},
+               ColumnAccountName: bson.M{"$regex": key},
+       }
+       if len(key) == 0 {
+               filter = bson.M{}
        }
        cursor, err := client.GetMongoClient().Find(ctx, CollectionAccount, 
filter)
        if err != nil {
@@ -105,9 +110,10 @@ func (ds *DataSource) ListAccount(ctx context.Context, key 
string) ([]*rbacframe
                var account rbacframe.Account
                err = cursor.Decode(&account)
                if err != nil {
-                       log.Error("Decode account failed: ", err)
-                       break
+                       log.Error("decode account failed: ", err)
+                       continue
                }
+               account.Password = ""
                accounts = append(accounts, &account)
        }
        return accounts, int64(len(accounts)), nil
@@ -115,7 +121,7 @@ func (ds *DataSource) ListAccount(ctx context.Context, key 
string) ([]*rbacframe
 
 func (ds *DataSource) DeleteAccount(ctx context.Context, key string) (bool, 
error) {
        filter := bson.M{
-               AccountName: key,
+               ColumnAccountName: key,
        }
        result, err := client.GetMongoClient().Delete(ctx, CollectionAccount, 
filter)
        if err != nil {
@@ -129,11 +135,17 @@ func (ds *DataSource) DeleteAccount(ctx context.Context, 
key string) (bool, erro
 
 func (ds *DataSource) UpdateAccount(ctx context.Context, key string, account 
*rbacframe.Account) error {
        filter := bson.M{
-               AccountName: key,
+               ColumnAccountName: key,
        }
        update := bson.M{
-               "$set": bson.M{AccountID: account.ID, AccountPassword: 
account.Name, AccountRole: account.Roles, AccountTokenExpirationTime: 
account.TokenExpirationTime,
-                       AccountCurrentPassword: account.CurrentPassword, 
AccountStatus: account.Status,
+               "$set": bson.M{
+                       ColumnID:                  account.ID,
+                       ColumnAccountName:         account.Name,
+                       ColumnPassword:            account.Password,
+                       ColumnRole:                account.Roles,
+                       ColumnTokenExpirationTime: account.TokenExpirationTime,
+                       ColumnCurrentPassword:     account.CurrentPassword,
+                       ColumnStatus:              account.Status,
                },
        }
        result, err := client.GetMongoClient().Update(ctx, CollectionAccount, 
filter, update)
diff --git a/datasource/mongo/database.go b/datasource/mongo/database.go
index 9cbcfb5..465e19a 100644
--- a/datasource/mongo/database.go
+++ b/datasource/mongo/database.go
@@ -24,61 +24,59 @@ import (
 )
 
 const (
-       DuplicateKey               = 11000
-       AccountName                = "name"
-       AccountID                  = "id"
-       AccountPassword            = "password"
-       AccountRole                = "role"
-       AccountTokenExpirationTime = "tokenexpirationtime"
-       AccountCurrentPassword     = "currentpassword"
-       AccountStatus              = "status"
-       RefreshTime                = "refreshtime"
-)
-
-const (
        CollectionAccount  = "account"
        CollectionService  = "service"
        CollectionSchema   = "schema"
        CollectionRule     = "rule"
        CollectionInstance = "instance"
        CollectionDep      = "dependency"
+       CollectionRole     = "role"
 )
 
 const (
-       DepsQueueUUID     = "0"
-       ErrorDuplicateKey = 11000
+       DepsQueueUUID = "0"
 )
 
 const (
-       ColumnDomain         = "domain"
-       ColumnProject        = "project"
-       ColumnTag            = "tags"
-       ColumnSchemaID       = "schemaid"
-       ColumnServiceID      = "serviceid"
-       ColumnRuleID         = "ruleid"
-       ColumnServiceInfo    = "serviceinfo"
-       ColumnProperty       = "properties"
-       ColumnModTime        = "modtimestamp"
-       ColumnEnv            = "environment"
-       ColumnAppID          = "appid"
-       ColumnServiceName    = "servicename"
-       ColumnAlias          = "alias"
-       ColumnVersion        = "version"
-       ColumnSchemas        = "schemas"
-       ColumnAttribute      = "attribute"
-       ColumnPattern        = "pattern"
-       ColumnDescription    = "description"
-       ColumnRuleType       = "ruletype"
-       ColumnSchemaInfo     = "schemainfo"
-       ColumnSchemaSummary  = "schemasummary"
-       ColumnConsumer       = "consumer"
-       ColumnDependencyInfo = "dependencyinfo"
-       ColumnRuleInfo       = "ruleinfo"
-       ColumnInstanceInfo   = "instanceinfo"
-       ColumnInstanceID     = "instanceid"
-       ColumnConsumerID     = "consumerid"
-       ColumnMongoID        = "_id"
-       ColumnTenant         = "tenant"
+       ColumnDomain              = "domain"
+       ColumnProject             = "project"
+       ColumnTag                 = "tags"
+       ColumnSchemaID            = "schemaid"
+       ColumnServiceID           = "serviceid"
+       ColumnRuleID              = "ruleid"
+       ColumnServiceInfo         = "serviceinfo"
+       ColumnProperty            = "properties"
+       ColumnModTime             = "modtimestamp"
+       ColumnEnv                 = "environment"
+       ColumnAppID               = "appid"
+       ColumnServiceName         = "servicename"
+       ColumnAlias               = "alias"
+       ColumnVersion             = "version"
+       ColumnSchemas             = "schemas"
+       ColumnAttribute           = "attribute"
+       ColumnPattern             = "pattern"
+       ColumnDescription         = "description"
+       ColumnRuleType            = "ruletype"
+       ColumnSchemaInfo          = "schemainfo"
+       ColumnSchemaSummary       = "schemasummary"
+       ColumnConsumer            = "consumer"
+       ColumnDependencyInfo      = "dependencyinfo"
+       ColumnRuleInfo            = "ruleinfo"
+       ColumnInstanceInfo        = "instanceinfo"
+       ColumnInstanceID          = "instanceid"
+       ColumnConsumerID          = "consumerid"
+       ColumnMongoID             = "_id"
+       ColumnTenant              = "tenant"
+       ColumnID                  = "id"
+       ColumnAccountName         = "name"
+       ColumnRoleName            = "name"
+       ColumnPerms               = "perms"
+       ColumnPassword            = "password"
+       ColumnRole                = "role"
+       ColumnTokenExpirationTime = "tokenexpirationtime"
+       ColumnCurrentPassword     = "currentpassword"
+       ColumnStatus              = "status"
+       ColumnRefreshTime         = "refreshtime"
 )
 
 type Service struct {
diff --git a/datasource/mongo/dep.go b/datasource/mongo/dep.go
index fe0467d..c45577a 100644
--- a/datasource/mongo/dep.go
+++ b/datasource/mongo/dep.go
@@ -115,7 +115,7 @@ func (ds *DataSource) AddOrUpdateDependencies(ctx 
context.Context, dependencyInf
                }
 
                consumerID, err := GetServiceID(ctx, consumerInfo)
-               if err != nil {
+               if err != nil && !errors.Is(err, datasource.ErrNoData) {
                        log.Error(fmt.Sprintf("put request into dependency 
queue failed, override: %t, get consumer %s id failed",
                                override, consumerFlag), err)
                        return pb.CreateResponse(pb.ErrInternal, err.Error()), 
err
@@ -155,32 +155,25 @@ func (ds *DataSource) DeleteDependency() {
        panic("implement me")
 }
 
-func GetServiceID(ctx context.Context, key *pb.MicroServiceKey) (serviceID 
string, err error) {
-       domain := util.ParseDomain(ctx)
-       project := util.ParseProject(ctx)
-       filter := bson.M{
-               ColumnDomain:  domain,
-               ColumnProject: project,
-               StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):         
key.Environment,
-               StringBuilder([]string{ColumnServiceInfo, ColumnAppID}):       
key.AppId,
-               StringBuilder([]string{ColumnServiceInfo, ColumnServiceName}): 
key.ServiceName,
-               StringBuilder([]string{ColumnServiceInfo, ColumnVersion}):     
key.Version}
-
-       findRes, err := client.GetMongoClient().Find(ctx, CollectionService, 
filter)
-       if err != nil {
-               return "", nil
+func GetServiceID(ctx context.Context, key *pb.MicroServiceKey) (string, 
error) {
+       id, err := getServiceID(ctx, GeneratorServiceNameFilter(ctx, key))
+       if err != nil && !errors.Is(err, datasource.ErrNoData) {
+               return "", err
        }
-       var service []*Service
-       for findRes.Next(ctx) {
-               var temp *Service
-               err := findRes.Decode(&temp)
-               if err != nil {
-                       return "", nil
-               }
-               service = append(service, temp)
+       if len(id) == 0 && len(key.Alias) != 0 {
+               return getServiceID(ctx, GeneratorServiceAliasFilter(ctx, key))
+       }
+       return id, nil
+}
+
+func getServiceID(ctx context.Context, filter bson.M) (serviceID string, err 
error) {
+       svc, err := GetService(ctx, filter)
+       if err != nil {
+               return
        }
-       if service == nil {
-               return "", nil
+       if svc != nil {
+               serviceID = svc.ServiceInfo.ServiceId
+               return
        }
-       return service[0].ServiceInfo.ServiceId, nil
+       return
 }
diff --git a/datasource/mongo/dependency_query.go 
b/datasource/mongo/dependency_query.go
index db91d47..d404f90 100644
--- a/datasource/mongo/dependency_query.go
+++ b/datasource/mongo/dependency_query.go
@@ -269,7 +269,7 @@ func (dr *DependencyRelation) GetDependencyConsumerIds() 
([]string, error) {
        consumerIDs := make([]string, 0, len(consumerDependAllList))
        for _, consumer := range consumerDependAllList {
                consumerID, err := GetServiceID(dr.ctx, consumer)
-               if err != nil {
+               if err != nil && !errors.Is(err, datasource.ErrNoData) {
                        log.Error(fmt.Sprintf("get consumer[%s/%s/%s/%s] 
failed",
                                consumer.Environment, consumer.AppId, 
consumer.ServiceName, consumer.Version), err)
                        return nil, err
diff --git a/datasource/mongo/heartbeat/heartbeatchecker/heartbeat.go 
b/datasource/mongo/heartbeat/heartbeatchecker/heartbeat.go
index 29ab0de..c5113df 100644
--- a/datasource/mongo/heartbeat/heartbeatchecker/heartbeat.go
+++ b/datasource/mongo/heartbeat/heartbeatchecker/heartbeat.go
@@ -28,12 +28,13 @@ import (
        "github.com/apache/servicecomb-service-center/pkg/log"
 )
 
-func updateInstanceRefreshTime(ctx context.Context, instanceID string) error {
+func updateInstanceRefreshTime(ctx context.Context, serviceID string, 
instanceID string) error {
        filter := bson.M{
+               mongo.StringBuilder([]string{mongo.ColumnInstanceInfo, 
mongo.ColumnServiceID}):  serviceID,
                mongo.StringBuilder([]string{mongo.ColumnInstanceInfo, 
mongo.ColumnInstanceID}): instanceID,
        }
        update := bson.M{
-               "$set": bson.M{mongo.RefreshTime: time.Now()},
+               "$set": bson.M{mongo.ColumnRefreshTime: time.Now()},
        }
        result, err := client.GetMongoClient().FindOneAndUpdate(ctx, 
mongo.CollectionInstance, filter, update)
        if err != nil {
diff --git a/datasource/mongo/heartbeat/heartbeatchecker/heartbeat_test.go 
b/datasource/mongo/heartbeat/heartbeatchecker/heartbeat_test.go
index ae66ac2..5c6c35f 100644
--- a/datasource/mongo/heartbeat/heartbeatchecker/heartbeat_test.go
+++ b/datasource/mongo/heartbeat/heartbeatchecker/heartbeat_test.go
@@ -41,7 +41,7 @@ func init() {
 
 func TestUpdateInstanceRefreshTime(t *testing.T) {
        t.Run("update instance refresh time: if the instance does not exist,the 
update should fail", func(t *testing.T) {
-               err := updateInstanceRefreshTime(context.Background(), 
"not-exist")
+               err := updateInstanceRefreshTime(context.Background(), 
"not-exist", "not-exist")
                log.Error("", err)
                assert.NotNil(t, err)
        })
@@ -56,9 +56,10 @@ func TestUpdateInstanceRefreshTime(t *testing.T) {
                }
                _, err := client.GetMongoClient().Insert(context.Background(), 
mongo.CollectionInstance, instance1)
                assert.Equal(t, nil, err)
-               err = updateInstanceRefreshTime(context.Background(), 
instance1.InstanceInfo.InstanceId)
+               err = updateInstanceRefreshTime(context.Background(), 
instance1.InstanceInfo.ServiceId, instance1.InstanceInfo.InstanceId)
                assert.Equal(t, nil, err)
                filter := bson.M{
+                       mongo.StringBuilder([]string{mongo.ColumnInstanceInfo, 
mongo.ColumnServiceID}):  instance1.InstanceInfo.ServiceId,
                        mongo.StringBuilder([]string{mongo.ColumnInstanceInfo, 
mongo.ColumnInstanceID}): instance1.InstanceInfo.InstanceId,
                }
                result, err := 
client.GetMongoClient().FindOne(context.Background(), mongo.CollectionInstance, 
filter)
@@ -68,6 +69,7 @@ func TestUpdateInstanceRefreshTime(t *testing.T) {
                assert.Nil(t, err)
                assert.NotEqual(t, instance1.RefreshTime, ins.RefreshTime)
                filter = bson.M{
+                       mongo.StringBuilder([]string{mongo.ColumnInstanceInfo, 
mongo.ColumnServiceID}):  instance1.InstanceInfo.ServiceId,
                        mongo.StringBuilder([]string{mongo.ColumnInstanceInfo, 
mongo.ColumnInstanceID}): instance1.InstanceInfo.InstanceId,
                }
                _, err = client.GetMongoClient().Delete(context.Background(), 
mongo.CollectionInstance, filter)
diff --git a/datasource/mongo/heartbeat/heartbeatchecker/heartbeatchecker.go 
b/datasource/mongo/heartbeat/heartbeatchecker/heartbeatchecker.go
index ed98911..03be2af 100644
--- a/datasource/mongo/heartbeat/heartbeatchecker/heartbeatchecker.go
+++ b/datasource/mongo/heartbeat/heartbeatchecker/heartbeatchecker.go
@@ -41,7 +41,7 @@ func NewHeartBeatChecker(opts heartbeat.Options) 
(heartbeat.HealthCheck, error)
 
 func (h *HeartBeatChecker) Heartbeat(ctx context.Context, request 
*pb.HeartbeatRequest) (*pb.HeartbeatResponse, error) {
        remoteIP := util.GetIPFromContext(ctx)
-       err := updateInstanceRefreshTime(ctx, request.InstanceId)
+       err := updateInstanceRefreshTime(ctx, request.ServiceId, 
request.InstanceId)
        if err != nil {
                log.Error(fmt.Sprintf("heartbeat failed, instance[%s]. operator 
%s", request.InstanceId, remoteIP), err)
                resp := &pb.HeartbeatResponse{
diff --git a/datasource/mongo/ms.go b/datasource/mongo/ms.go
index d60a9d8..075c536 100644
--- a/datasource/mongo/ms.go
+++ b/datasource/mongo/ms.go
@@ -19,10 +19,13 @@ package mongo
 
 import (
        "context"
+       "crypto/sha1"
+       "encoding/json"
        "errors"
        "fmt"
        "reflect"
        "regexp"
+       "sort"
        "strconv"
        "strings"
        "time"
@@ -62,36 +65,59 @@ func (ds *DataSource) RegisterService(ctx context.Context, 
request *discovery.Cr
        }
        service.Timestamp = strconv.FormatInt(time.Now().Unix(), 10)
        service.ModTimestamp = service.Timestamp
-       // the service unique index in table is 
(serviceId,serviceEnv,serviceAppid,servicename,serviceAlias,serviceVersion)
+       // the service unique index in table is 
(serviceId/serviceEnv,serviceAppid,servicename,serviceVersion)
+       if len(service.Alias) != 0 {
+               serviceID, err := GetServiceID(ctx, &discovery.MicroServiceKey{
+                       Environment: service.Environment,
+                       AppId:       service.AppId,
+                       ServiceName: service.ServiceName,
+                       Version:     service.Version,
+                       Alias:       service.Alias,
+               })
+               if err != nil && !errors.Is(err, datasource.ErrNoData) {
+                       return &discovery.CreateServiceResponse{
+                               Response: 
discovery.CreateResponse(discovery.ErrUnavailableBackend, err.Error()),
+                       }, err
+               }
+               if len(serviceID) != 0 {
+                       if len(requestServiceID) != 0 && requestServiceID != 
serviceID {
+                               log.Warn(fmt.Sprintf("create micro-service[%s] 
failed, service already exists, operator: %s",
+                                       serviceFlag, remoteIP))
+                               return &discovery.CreateServiceResponse{
+                                       Response: 
discovery.CreateResponse(discovery.ErrServiceAlreadyExists,
+                                               "ServiceID conflict or found 
the same service with different id."),
+                               }, nil
+                       }
+                       return &discovery.CreateServiceResponse{
+                               Response:  
discovery.CreateResponse(discovery.ResponseSuccess, "register service 
successfully"),
+                               ServiceId: serviceID,
+                       }, nil
+               }
+       }
        insertRes, err := client.GetMongoClient().Insert(ctx, 
CollectionService, &Service{Domain: domain, Project: project, ServiceInfo: 
service})
        if err != nil {
                if client.IsDuplicateKey(err) {
-                       if len(requestServiceID) == 0 {
-                               serviceIDInner, err := GetServiceID(ctx, 
&discovery.MicroServiceKey{
-                                       Environment: service.Environment,
-                                       AppId:       service.AppId,
-                                       ServiceName: service.ServiceName,
-                                       Version:     service.Version,
-                               })
-                               if err != nil {
-                                       return &discovery.CreateServiceResponse{
-                                               Response: 
discovery.CreateResponse(discovery.ErrUnavailableBackend, err.Error()),
-                                       }, err
-                               }
-                               if len(serviceIDInner) != 0 {
-                                       log.Warn(fmt.Sprintf("create 
micro-service[%s][%s] failed, service already exists, operator: %s",
-                                               serviceIDInner, serviceFlag, 
remoteIP))
-                                       return &discovery.CreateServiceResponse{
-                                               Response:  
discovery.CreateResponse(discovery.ResponseSuccess, "register service 
successfully"),
-                                               ServiceId: serviceIDInner,
-                                       }, nil
-                               }
+                       serviceIDInner, err := GetServiceID(ctx, 
&discovery.MicroServiceKey{
+                               Environment: service.Environment,
+                               AppId:       service.AppId,
+                               ServiceName: service.ServiceName,
+                               Version:     service.Version,
+                       })
+                       if err != nil && !errors.Is(err, datasource.ErrNoData) {
+                               return &discovery.CreateServiceResponse{
+                                       Response: 
discovery.CreateResponse(discovery.ErrUnavailableBackend, err.Error()),
+                               }, err
+                       }
+                       // serviceid conflict with the service in the database
+                       if len(requestServiceID) != 0 && serviceIDInner != 
requestServiceID {
+                               return &discovery.CreateServiceResponse{
+                                       Response: 
discovery.CreateResponse(discovery.ErrServiceAlreadyExists,
+                                               "ServiceID conflict or found 
the same service with different id."),
+                               }, nil
                        }
-                       log.Warn(fmt.Sprintf("create micro-service[%s] failed, 
service already exists, operator: %s",
-                               serviceFlag, remoteIP))
                        return &discovery.CreateServiceResponse{
-                               Response: 
discovery.CreateResponse(discovery.ErrServiceAlreadyExists,
-                                       "ServiceID conflict or found the same 
service with different id."),
+                               Response:  
discovery.CreateResponse(discovery.ResponseSuccess, "register service 
successfully"),
+                               ServiceId: serviceIDInner,
                        }, nil
                }
                log.Error(fmt.Sprintf("create micro-service[%s] failed, service 
already exists, operator: %s",
@@ -191,7 +217,6 @@ func (ds *DataSource) GetService(ctx context.Context, 
request *discovery.GetServ
 }
 
 func (ds *DataSource) ExistServiceByID(ctx context.Context, request 
*discovery.GetExistenceByIDRequest) (*discovery.GetExistenceByIDResponse, 
error) {
-
        exist, err := ServiceExistID(ctx, request.ServiceId)
        if err != nil {
                return &discovery.GetExistenceByIDResponse{
@@ -1526,7 +1551,7 @@ func (ds *DataSource) RegisterInstance(ctx 
context.Context, request *discovery.R
        return registryInstance(ctx, request)
 }
 
-// GetInstances returns instances under the current domain
+// GetInstance returns instance under the current domain
 func (ds *DataSource) GetInstance(ctx context.Context, request 
*discovery.GetOneInstanceRequest) (*discovery.GetOneInstanceResponse, error) {
        var service *Service
        var err error
@@ -1583,27 +1608,52 @@ func (ds *DataSource) GetInstance(ctx context.Context, 
request *discovery.GetOne
                        Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
                }, err
        }
-       if services != nil {
-               serviceIDs = filterServiceIDs(ctx, request.ConsumerServiceId, 
request.Tags, services)
-       }
-       if services == nil || len(serviceIDs) == 0 {
-               mes := fmt.Errorf("%s failed, provider does not exist", 
findFlag())
-               log.Error("get instance failed", mes)
-               return &discovery.GetOneInstanceResponse{
-                       Response: 
discovery.CreateResponse(discovery.ErrServiceNotExists, mes.Error()),
-               }, nil
-       }
-       instances, err := instancesFilter(ctx, serviceIDs)
-       if len(instances) == 0 {
+       rev, _ := ctx.Value(util.CtxRequestRevision).(string)
+       serviceIDs = filterServiceIDs(ctx, request.ConsumerServiceId, 
request.Tags, services)
+       if len(serviceIDs) == 0 {
                mes := fmt.Errorf("%s failed, provider instance does not 
exist", findFlag())
                log.Error("get instance failed", err)
                return &discovery.GetOneInstanceResponse{
                        Response: 
discovery.CreateResponse(discovery.ErrInstanceNotExists, mes.Error()),
                }, nil
        }
+       instances, err := GetInstancesByServiceID(ctx, 
request.ProviderServiceId)
+       if err != nil {
+               log.Error(fmt.Sprintf("get instance failed %s", findFlag()), 
err)
+               return &discovery.GetOneInstanceResponse{
+                       Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+               }, err
+       }
+       instance := instances[0]
+       // use explicit instanceId to query
+       if len(request.ProviderInstanceId) != 0 {
+               isExist := false
+               for _, ins := range instances {
+                       if ins.InstanceId == request.ProviderInstanceId {
+                               instance = ins
+                               isExist = true
+                               break
+                       }
+               }
+               if !isExist {
+                       mes := fmt.Errorf("%s failed, provider instance does 
not exist", findFlag())
+                       log.Error("get instance failed", err)
+                       return &discovery.GetOneInstanceResponse{
+                               Response: 
discovery.CreateResponse(discovery.ErrInstanceNotExists, mes.Error()),
+                       }, nil
+               }
+       }
+
+       newRev, _ := formatRevision(request.ConsumerServiceId, instances)
+       if rev == newRev {
+               instance = nil // for gRPC
+       }
+       // TODO support gRPC output context
+       _ = util.WithResponseRev(ctx, newRev)
+
        return &discovery.GetOneInstanceResponse{
                Response: discovery.CreateResponse(discovery.ResponseSuccess, 
"Get instance successfully."),
-               Instance: instances[0],
+               Instance: instance,
        }, nil
 }
 
@@ -1653,6 +1703,7 @@ func (ds *DataSource) GetInstances(ctx context.Context, 
request *discovery.GetIn
                        provider.ServiceInfo.ServiceId, 
provider.ServiceInfo.Environment, provider.ServiceInfo.AppId, 
provider.ServiceInfo.ServiceName, provider.ServiceInfo.Version)
        }
 
+       rev, _ := ctx.Value(util.CtxRequestRevision).(string)
        serviceIDs := filterServiceIDs(ctx, request.ConsumerServiceId, 
request.Tags, []*Service{provider})
        if len(serviceIDs) == 0 {
                mes := fmt.Errorf("%s failed, provider does not exist", 
findFlag())
@@ -1668,6 +1719,11 @@ func (ds *DataSource) GetInstances(ctx context.Context, 
request *discovery.GetIn
                        Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
                }, err
        }
+       newRev, _ := formatRevision(request.ConsumerServiceId, instances)
+       if rev == newRev {
+               instances = nil // for gRPC
+       }
+       _ = util.WithResponseRev(ctx, newRev)
        return &discovery.GetInstancesResponse{
                Response:  discovery.CreateResponse(discovery.ResponseSuccess, 
"Query service instances successfully."),
                Instances: instances,
@@ -1765,15 +1821,23 @@ func (ds *DataSource) FindInstances(ctx 
context.Context, request *discovery.Find
                Environment: request.Environment,
                AppId:       request.AppId,
                ServiceName: request.ServiceName,
-               Alias:       request.ServiceName,
+               Alias:       request.Alias,
                Version:     request.VersionRule,
        }
+       rev, ok := ctx.Value(util.CtxRequestRevision).(string)
+       if !ok {
+               err := errors.New("rev request context is not type string")
+               log.Error("", err)
+               return &discovery.FindInstancesResponse{
+                       Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+               }, err
+       }
 
        if apt.IsGlobal(provider) {
-               return ds.findSharedServiceInstance(ctx, request, provider)
+               return ds.findSharedServiceInstance(ctx, request, provider, rev)
        }
 
-       return ds.findInstance(ctx, request, provider)
+       return ds.findInstance(ctx, request, provider, rev)
 }
 
 func (ds *DataSource) UpdateInstanceStatus(ctx context.Context, request 
*discovery.UpdateInstanceStatusRequest) 
(*discovery.UpdateInstanceStatusResponse, error) {
@@ -2009,13 +2073,20 @@ func registryInstance(ctx context.Context, request 
*discovery.RegisterInstanceRe
        }, nil
 }
 
-func (ds *DataSource) findSharedServiceInstance(ctx context.Context, request 
*discovery.FindInstancesRequest, provider *discovery.MicroServiceKey) 
(*discovery.FindInstancesResponse, error) {
+func (ds *DataSource) findSharedServiceInstance(ctx context.Context, request 
*discovery.FindInstancesRequest, provider *discovery.MicroServiceKey, rev 
string) (*discovery.FindInstancesResponse, error) {
        var err error
        // it means the shared micro-services must be the same env with SC.
        provider.Environment = apt.Service.Environment
        findFlag := func() string {
                return fmt.Sprintf("find shared provider[%s/%s/%s/%s]", 
provider.Environment, provider.AppId, provider.ServiceName, provider.Version)
        }
+       basicFilterServices, err := servicesBasicFilter(ctx, provider)
+       if err != nil {
+               log.Error(fmt.Sprintf("find shared service instance failed %s", 
findFlag()), err)
+               return &discovery.FindInstancesResponse{
+                       Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+               }, err
+       }
        services, err := findServices(ctx, provider)
        if err != nil {
                log.Error(fmt.Sprintf("find shared service instance failed %s", 
findFlag()), err)
@@ -2023,7 +2094,7 @@ func (ds *DataSource) findSharedServiceInstance(ctx 
context.Context, request *di
                        Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
                }, err
        }
-       if services == nil {
+       if services == nil && len(basicFilterServices) == 0 {
                mes := fmt.Errorf("%s failed, provider does not exist", 
findFlag())
                log.Error("find shared service instance failed", mes)
                return &discovery.FindInstancesResponse{
@@ -2031,12 +2102,6 @@ func (ds *DataSource) findSharedServiceInstance(ctx 
context.Context, request *di
                }, nil
        }
        serviceIDs := filterServiceIDs(ctx, request.ConsumerServiceId, 
request.Tags, services)
-       if len(serviceIDs) == 0 {
-               return &discovery.FindInstancesResponse{
-                       Response:  
discovery.CreateResponse(discovery.ResponseSuccess, "Query service instances 
successfully."),
-                       Instances: nil,
-               }, nil
-       }
        instances, err := instancesFilter(ctx, serviceIDs)
        if err != nil {
                log.Error(fmt.Sprintf("find shared service instance failed %s", 
findFlag()), err)
@@ -2044,13 +2109,19 @@ func (ds *DataSource) findSharedServiceInstance(ctx 
context.Context, request *di
                        Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
                }, err
        }
+       newRev, _ := formatRevision(request.ConsumerServiceId, instances)
+       if rev == newRev {
+               instances = nil // for gRPC
+       }
+       // TODO support gRPC output context
+       _ = util.WithResponseRev(ctx, newRev)
        return &discovery.FindInstancesResponse{
                Response:  discovery.CreateResponse(discovery.ResponseSuccess, 
"Query service instances successfully."),
                Instances: instances,
        }, nil
 }
 
-func (ds *DataSource) findInstance(ctx context.Context, request 
*discovery.FindInstancesRequest, provider *discovery.MicroServiceKey) 
(*discovery.FindInstancesResponse, error) {
+func (ds *DataSource) findInstance(ctx context.Context, request 
*discovery.FindInstancesRequest, provider *discovery.MicroServiceKey, rev 
string) (*discovery.FindInstancesResponse, error) {
        var err error
        domainProject := util.ParseDomainProject(ctx)
        service := &Service{ServiceInfo: &discovery.MicroService{Environment: 
request.Environment}}
@@ -2085,6 +2156,13 @@ func (ds *DataSource) findInstance(ctx context.Context, 
request *discovery.FindI
                        request.ConsumerServiceId, 
service.ServiceInfo.Environment, service.ServiceInfo.AppId, 
service.ServiceInfo.ServiceName, service.ServiceInfo.Version,
                        provider.Environment, provider.AppId, 
provider.ServiceName, provider.Version)
        }
+       basicFilterServices, err := servicesBasicFilter(ctx, provider)
+       if err != nil {
+               log.Error(fmt.Sprintf("find instance failed %s", findFlag()), 
err)
+               return &discovery.FindInstancesResponse{
+                       Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
+               }, err
+       }
        services, err := findServices(ctx, provider)
        if err != nil {
                log.Error(fmt.Sprintf("find instance failed %s", findFlag()), 
err)
@@ -2092,7 +2170,7 @@ func (ds *DataSource) findInstance(ctx context.Context, 
request *discovery.FindI
                        Response: 
discovery.CreateResponse(discovery.ErrInternal, err.Error()),
                }, err
        }
-       if services == nil {
+       if services == nil && len(basicFilterServices) == 0 {
                mes := fmt.Errorf("%s failed, provider does not exist", 
findFlag())
                log.Error("find instance failed", mes)
                return &discovery.FindInstancesResponse{
@@ -2100,12 +2178,6 @@ func (ds *DataSource) findInstance(ctx context.Context, 
request *discovery.FindI
                }, nil
        }
        serviceIDs := filterServiceIDs(ctx, request.ConsumerServiceId, 
request.Tags, services)
-       if len(serviceIDs) == 0 {
-               return &discovery.FindInstancesResponse{
-                       Response:  
discovery.CreateResponse(discovery.ResponseSuccess, "Query service instances 
successfully."),
-                       Instances: nil,
-               }, nil
-       }
        instances, err := instancesFilter(ctx, serviceIDs)
        if err != nil {
                log.Error(fmt.Sprintf("find instance failed %s", findFlag()), 
err)
@@ -2136,7 +2208,12 @@ func (ds *DataSource) findInstance(ctx context.Context, 
request *discovery.FindI
                        }, err
                }
        }
-
+       newRev, _ := formatRevision(request.ConsumerServiceId, instances)
+       if rev == newRev {
+               instances = nil // for gRPC
+       }
+       // TODO support gRPC output context
+       _ = util.WithResponseRev(ctx, newRev)
        return &discovery.FindInstancesResponse{
                Response:  discovery.CreateResponse(discovery.ResponseSuccess, 
"Query service instances successfully."),
                Instances: instances,
@@ -2382,56 +2459,72 @@ func preProcessRegisterInstance(ctx context.Context, 
instance *discovery.MicroSe
        return nil
 }
 
+// servicesBasicFilter query services with domain, project, env, appID, 
serviceName, alias
+func servicesBasicFilter(ctx context.Context, key *discovery.MicroServiceKey) 
([]*Service, error) {
+       tenant := strings.Split(key.Tenant, "/")
+       if len(tenant) != 2 {
+               return nil, errors.New("invalid 'domain' or 'project'")
+       }
+       filter := bson.M{
+               ColumnDomain:  tenant[0],
+               ColumnProject: tenant[1],
+               StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):         
key.Environment,
+               StringBuilder([]string{ColumnServiceInfo, ColumnAppID}):       
key.AppId,
+               StringBuilder([]string{ColumnServiceInfo, ColumnServiceName}): 
key.ServiceName,
+               StringBuilder([]string{ColumnServiceInfo, ColumnAlias}):       
key.Alias,
+       }
+       rangeIdx := strings.Index(key.Version, "-")
+       // if the version number is clear, need to add the version number to 
query
+       switch {
+       case key.Version == "latest":
+               return servicesFilter(ctx, filter)
+       case len(key.Version) > 0 && key.Version[len(key.Version)-1:] == "+":
+               return servicesFilter(ctx, filter)
+       case rangeIdx > 0:
+               return servicesFilter(ctx, filter)
+       default:
+               filter[StringBuilder([]string{ColumnServiceInfo, 
ColumnVersion})] = key.Version
+               return servicesFilter(ctx, filter)
+       }
+}
+
 func findServices(ctx context.Context, key *discovery.MicroServiceKey) 
([]*Service, error) {
        tenant := strings.Split(key.Tenant, "/")
        if len(tenant) != 2 {
                return nil, errors.New("invalid 'domain' or 'project'")
        }
        rangeIdx := strings.Index(key.Version, "-")
+       filter := bson.M{
+               ColumnDomain:  tenant[0],
+               ColumnProject: tenant[1],
+               StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):         
key.Environment,
+               StringBuilder([]string{ColumnServiceInfo, ColumnAppID}):       
key.AppId,
+               StringBuilder([]string{ColumnServiceInfo, ColumnServiceName}): 
key.ServiceName,
+               StringBuilder([]string{ColumnServiceInfo, ColumnAlias}):       
key.Alias,
+       }
        switch {
        case key.Version == "latest":
-               filter := bson.M{
-                       ColumnDomain:  tenant[0],
-                       ColumnProject: tenant[1],
-                       StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):  
       key.Environment,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnAppID}):       key.AppId,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnServiceName}): key.ServiceName,
-               }
                return latestServicesFilter(ctx, filter)
        case len(key.Version) > 0 && key.Version[len(key.Version)-1:] == "+":
                start := key.Version[:len(key.Version)-1]
-               filter := bson.M{
-                       ColumnDomain:  tenant[0],
-                       ColumnProject: tenant[1],
-                       StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):  
       key.Environment,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnAppID}):       key.AppId,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnServiceName}): key.ServiceName,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnVersion}):     bson.M{"$gte": start}}
+               filter[StringBuilder([]string{ColumnServiceInfo, 
ColumnVersion})] = bson.M{"$gte": start}
                return servicesFilter(ctx, filter)
        case rangeIdx > 0:
                start := key.Version[:rangeIdx]
                end := key.Version[rangeIdx+1:]
-               filter := bson.M{
-                       ColumnDomain:  tenant[0],
-                       ColumnProject: tenant[1],
-                       StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):  
       key.Environment,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnAppID}):       key.AppId,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnServiceName}): key.ServiceName,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnVersion}):     bson.M{"$gte": start, "$lte": end}}
+               filter[StringBuilder([]string{ColumnServiceInfo, 
ColumnVersion})] = bson.M{"$gte": start, "$lte": end}
                return servicesFilter(ctx, filter)
        default:
-               filter := bson.M{
-                       ColumnDomain:  tenant[0],
-                       ColumnProject: tenant[1],
-                       StringBuilder([]string{ColumnServiceInfo, ColumnEnv}):  
       key.Environment,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnAppID}):       key.AppId,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnServiceName}): key.ServiceName,
-                       StringBuilder([]string{ColumnServiceInfo, 
ColumnVersion}):     key.Version}
+               filter[StringBuilder([]string{ColumnServiceInfo, 
ColumnVersion})] = key.Version
                return servicesFilter(ctx, filter)
        }
 }
 
 func instancesFilter(ctx context.Context, serviceIDs []string) 
([]*discovery.MicroServiceInstance, error) {
+       var instances []*discovery.MicroServiceInstance
+       if len(serviceIDs) == 0 {
+               return instances, nil
+       }
        resp, err := client.GetMongoClient().Find(ctx, CollectionInstance, 
bson.M{StringBuilder([]string{ColumnInstanceInfo, ColumnServiceID}): 
bson.M{"$in": serviceIDs}}, &options.FindOptions{
                Sort: bson.M{StringBuilder([]string{ColumnInstanceInfo, 
ColumnVersion}): -1}})
        if err != nil {
@@ -2440,7 +2533,6 @@ func instancesFilter(ctx context.Context, serviceIDs 
[]string) ([]*discovery.Mic
        if resp == nil {
                return nil, errors.New("no related instances were found")
        }
-       var instances []*discovery.MicroServiceInstance
        for resp.Next(ctx) {
                var instance Instance
                err := resp.Decode(&instance)
@@ -2455,6 +2547,9 @@ func instancesFilter(ctx context.Context, serviceIDs 
[]string) ([]*discovery.Mic
 func filterServiceIDs(ctx context.Context, consumerID string, tags []string, 
services []*Service) []string {
        var filterService []*Service
        var serviceIDs []string
+       if len(services) == 0 {
+               return serviceIDs
+       }
        filterService = tagsFilter(services, tags)
        filterService = accessibleFilter(ctx, consumerID, filterService)
        for _, service := range filterService {
@@ -2464,6 +2559,9 @@ func filterServiceIDs(ctx context.Context, consumerID 
string, tags []string, ser
 }
 
 func tagsFilter(services []*Service, tags []string) []*Service {
+       if len(tags) == 0 {
+               return services
+       }
        var newServices []*Service
        for _, service := range services {
                index := 0
@@ -2541,9 +2639,9 @@ func latestServicesFilter(ctx context.Context, filter 
bson.M) ([]*Service, error
 
 func getTags(ctx context.Context, domain string, project string, serviceID 
string) (tags map[string]string, err error) {
        filter := bson.M{
-               ColumnDomain:    domain,
-               ColumnProject:   project,
-               ColumnServiceID: serviceID,
+               ColumnDomain:  domain,
+               ColumnProject: project,
+               StringBuilder([]string{ColumnServiceInfo, ColumnServiceID}): 
serviceID,
        }
        result, err := client.GetMongoClient().FindOne(ctx, CollectionService, 
filter)
        if err != nil {
@@ -2714,22 +2812,23 @@ func getRulesUtil(ctx context.Context, domain string, 
project string, serviceID
                ColumnProject:   project,
                ColumnServiceID: serviceID,
        }
-       resp, err := client.GetMongoClient().Find(ctx, CollectionRule, filter)
+       cursor, err := client.GetMongoClient().Find(ctx, CollectionRule, filter)
        if err != nil {
                return nil, err
        }
-       if resp.Err() != nil {
-               return nil, resp.Err()
+       if cursor.Err() != nil {
+               return nil, cursor.Err()
        }
        var rules []*Rule
-       for resp.Next(ctx) {
-               var rule *Rule
-               err := resp.Decode(rule)
+       defer cursor.Close(ctx)
+       for cursor.Next(ctx) {
+               var rule Rule
+               err := cursor.Decode(&rule)
                if err != nil {
                        log.Error("type conversion error", err)
                        return nil, err
                }
-               rules = append(rules, rule)
+               rules = append(rules, &rule)
        }
        return rules, nil
 }
@@ -2846,7 +2945,7 @@ func GetInstancesByServiceID(ctx context.Context, 
serviceID string) ([]*discover
                }
                res = append(res, inst.InstanceInfo)
        }
-       if cacheUnavailable {
+       if cacheUnavailable || len(res) == 0 {
                res, err := instancesFilter(ctx, []string{serviceID})
                if err != nil {
                        return nil, err
@@ -2855,3 +2954,19 @@ func GetInstancesByServiceID(ctx context.Context, 
serviceID string) ([]*discover
        }
        return res, nil
 }
+
+func formatRevision(consumerServiceId string, instances 
[]*discovery.MicroServiceInstance) (string, error) {
+       if instances == nil {
+               return fmt.Sprintf("%x", 
sha1.Sum(util.StringToBytesWithNoCopy(consumerServiceId))), nil
+       }
+       copyInstance := make([]*discovery.MicroServiceInstance, len(instances))
+       copy(copyInstance, instances)
+       sort.Sort(InstanceSlice(copyInstance))
+       data, err := json.Marshal(copyInstance)
+       if err != nil {
+               log.Error("fail to marshal instance json", err)
+               return "", err
+       }
+       s := fmt.Sprintf("%s.%x", consumerServiceId, sha1.Sum(data))
+       return fmt.Sprintf("%x", sha1.Sum(util.StringToBytesWithNoCopy(s))), nil
+}
diff --git a/datasource/mongo/ms_test.go b/datasource/mongo/ms_test.go
index 7fcf983..c9b05fb 100644
--- a/datasource/mongo/ms_test.go
+++ b/datasource/mongo/ms_test.go
@@ -154,7 +154,7 @@ func TestService_Register(t *testing.T) {
                })
                assert.NotNil(t, resp)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
                // serviceName: some-relay-ms-service-name
                // alias: sr1-ms-service-name
@@ -175,7 +175,7 @@ func TestService_Register(t *testing.T) {
                })
                assert.NotNil(t, resp)
                assert.NoError(t, err)
-               assert.Equal(t, pb.ErrServiceAlreadyExists, 
resp.Response.GetCode())
+               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
                // serviceName: some-relay-ms-service-name
                // alias: sr1-ms-service-name
@@ -2364,6 +2364,14 @@ func TestInstance_HeartBeat(t *testing.T) {
                assert.NoError(t, err)
                assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
 
+               log.Info("serviceId does not exist")
+               resp, err = datasource.Instance().Heartbeat(getContext(), 
&pb.HeartbeatRequest{
+                       ServiceId:  "100000000000",
+                       InstanceId: instanceID1,
+               })
+               assert.NoError(t, err)
+               assert.NotEqual(t, pb.ResponseSuccess, resp.Response.GetCode())
+
                log.Info("instance does not exist")
                resp, err = datasource.Instance().Heartbeat(getContext(), 
&pb.HeartbeatRequest{
                        ServiceId:  serviceID,
@@ -2839,8 +2847,8 @@ func TestInstance_Query(t *testing.T) {
 
                respFind, err = 
datasource.Instance().FindInstances(getContext(), &pb.FindInstancesRequest{
                        ConsumerServiceId: serviceID1,
-                       AppId:             "query_instance_ms",
-                       ServiceName:       "query_instance_service_ms",
+                       AppId:             "query_instance",
+                       ServiceName:       "query_instance_service",
                        VersionRule:       "0.0.0",
                })
                assert.NoError(t, err)
diff --git a/datasource/mongo/role.go b/datasource/mongo/role.go
index 1ef36ee..b6c913c 100644
--- a/datasource/mongo/role.go
+++ b/datasource/mongo/role.go
@@ -20,29 +20,117 @@ package mongo
 import (
        "context"
 
+       "go.mongodb.org/mongo-driver/bson"
+
+       "github.com/apache/servicecomb-service-center/datasource"
+       "github.com/apache/servicecomb-service-center/datasource/mongo/client"
+       "github.com/apache/servicecomb-service-center/pkg/log"
        "github.com/apache/servicecomb-service-center/pkg/rbacframe"
+       "github.com/apache/servicecomb-service-center/pkg/util"
 )
 
 func (ds *DataSource) CreateRole(ctx context.Context, r *rbacframe.Role) error 
{
+       exist, err := ds.RoleExist(ctx, r.Name)
+       if err != nil {
+               log.Error("failed to query role", err)
+               return err
+       }
+       if exist {
+               return datasource.ErrRoleDuplicated
+       }
+       r.ID = util.GenerateUUID()
+       _, err = client.GetMongoClient().Insert(ctx, CollectionRole, r)
+       if err != nil {
+               if client.IsDuplicateKey(err) {
+                       return datasource.ErrRoleDuplicated
+               }
+               return err
+       }
+       log.Info("create new role: " + r.ID)
        return nil
 }
 
 func (ds *DataSource) RoleExist(ctx context.Context, name string) (bool, 
error) {
-       return false, nil
+       filter := bson.M{
+               ColumnRoleName: name,
+       }
+       count, err := client.GetMongoClient().Count(ctx, CollectionRole, filter)
+       if err != nil {
+               return false, err
+       }
+       if count == 0 {
+               return false, nil
+       }
+       return true, nil
 }
 
 func (ds *DataSource) GetRole(ctx context.Context, name string) 
(*rbacframe.Role, error) {
-       return &rbacframe.Role{}, nil
+       filter := bson.M{
+               ColumnRoleName: name,
+       }
+       result, err := client.GetMongoClient().FindOne(ctx, CollectionRole, 
filter)
+       if err != nil {
+               return nil, err
+       }
+       if result.Err() != nil {
+               return nil, client.ErrNoDocuments
+       }
+       var role rbacframe.Role
+       err = result.Decode(&role)
+       if err != nil {
+               log.Error("Decode role failed: ", err)
+               return nil, err
+       }
+       return &role, nil
 }
 
 func (ds *DataSource) ListRole(ctx context.Context) ([]*rbacframe.Role, int64, 
error) {
-       return nil, 0, nil
+       cursor, err := client.GetMongoClient().Find(ctx, CollectionRole, 
bson.M{})
+       if err != nil {
+               return nil, 0, err
+       }
+       var roles []*rbacframe.Role
+       defer cursor.Close(ctx)
+       for cursor.Next(ctx) {
+               var role rbacframe.Role
+               err = cursor.Decode(&role)
+               if err != nil {
+                       log.Error("decode role failed: ", err)
+                       continue
+               }
+               roles = append(roles, &role)
+       }
+       return roles, int64(len(roles)), nil
 }
 
 func (ds *DataSource) DeleteRole(ctx context.Context, name string) (bool, 
error) {
-       return false, nil
+       filter := bson.M{
+               ColumnRoleName: name,
+       }
+       result, err := client.GetMongoClient().Delete(ctx, CollectionRole, 
filter)
+       if err != nil {
+               return false, err
+       }
+       if result.DeletedCount == 0 {
+               return false, nil
+       }
+       return true, nil
 }
 
 func (ds *DataSource) UpdateRole(ctx context.Context, name string, role 
*rbacframe.Role) error {
+       filter := bson.M{
+               ColumnRoleName: name,
+       }
+       update := bson.M{
+               "$set": bson.M{
+                       ColumnID:       role.ID,
+                       ColumnRoleName: role.Name,
+                       ColumnPerms:    role.Perms,
+               },
+       }
+       _, err := client.GetMongoClient().Update(ctx, CollectionRole, filter, 
update)
+       if err != nil {
+               return err
+       }
        return nil
 }
diff --git a/datasource/mongo/role_test.go b/datasource/mongo/role_test.go
new file mode 100644
index 0000000..c77940e
--- /dev/null
+++ b/datasource/mongo/role_test.go
@@ -0,0 +1,90 @@
+/*
+ * 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 mongo_test
+
+import (
+       "context"
+       "testing"
+
+       "github.com/astaxie/beego"
+       "github.com/stretchr/testify/assert"
+
+       "github.com/apache/servicecomb-service-center/datasource"
+       "github.com/apache/servicecomb-service-center/pkg/rbacframe"
+)
+
+var (
+       r1 = rbacframe.Role{
+               ID:    "11111-22222-33333",
+               Name:  "test-role1",
+               Perms: nil,
+       }
+
+       r2 = rbacframe.Role{
+               ID:    "11111-22222-33333-44444",
+               Name:  "test-role2",
+               Perms: nil,
+       }
+)
+
+func init() {
+       beego.AppConfig.Set("registry_plugin", "mongo")
+}
+
+func TestRole(t *testing.T) {
+       t.Run("create role should success", func(t *testing.T) {
+               err := datasource.Instance().CreateRole(context.Background(), 
&r1)
+               assert.NoError(t, err)
+               r, err := datasource.Instance().GetRole(context.Background(), 
"test-role1")
+               assert.NoError(t, err)
+               assert.Equal(t, r1, *r)
+       })
+       t.Run("role should exist", func(t *testing.T) {
+               exist, err := 
datasource.Instance().RoleExist(context.Background(), "test-role1")
+               assert.NoError(t, err)
+               assert.True(t, exist)
+       })
+
+       t.Run("repeated create role should failed", func(t *testing.T) {
+               err := datasource.Instance().CreateRole(context.Background(), 
&r1)
+               assert.Error(t, err)
+       })
+
+       t.Run("update role should success", func(t *testing.T) {
+               r1.ID = "11111-22222-33333-4"
+               err := datasource.Instance().UpdateRole(context.Background(), 
"test-role1", &r1)
+               assert.NoError(t, err)
+       })
+       t.Run("add new role should success", func(t *testing.T) {
+               err := datasource.Instance().CreateRole(context.Background(), 
&r2)
+               assert.NoError(t, err)
+               _, n, err := 
datasource.Instance().ListRole(context.Background())
+               assert.NoError(t, err)
+               assert.Equal(t, int64(2), n)
+       })
+
+       t.Run("delete role should success", func(t *testing.T) {
+               _, err := 
datasource.Instance().DeleteRole(context.Background(), "test-role1")
+               assert.NoError(t, err)
+               _, err = datasource.Instance().DeleteRole(context.Background(), 
"test-role2")
+               assert.NoError(t, err)
+               _, n, err := 
datasource.Instance().ListRole(context.Background())
+               assert.NoError(t, err)
+               assert.Equal(t, int64(0), n)
+       })
+}
diff --git a/datasource/mongo/util.go b/datasource/mongo/util.go
index b7e045c..cc2179b 100644
--- a/datasource/mongo/util.go
+++ b/datasource/mongo/util.go
@@ -28,6 +28,20 @@ import (
        "go.mongodb.org/mongo-driver/bson"
 )
 
+type InstanceSlice []*pb.MicroServiceInstance
+
+func (s InstanceSlice) Len() int {
+       return len(s)
+}
+
+func (s InstanceSlice) Swap(i, j int) {
+       s[i], s[j] = s[j], s[i]
+}
+
+func (s InstanceSlice) Less(i, j int) bool {
+       return s[i].InstanceId < s[j].InstanceId
+}
+
 func StringBuilder(data []string) string {
        var str strings.Builder
        for index, value := range data {
diff --git a/go.mod b/go.mod
index 0fcc368..e02c5d8 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
        github.com/dgrijalva/jwt-go v3.2.0+incompatible
        github.com/dustin/go-humanize v1.0.0 // indirect
        github.com/ghodss/yaml v1.0.0
-       github.com/go-chassis/cari v0.0.0-20201124050026-32fbf4d53439
+       github.com/go-chassis/cari v0.0.2-0.20210111105320-0bb4211266b7
        github.com/go-chassis/foundation v0.2.2-0.20201208060254-d5e8e5beb1f0
        github.com/go-chassis/go-archaius v1.3.6-0.20201130023516-387922b408d0
        github.com/go-chassis/go-chassis/v2 v2.1.1-0.20201208095114-93feb76fd997
diff --git a/scripts/ut_test_in_docker.sh b/scripts/ut_test_in_docker.sh
index 5d73a59..9924966 100755
--- a/scripts/ut_test_in_docker.sh
+++ b/scripts/ut_test_in_docker.sh
@@ -67,11 +67,7 @@ fi
 
 export TEST_MODE=mongo
 [ $? == 0 ] && ut_for_dir datasource/mongo
-# 由於mongo接口未全部實現先注釋
-#[ $? == 0 ] && ut_for_dir pkg
-#[ $? == 0 ] && ut_for_dir server
-#[ $? == 0 ] && ut_for_dir scctl
-#[ $? == 0 ] && ut_for_dir syncer
+[ $? == 0 ] && ut_for_dir syncer
 ret=$?
 
 if [ ${ret} == 0 ]; then
diff --git a/server/service/rbac/decision_test.go 
b/server/service/rbac/decision_test.go
index 977bb1f..acc9cf4 100644
--- a/server/service/rbac/decision_test.go
+++ b/server/service/rbac/decision_test.go
@@ -19,13 +19,42 @@ package rbac_test
 
 import (
        "context"
+       "io/ioutil"
        "testing"
 
-       "github.com/apache/servicecomb-service-center/server/service/rbac"
+       "github.com/go-chassis/go-archaius"
+       "github.com/go-chassis/go-chassis/v2/security/secret"
        "github.com/stretchr/testify/assert"
+
+       "github.com/apache/servicecomb-service-center/server/service/rbac"
+       "github.com/apache/servicecomb-service-center/server/service/rbac/dao"
 )
 
 func TestAllow(t *testing.T) {
+       err := archaius.Init(archaius.WithMemorySource(), 
archaius.WithENVSource())
+       assert.NoError(t, err)
+
+       pri, pub, err := secret.GenRSAKeyPair(4096)
+       assert.NoError(t, err)
+
+       b, err := secret.RSAPrivate2Bytes(pri)
+       assert.NoError(t, err)
+       ioutil.WriteFile("./private.key", b, 0600)
+       b, err = secret.RSAPublicKey2Bytes(pub)
+       err = ioutil.WriteFile("./rbac.pub", b, 0600)
+       assert.NoError(t, err)
+
+       archaius.Set(rbac.InitPassword, "Complicated_password1")
+
+       dao.DeleteAccount(context.Background(), "root")
+       dao.DeleteAccount(context.Background(), "a")
+       dao.DeleteAccount(context.Background(), "b")
+
+       rbac.Init()
+       a, err := dao.GetAccount(context.Background(), "root")
+       assert.NoError(t, err)
+       assert.Equal(t, "root", a.Name)
+
        t.Run("admin can operate any resource", func(t *testing.T) {
                ok, _ := rbac.Allow(context.TODO(), []string{"admin"}, 
"default", "account", "create")
                assert.True(t, ok)
diff --git a/server/service/rbac/rbac_test.go b/server/service/rbac/rbac_test.go
index 7762f63..93e92d3 100644
--- a/server/service/rbac/rbac_test.go
+++ b/server/service/rbac/rbac_test.go
@@ -129,9 +129,8 @@ func TestInitRBAC(t *testing.T) {
        })
 
        t.Run("delete the not exist role", func(t *testing.T) {
-               r, err := dao.DeleteRole(context.Background(), "tester")
+               _, err := dao.DeleteRole(context.Background(), "tester")
                assert.NoError(t, err)
-               assert.Equal(t, false, r)
        })
 
        t.Run("list exist role", func(t *testing.T) {
@@ -178,6 +177,6 @@ func TestInitRBAC(t *testing.T) {
        t.Run("delete the new role", func(t *testing.T) {
                r, err := dao.DeleteRole(context.Background(), "tester")
                assert.NoError(t, err)
-               assert.Equal(t, false, r)
+               assert.Equal(t, true, r)
        })
 }

Reply via email to