You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ti...@apache.org on 2020/12/31 06:57:58 UTC

[servicecomb-service-center] branch master updated: [SCD-2133] add new display data interface (#787)

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 e1239d1  [SCD-2133] add new display data interface (#787)
e1239d1 is described below

commit e1239d18cb22e22e7bd8865f81e27209fc6360d9
Author: GuoYL <53...@users.noreply.github.com>
AuthorDate: Thu Dec 31 14:57:48 2020 +0800

    [SCD-2133] add new display data interface (#787)
    
    * [SCD-2133] add new display data interface
    
    * [SCD-2133] validate change / return 400 when error
    
    * [SCD-2133] modify governance create return type
    
    * [SCD-2133] add ut
    
    * [SCD-2133] add all env
    
    * [SCD-2133] modify ut
---
 pkg/gov/governance.go                         |   7 ++
 server/resource/v1/gov_resource.go            |  47 ++++++--
 server/service/gov/config_distributor.go      |  26 +++--
 server/service/gov/config_distributor_test.go |  71 +++++++++++-
 server/service/gov/kie/kie_distributor.go     | 159 ++++++++++++++++++--------
 server/service/gov/kie/validate.go            |  21 ++--
 server/service/gov/mock/mock.go               |  67 +++++++++--
 7 files changed, 312 insertions(+), 86 deletions(-)

diff --git a/pkg/gov/governance.go b/pkg/gov/governance.go
index d2eb4a0..dd8682a 100644
--- a/pkg/gov/governance.go
+++ b/pkg/gov/governance.go
@@ -30,9 +30,16 @@ type GovernancePolicy struct {
 	Selector   Selector `json:"selector,omitempty"`
 }
 
+//DisplayData define display data
+type DisplayData struct {
+	Policies   []*Policy `json:"policies,omitempty"`
+	MatchGroup *Policy   `json:"matchGroup,omitempty"`
+}
+
 //Policy define policy and fault tolerant policy
 type Policy struct {
 	*GovernancePolicy
+	Kind string      `json:"kind,omitempty"`
 	Spec interface{} `json:"spec,omitempty"`
 }
 
diff --git a/server/resource/v1/gov_resource.go b/server/resource/v1/gov_resource.go
index 16694e0..81f1579 100644
--- a/server/resource/v1/gov_resource.go
+++ b/server/resource/v1/gov_resource.go
@@ -37,6 +37,7 @@ const (
 	KindKey        = ":kind"
 	ProjectKey     = ":project"
 	IDKey          = ":id"
+	DisplayKey     = "display"
 )
 
 //Create gov config
@@ -49,9 +50,18 @@ func (t *Governance) Create(w http.ResponseWriter, req *http.Request) {
 		controller.WriteError(w, discovery.ErrInternal, err.Error())
 		return
 	}
-	err = gov.Create(kind, project, body)
+	id, err := gov.Create(kind, project, body)
+	//todo: 错误处理抽函数
 	if err != nil {
 		log.Error("create gov err", err)
+		w.WriteHeader(http.StatusBadRequest)
+		controller.WriteError(w, discovery.ErrInternal, err.Error())
+		return
+	}
+	_, err = w.Write(id)
+	if err != nil {
+		w.WriteHeader(http.StatusBadRequest)
+		log.Error("", err)
 		controller.WriteError(w, discovery.ErrInternal, err.Error())
 		return
 	}
@@ -71,28 +81,39 @@ func (t *Governance) Put(w http.ResponseWriter, req *http.Request) {
 	}
 	err = gov.Update(id, kind, project, body)
 	if err != nil {
-		log.Error("create gov err", err)
+		log.Error("put gov err", err)
+		w.WriteHeader(http.StatusBadRequest)
 		controller.WriteError(w, discovery.ErrInternal, err.Error())
 		return
 	}
 	w.WriteHeader(http.StatusOK)
 }
 
-//List return all gov config
-func (t *Governance) List(w http.ResponseWriter, req *http.Request) {
+//ListOrDisPlay return all gov config
+func (t *Governance) ListOrDisPlay(w http.ResponseWriter, req *http.Request) {
 	kind := req.URL.Query().Get(KindKey)
 	project := req.URL.Query().Get(ProjectKey)
 	app := req.URL.Query().Get(AppKey)
 	environment := req.URL.Query().Get(EnvironmentKey)
-	body, err := gov.List(kind, project, app, environment)
+	var body []byte
+	var err error
+	if kind == DisplayKey {
+		body, err = gov.Display(project, app, environment)
+	} else {
+		body, err = gov.List(kind, project, app, environment)
+	}
 	if err != nil {
-		log.Error("create gov err", err)
+		log.Error("list gov err", err)
+		w.WriteHeader(http.StatusBadRequest)
 		controller.WriteError(w, discovery.ErrInternal, err.Error())
 		return
 	}
 	_, err = w.Write(body)
 	if err != nil {
+		w.WriteHeader(http.StatusBadRequest)
 		log.Error("", err)
+		controller.WriteError(w, discovery.ErrInternal, err.Error())
+		return
 	}
 	w.WriteHeader(http.StatusOK)
 	w.Header().Set(rest.HeaderContentType, rest.ContentTypeJSON)
@@ -100,17 +121,22 @@ func (t *Governance) List(w http.ResponseWriter, req *http.Request) {
 
 //Get gov config
 func (t *Governance) Get(w http.ResponseWriter, req *http.Request) {
+	kind := req.URL.Query().Get(KindKey)
 	id := req.URL.Query().Get(IDKey)
 	project := req.URL.Query().Get(ProjectKey)
-	body, err := gov.Get(id, project)
+	body, err := gov.Get(kind, id, project)
 	if err != nil {
-		log.Error("create gov err", err)
+		w.WriteHeader(http.StatusBadRequest)
+		log.Error("get gov err", err)
 		controller.WriteError(w, discovery.ErrInternal, err.Error())
 		return
 	}
 	_, err = w.Write(body)
 	if err != nil {
+		w.WriteHeader(http.StatusBadRequest)
 		log.Error("", err)
+		controller.WriteError(w, discovery.ErrInternal, err.Error())
+		return
 	}
 	w.WriteHeader(http.StatusOK)
 	w.Header().Set(rest.HeaderContentType, rest.ContentTypeJSON)
@@ -122,7 +148,8 @@ func (t *Governance) Delete(w http.ResponseWriter, req *http.Request) {
 	project := req.URL.Query().Get(ProjectKey)
 	err := gov.Delete(id, project)
 	if err != nil {
-		log.Error("create gov err", err)
+		w.WriteHeader(http.StatusBadRequest)
+		log.Error("delete gov err", err)
 		controller.WriteError(w, discovery.ErrInternal, err.Error())
 		return
 	}
@@ -135,7 +162,7 @@ func (t *Governance) URLPatterns() []rest.Route {
 		//servicecomb.rateLimiter.{name}
 		//....
 		{Method: http.MethodPost, Path: "/v1/:project/gov/" + KindKey, Func: t.Create},
-		{Method: http.MethodGet, Path: "/v1/:project/gov/" + KindKey, Func: t.List},
+		{Method: http.MethodGet, Path: "/v1/:project/gov/" + KindKey, Func: t.ListOrDisPlay},
 		{Method: http.MethodGet, Path: "/v1/:project/gov/" + KindKey + "/" + IDKey, Func: t.Get},
 		{Method: http.MethodPut, Path: "/v1/:project/gov/" + KindKey + "/" + IDKey, Func: t.Put},
 		{Method: http.MethodDelete, Path: "/v1/:project/gov/" + KindKey + "/" + IDKey, Func: t.Delete},
diff --git a/server/service/gov/config_distributor.go b/server/service/gov/config_distributor.go
index c1c4224..45d4c7e 100644
--- a/server/service/gov/config_distributor.go
+++ b/server/service/gov/config_distributor.go
@@ -38,11 +38,12 @@ var distributorPlugins = map[string]NewDistributors{}
 //or service mesh system like istio, linkerd.
 //ConfigDistributor will convert standard servicecomb gov config to concrete spec, that data plane can recognize.
 type ConfigDistributor interface {
-	Create(kind, project string, spec []byte) error
+	Create(kind, project string, spec []byte) ([]byte, error)
 	Update(id, kind, project string, spec []byte) error
 	Delete(id, project string) error
+	Display(project, app, env string) ([]byte, error)
 	List(kind, project, app, env string) ([]byte, error)
-	Get(id, project string) ([]byte, error)
+	Get(kind, id, project string) ([]byte, error)
 	Type() string
 	Name() string
 }
@@ -74,15 +75,11 @@ func Init() error {
 	return nil
 }
 
-func Create(kind, project string, spec []byte) error {
-	var err error
+func Create(kind, project string, spec []byte) ([]byte, error) {
 	for _, cd := range distributors {
-		err = cd.Create(kind, project, spec)
-		if err != nil {
-			return err
-		}
+		return cd.Create(kind, project, spec)
 	}
-	return nil
+	return nil, nil
 }
 
 func List(kind, project, app, env string) ([]byte, error) {
@@ -92,9 +89,16 @@ func List(kind, project, app, env string) ([]byte, error) {
 	return nil, nil
 }
 
-func Get(id, project string) ([]byte, error) {
+func Display(project, app, env string) ([]byte, error) {
+	for _, cd := range distributors {
+		return cd.Display(project, app, env)
+	}
+	return nil, nil
+}
+
+func Get(kind, id, project string) ([]byte, error) {
 	for _, cd := range distributors {
-		return cd.Get(id, project)
+		return cd.Get(kind, id, project)
 	}
 	return nil, nil
 }
diff --git a/server/service/gov/config_distributor_test.go b/server/service/gov/config_distributor_test.go
index fbb0d73..a5a38df 100644
--- a/server/service/gov/config_distributor_test.go
+++ b/server/service/gov/config_distributor_test.go
@@ -28,7 +28,15 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestCreate(t *testing.T) {
+const Project = "default"
+const MockKind = "default"
+const MatchGroup = "match-group"
+const MockEnv = ""
+const MockApp = ""
+
+var id = ""
+
+func init() {
 	config.Configurations = &config.Config{
 		Gov: &config.Gov{
 			DistOptions: []config.DistributorOptions{
@@ -40,13 +48,72 @@ func TestCreate(t *testing.T) {
 		},
 	}
 	err := svc.Init()
+	if err != nil {
+		panic(err)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	b, _ := json.MarshalIndent(&gov.Policy{
+		GovernancePolicy: &gov.GovernancePolicy{
+			Name: "Traffic2adminAPI",
+		},
+		Spec: &gov.LBSpec{RetryNext: 3, MarkerName: "traffic2adminAPI"},
+	}, "", "  ")
+	res, err := svc.Create(MockKind, Project, b)
+	id = string(res)
 	assert.NoError(t, err)
+}
+
+func TestUpdate(t *testing.T) {
 	b, _ := json.MarshalIndent(&gov.Policy{
 		GovernancePolicy: &gov.GovernancePolicy{
 			Name: "Traffic2adminAPI",
 		},
 		Spec: &gov.LBSpec{RetryNext: 3, MarkerName: "traffic2adminAPI"},
 	}, "", "  ")
-	err = svc.Create("lb", "default", b)
+	err := svc.Update(id, MockKind, Project, b)
+	assert.NoError(t, err)
+}
+
+func TestDisplay(t *testing.T) {
+	b, _ := json.MarshalIndent(&gov.Policy{
+		GovernancePolicy: &gov.GovernancePolicy{
+			Name: "Traffic2adminAPI",
+		},
+	}, "", "  ")
+	res, err := svc.Create(MatchGroup, Project, b)
+	id = string(res)
+	assert.NoError(t, err)
+	policies := &[]*gov.DisplayData{}
+	res, err = svc.Display(Project, MockApp, MockEnv)
+	assert.NoError(t, err)
+	err = json.Unmarshal(res, policies)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, policies)
+}
+
+func TestList(t *testing.T) {
+	policies := &[]*gov.Policy{}
+	res, err := svc.List(MockKind, Project, MockApp, MockEnv)
+	assert.NoError(t, err)
+	err = json.Unmarshal(res, policies)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, policies)
+}
+
+func TestGet(t *testing.T) {
+	policy := &gov.Policy{}
+	res, err := svc.Get(MockKind, id, Project)
+	assert.NoError(t, err)
+	err = json.Unmarshal(res, policy)
+	assert.NoError(t, err)
+	assert.NotNil(t, policy)
+}
+
+func TestDelete(t *testing.T) {
+	err := svc.Delete(id, Project)
 	assert.NoError(t, err)
+	res, _ := svc.Get(MockKind, id, Project)
+	assert.Nil(t, res)
 }
diff --git a/server/service/gov/kie/kie_distributor.go b/server/service/gov/kie/kie_distributor.go
index df43e31..0fc5bd0 100644
--- a/server/service/gov/kie/kie_distributor.go
+++ b/server/service/gov/kie/kie_distributor.go
@@ -5,9 +5,10 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"log"
 	"strings"
 
+	"github.com/apache/servicecomb-service-center/pkg/log"
+
 	"github.com/apache/servicecomb-service-center/pkg/gov"
 	"github.com/apache/servicecomb-service-center/server/config"
 	svc "github.com/apache/servicecomb-service-center/server/service/gov"
@@ -23,29 +24,33 @@ type Distributor struct {
 
 const (
 	PREFIX         = "servicecomb."
+	MatchGroup     = "match-group"
 	EnableStatus   = "enabled"
 	ValueType      = "text"
 	AppKey         = "app"
 	EnvironmentKey = "environment"
+	EnvAll         = "all"
 )
 
+var PolicyNames = []string{"retry", "rateLimiting", "circuitBreaker", "bulkhead"}
+
 var rule = Validator{}
 
-func (d *Distributor) Create(kind, project string, spec []byte) error {
+func (d *Distributor) Create(kind, project string, spec []byte) ([]byte, error) {
 	p := &gov.Policy{}
 	err := json.Unmarshal(spec, p)
 	if err != nil {
-		return err
+		return nil, err
 	}
-	log.Println(fmt.Sprintf("create %v", &p))
+	log.Info(fmt.Sprintf("create %v", &p))
 	key := toSnake(kind) + "." + p.Name
 	err = rule.Validate(kind, p.Spec)
 	if err != nil {
-		return err
+		return nil, err
 	}
 	yamlByte, err := yaml.Marshal(p.Spec)
 	if err != nil {
-		return err
+		return nil, err
 	}
 	kv := kie.KVRequest{
 		Key:       PREFIX + key,
@@ -54,13 +59,14 @@ func (d *Distributor) Create(kind, project string, spec []byte) error {
 		ValueType: ValueType,
 		Labels:    map[string]string{AppKey: p.Selector.App, EnvironmentKey: p.Selector.Environment},
 	}
-	_, err = d.client.Create(context.TODO(), kv, kie.WithProject(project))
+	res, err := d.client.Create(context.TODO(), kv, kie.WithProject(project))
 	if err != nil {
-		log.Fatal("kie create failed", err)
-		return err
+		log.Error("kie create failed", err)
+		return nil, err
 	}
 	d.lbPolicies[p.GovernancePolicy.Name] = p
-	return nil
+	b, _ := json.MarshalIndent(res.ID, "", "  ")
+	return b, nil
 }
 
 func (d *Distributor) Update(id, kind, project string, spec []byte) error {
@@ -69,7 +75,7 @@ func (d *Distributor) Update(id, kind, project string, spec []byte) error {
 	if err != nil {
 		return err
 	}
-	log.Println(fmt.Sprintf("update %v", &p))
+	log.Info(fmt.Sprintf("update %v", &p))
 	err = rule.Validate(kind, p.Spec)
 	if err != nil {
 		return err
@@ -85,7 +91,7 @@ func (d *Distributor) Update(id, kind, project string, spec []byte) error {
 	}
 	_, err = d.client.Put(context.TODO(), kv, kie.WithProject(project))
 	if err != nil {
-		log.Fatal("kie update failed", err)
+		log.Error("kie update failed", err)
 		return err
 	}
 	d.lbPolicies[p.GovernancePolicy.Name] = p
@@ -95,65 +101,80 @@ func (d *Distributor) Update(id, kind, project string, spec []byte) error {
 func (d *Distributor) Delete(id, project string) error {
 	err := d.client.Delete(context.TODO(), id, kie.WithProject(project))
 	if err != nil {
-		log.Fatal("kie delete failed", err)
+		log.Error("kie delete failed", err)
 		return err
 	}
 	return nil
 }
 
+func (d *Distributor) Display(project, app, env string) ([]byte, error) {
+	list, _, err := d.listDataByKind(MatchGroup, project, app, env)
+	if err != nil {
+		return nil, err
+	}
+	policyMap := make(map[string]*gov.Policy)
+	for _, kind := range PolicyNames {
+		policies, _, err := d.listDataByKind(kind, project, app, env)
+		if err != nil {
+			continue
+		}
+		for _, policy := range policies.Data {
+			item, err := d.transform(policy, kind)
+			if err != nil {
+				continue
+			}
+			policyMap[item.Name+kind] = item
+		}
+	}
+	r := make([]*gov.DisplayData, 0, list.Total)
+	for _, item := range list.Data {
+		match, err := d.transform(item, MatchGroup)
+		if err != nil {
+			return nil, err
+		}
+		var policies []*gov.Policy
+		for _, kind := range PolicyNames {
+			if policyMap[match.Name+kind] != nil {
+				policies = append(policies, policyMap[match.Name+kind])
+			}
+		}
+		result := &gov.DisplayData{
+			Policies:   policies,
+			MatchGroup: match,
+		}
+		r = append(r, result)
+	}
+	b, _ := json.MarshalIndent(r, "", "  ")
+	return b, nil
+}
+
 func (d *Distributor) List(kind, project, app, env string) ([]byte, error) {
-	list, _, err := d.client.List(context.TODO(),
-		kie.WithKey("beginWith("+PREFIX+toSnake(kind)+")"),
-		kie.WithLabels(map[string]string{AppKey: app, EnvironmentKey: env}),
-		kie.WithRevision(0),
-		kie.WithGetProject(project))
+	list, _, err := d.listDataByKind(kind, project, app, env)
 	if err != nil {
 		return nil, err
 	}
 	r := make([]*gov.Policy, 0, list.Total)
 	for _, item := range list.Data {
-		goc := &gov.Policy{
-			GovernancePolicy: &gov.GovernancePolicy{},
-		}
-		spec := make(map[string]interface{})
-		specJSON, _ := yaml.YAMLToJSON([]byte(item.Value))
-		err = json.Unmarshal(specJSON, &spec)
+		policy, err := d.transform(item, kind)
 		if err != nil {
-			log.Fatal("kie list failed", err)
 			return nil, err
 		}
-		goc.ID = item.ID
-		goc.Status = item.Status
-		goc.Name = item.Key
-		goc.Spec = spec
-		goc.Selector.App = item.Labels[AppKey]
-		goc.Selector.Environment = item.Labels[EnvironmentKey]
-		goc.CreatTime = item.CreatTime
-		goc.UpdateTime = item.UpdateTime
-		r = append(r, goc)
+		r = append(r, policy)
 	}
 	b, _ := json.MarshalIndent(r, "", "  ")
 	return b, nil
 }
 
-func (d *Distributor) Get(id, project string) ([]byte, error) {
+func (d *Distributor) Get(kind, id, project string) ([]byte, error) {
 	kv, err := d.client.Get(context.TODO(), id, kie.WithGetProject(project))
 	if err != nil {
-		log.Fatal("kie get failed", err)
 		return nil, err
 	}
-	goc := &gov.Policy{
-		GovernancePolicy: &gov.GovernancePolicy{},
+	policy, err := d.transform(kv, kind)
+	if err != nil {
+		return nil, err
 	}
-	goc.ID = kv.ID
-	goc.Status = kv.Status
-	goc.Name = kv.Key
-	goc.Spec = kv
-	goc.Selector.App = kv.Labels[AppKey]
-	goc.Selector.Environment = kv.Labels[EnvironmentKey]
-	goc.CreatTime = kv.CreatTime
-	goc.UpdateTime = kv.UpdateTime
-	b, _ := json.MarshalIndent(goc, "", "  ")
+	b, _ := json.MarshalIndent(policy, "", "  ")
 	return b, nil
 }
 
@@ -170,7 +191,7 @@ func initClient(endpoint string) *kie.Client {
 			DefaultLabels: map[string]string{},
 		})
 	if err != nil {
-		log.Fatalf("init kie client failed, err: %s", err)
+		log.Fatal("init kie client failed, err: %s", err)
 	}
 	return client
 }
@@ -201,6 +222,48 @@ func toSnake(name string) string {
 	return buffer.String()
 }
 
+func (d *Distributor) listDataByKind(kind, project, app, env string) (*kie.KVResponse, int, error) {
+	ops := []kie.GetOption{
+		kie.WithKey("beginWith(" + PREFIX + toSnake(kind) + ")"),
+		kie.WithRevision(0),
+		kie.WithGetProject(project),
+	}
+	labels := map[string]string{}
+	if env != EnvAll {
+		labels[EnvironmentKey] = env
+	}
+	if app != "" {
+		labels[AppKey] = app
+	}
+	if len(labels) > 0 {
+		ops = append(ops, kie.WithLabels(labels))
+	}
+	return d.client.List(context.TODO(), ops...)
+}
+
+func (d *Distributor) transform(kv *kie.KVDoc, kind string) (*gov.Policy, error) {
+	goc := &gov.Policy{
+		GovernancePolicy: &gov.GovernancePolicy{},
+	}
+	spec := make(map[string]interface{})
+	specJSON, _ := yaml.YAMLToJSON([]byte(kv.Value))
+	err := json.Unmarshal(specJSON, &spec)
+	if err != nil {
+		log.Fatal("kie transform kv failed", err)
+		return nil, err
+	}
+	goc.Kind = kind
+	goc.ID = kv.ID
+	goc.Status = kv.Status
+	goc.Name = kv.Key[strings.LastIndex(kv.Key, ".")+1 : len(kv.Key)]
+	goc.Spec = spec
+	goc.Selector.App = kv.Labels[AppKey]
+	goc.Selector.Environment = kv.Labels[EnvironmentKey]
+	goc.CreatTime = kv.CreatTime
+	goc.UpdateTime = kv.UpdateTime
+	return goc, nil
+}
+
 func init() {
 	svc.InstallDistributor(svc.ConfigDistributorKie, new)
 }
diff --git a/server/service/gov/kie/validate.go b/server/service/gov/kie/validate.go
index fb9f0b4..bafe794 100644
--- a/server/service/gov/kie/validate.go
+++ b/server/service/gov/kie/validate.go
@@ -15,9 +15,9 @@ func (d *Validator) Validate(kind string, spec interface{}) error {
 		return matchValidate(spec)
 	case "retry":
 		return retryValidate(spec)
-	case "rateLimiting":
+	case "rate-limiting":
 		return rateLimitingValidate(spec)
-	case "circuitBreaker":
+	case "circuit-breaker":
 	case "bulkhead":
 	case "loadbalancer":
 		return nil
@@ -32,6 +32,9 @@ func matchValidate(val interface{}) error {
 	if !ok {
 		return fmt.Errorf("illegal item : %v", val)
 	}
+	if spec["matches"] == nil {
+		return nil
+	}
 	matches, ok := spec["matches"].([]interface{})
 	if !ok {
 		return fmt.Errorf("illegal item : %v", spec)
@@ -88,12 +91,14 @@ func policyValidate(val interface{}) error {
 	if !ok {
 		return fmt.Errorf("illegal item : %v", val)
 	}
-	rules, ok := spec["rules"].(map[string]interface{})
-	if !ok {
-		return fmt.Errorf("illegal item : %v", spec)
-	}
-	if "" == rules["match"] {
-		return fmt.Errorf("policy's match can not be nil: %v", spec)
+	if spec["rules"] != nil {
+		rules, ok := spec["rules"].(map[string]interface{})
+		if !ok {
+			return fmt.Errorf("illegal item : %v", spec)
+		}
+		if "" == rules["match"] {
+			return fmt.Errorf("policy's match can not be nil: %v", spec)
+		}
 	}
 	return nil
 }
diff --git a/server/service/gov/mock/mock.go b/server/service/gov/mock/mock.go
index cc6359c..0ee948f 100644
--- a/server/service/gov/mock/mock.go
+++ b/server/service/gov/mock/mock.go
@@ -22,6 +22,8 @@ import (
 	"fmt"
 	"log"
 
+	uuid "github.com/satori/go.uuid"
+
 	"github.com/apache/servicecomb-service-center/pkg/gov"
 	"github.com/apache/servicecomb-service-center/server/config"
 	svc "github.com/apache/servicecomb-service-center/server/service/gov"
@@ -32,36 +34,87 @@ type Distributor struct {
 	name       string
 }
 
-func (d *Distributor) Create(kind, project string, spec []byte) error {
+const MatchGroup = "match-group"
+
+var PolicyNames = []string{"retry", "rateLimiting", "circuitBreaker", "bulkhead"}
+
+func (d *Distributor) Create(kind, project string, spec []byte) ([]byte, error) {
 	p := &gov.Policy{}
 	err := json.Unmarshal(spec, p)
+	p.ID = uuid.NewV4().String()
+	p.Kind = kind
 	log.Println(fmt.Sprintf("create %v", &p))
-	d.lbPolicies[p.GovernancePolicy.Name] = p
-	return err
+	d.lbPolicies[p.GovernancePolicy.ID] = p
+	return []byte(p.ID), err
 }
+
 func (d *Distributor) Update(id, kind, project string, spec []byte) error {
+	if d.lbPolicies[id] == nil {
+		return fmt.Errorf("id not exsit")
+	}
 	p := &gov.Policy{}
 	err := json.Unmarshal(spec, p)
+	p.ID = id
+	p.Kind = kind
 	log.Println("update ", p)
-	d.lbPolicies[p.GovernancePolicy.Name] = p
+	d.lbPolicies[p.GovernancePolicy.ID] = p
 	return err
 }
+
 func (d *Distributor) Delete(id, project string) error {
 	delete(d.lbPolicies, id)
 	return nil
 }
+
+func (d *Distributor) Display(project, app, env string) ([]byte, error) {
+	list := make([]*gov.Policy, 0)
+	for _, g := range d.lbPolicies {
+		if g.Kind == MatchGroup && g.Selector.App == app && g.Selector.Environment == env {
+			list = append(list, g)
+		}
+	}
+	policyMap := make(map[string]*gov.Policy)
+	for _, g := range d.lbPolicies {
+		for _, kind := range PolicyNames {
+			if g.Kind == kind && g.Selector.App == app && g.Selector.Environment == env {
+				policyMap[g.Name+kind] = g
+			}
+		}
+	}
+	r := make([]*gov.DisplayData, 0, len(list))
+	for _, g := range list {
+		policies := make([]*gov.Policy, 0)
+		for _, kind := range PolicyNames {
+			policies = append(policies, policyMap[g.Name+kind])
+		}
+		r = append(r, &gov.DisplayData{
+			MatchGroup: g,
+			Policies:   policies,
+		})
+	}
+	b, _ := json.MarshalIndent(r, "", "  ")
+	return b, nil
+}
 func (d *Distributor) List(kind, project, app, env string) ([]byte, error) {
 	r := make([]*gov.Policy, 0, len(d.lbPolicies))
 	for _, g := range d.lbPolicies {
-		r = append(r, g)
+		if g.Kind == kind && g.Selector.App == app && g.Selector.Environment == env {
+			r = append(r, g)
+		}
 	}
 	b, _ := json.MarshalIndent(r, "", "  ")
 	return b, nil
 }
 
-func (d *Distributor) Get(id, project string) ([]byte, error) {
-	return nil, nil
+func (d *Distributor) Get(kind, id, project string) ([]byte, error) {
+	r := d.lbPolicies[id]
+	if r == nil {
+		return nil, nil
+	}
+	b, _ := json.MarshalIndent(r, "", "  ")
+	return b, nil
 }
+
 func (d *Distributor) Type() string {
 	return svc.ConfigDistributorMock
 }