You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@apisix.apache.org by kv...@apache.org on 2020/08/18 14:06:06 UTC

[apisix-control-plane] branch master updated: feat: add diff method and some tests for mem object (#13)

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

kvn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-control-plane.git


The following commit(s) were added to refs/heads/master by this push:
     new 8d2ac23  feat: add diff method and  some tests for mem object (#13)
8d2ac23 is described below

commit 8d2ac23392fa2abe91b9b50ade7e6f3f39b534f2
Author: kv <gx...@163.com>
AuthorDate: Tue Aug 18 22:04:48 2020 +0800

    feat: add diff method and  some tests for mem object (#13)
---
 doc/yaml_struct.md                       |   2 +-
 go.mod                                   |   3 +
 go.sum                                   |  13 +++
 pkg/mem/{plugin.go => diff.go}           |  52 ++++++---
 pkg/mem/diff_test.go                     | 162 ++++++++++++++++++++++++++++
 pkg/mem/gateway.go                       |  19 ++++
 pkg/mem/{plugin.go => mem_suite_test.go} |  27 ++---
 pkg/mem/mem_test.go                      | 180 +++++++++++++++++++++++++++++++
 pkg/mem/model.go                         |   4 +-
 pkg/mem/plugin.go                        |  16 +++
 pkg/mem/route.go                         |  16 +++
 pkg/mem/store.go                         |   4 +-
 pkg/mem/upstream.go                      |  16 +++
 13 files changed, 478 insertions(+), 36 deletions(-)

diff --git a/doc/yaml_struct.md b/doc/yaml_struct.md
index e4588aa..c8ce2fd 100644
--- a/doc/yaml_struct.md
+++ b/doc/yaml_struct.md
@@ -219,7 +219,7 @@ http:
      name:
        exact: "json"
      age:
-       Greater: 18
+       greater: 18
   - uris:
     - prefix: "/"
 
diff --git a/go.mod b/go.mod
index 2f8dc5c..7dcac3d 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,9 @@ require (
 	github.com/hashicorp/go-memdb v1.2.1
 	github.com/onsi/ginkgo v1.14.0
 	github.com/onsi/gomega v1.10.1
+	github.com/sergi/go-diff v1.1.0 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0
+	github.com/yudai/gojsondiff v1.0.0
+	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
 	gopkg.in/yaml.v2 v2.3.0
 )
diff --git a/go.sum b/go.sum
index 4602519..3d9309c 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,5 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -24,6 +25,9 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -34,14 +38,21 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
 github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
+github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
+github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
+github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
+github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
@@ -69,9 +80,11 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
 google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/pkg/mem/plugin.go b/pkg/mem/diff.go
similarity index 53%
copy from pkg/mem/plugin.go
copy to pkg/mem/diff.go
index 253d586..2a1e1d7 100644
--- a/pkg/mem/plugin.go
+++ b/pkg/mem/diff.go
@@ -17,23 +17,45 @@
 
 package mem
 
-type PluginDB struct {
-	Plugins []*Plugin
-}
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/yudai/gojsondiff"
+)
+
+var (
+	differ = gojsondiff.New()
+)
 
-// insert Plugin to memDB
-func (db *PluginDB) Insert() error {
-	txn := DB.Txn(true)
-	defer txn.Abort()
-	for _, r := range db.Plugins {
-		if err := txn.Insert(PluginTable, r); err != nil {
-			return err
-		}
+func HasDiff(a, b interface{}) (bool, error) {
+	aJSON, err := json.Marshal(a)
+	if err != nil {
+		return false, err
+	}
+	bJSON, err := json.Marshal(b)
+	if err != nil {
+		return false, err
+	}
+	if d, err := differ.Compare(aJSON, bJSON); err != nil {
+		return false, err
+	} else {
+		fmt.Println(d.Deltas())
+		return d.Modified(), nil
 	}
-	txn.Commit()
-	return nil
 }
 
-func (g *Plugin) Diff(t MemModel) bool {
-	return true
+func Diff(a, b interface{}) (gojsondiff.Diff, error) {
+	aJSON, err := json.Marshal(a)
+	if err != nil {
+		return nil, err
+	}
+	bJSON, err := json.Marshal(b)
+	if err != nil {
+		return nil, err
+	}
+	if d, err := differ.Compare(aJSON, bJSON); err != nil {
+		return nil, err
+	} else {
+		return d, nil
+	}
 }
diff --git a/pkg/mem/diff_test.go b/pkg/mem/diff_test.go
new file mode 100644
index 0000000..66a24f2
--- /dev/null
+++ b/pkg/mem/diff_test.go
@@ -0,0 +1,162 @@
+/*
+ * 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 mem_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/apache/apisix-control-plane/pkg/mem"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("Diff", func() {
+	Describe("compare with mems", func() {
+		Context("gateway compare", func() {
+			It("keep the same", func() {
+				a := []byte(`{
+						"kind": "Gateway",
+						"name": "g1",
+						"full_name": "Gateway:g1",
+						"servers": [
+							{
+								"hosts": ["www.foo.com", "foo.com"],
+								"port": {
+									"number": 8080,
+									"name": "http",
+									"protocol": "HTTP"
+								}
+							},
+							{
+								"hosts": ["www.foo2.com", "foo2.com"],
+								"port": {
+									"number": 8082,
+									"name": "http2",
+									"protocol": "HTTP"
+								}
+							}
+						]
+					}`)
+				b := []byte(`{
+						"kind": "Gateway",
+						"name": "g1",
+						"full_name": "Gateway:g1",
+						"servers": [
+							{
+								"hosts": ["www.foo.com", "foo.com"],
+								"port": {
+									"number": 8080,
+									"name": "http",
+									"protocol": "HTTP"
+								}
+							},
+							{
+								"hosts": ["www.foo2.com", "foo2.com"],
+								"port": {
+									"number": 8082,
+									"name": "http2",
+									"protocol": "HTTP"
+								}
+							}
+						]
+					}`)
+				var ga *mem.Gateway
+				if err := json.Unmarshal(a, &ga); err != nil {
+					fmt.Println(err)
+				}
+				var gb *mem.Gateway
+				if err := json.Unmarshal(b, &gb); err != nil {
+					fmt.Println(err)
+				}
+
+				if result, err := mem.HasDiff(*ga, *gb); err != nil {
+					fmt.Println(err)
+				} else {
+					Expect(err).NotTo(HaveOccurred())
+					Expect(result).To(Equal(false))
+				}
+			})
+		})
+
+		Context("gateway compare", func() {
+			It("different because of the sort of servers", func() {
+				a := []byte(`{
+						"kind": "Gateway",
+						"name": "g1",
+						"full_name": "Gateway:g1",
+						"servers": [
+							{
+								"hosts": ["www.foo.com", "foo.com"],
+								"port": {
+									"number": 8080,
+									"name": "http",
+									"protocol": "HTTP"
+								}
+							},
+							{
+								"hosts": ["www.foo2.com", "foo2.com"],
+								"port": {
+									"number": 8082,
+									"name": "http2",
+									"protocol": "HTTP"
+								}
+							}
+						]
+					}`)
+				b := []byte(`{
+						"kind": "Gateway",
+						"name": "g1",
+						"full_name": "Gateway:g1",
+						"servers": [
+							{
+								"hosts": ["www.foo2.com", "foo2.com"],
+								"port": {
+									"number": 8082,
+									"name": "http2",
+									"protocol": "HTTP"
+								}
+							},
+							{
+								"hosts": ["www.foo.com", "foo.com"],
+								"port": {
+									"number": 8080,
+									"name": "http",
+									"protocol": "HTTP"
+								}
+							}
+						]
+					}`)
+				var ga *mem.Gateway
+				if err := json.Unmarshal(a, &ga); err != nil {
+					fmt.Println(err)
+				}
+				var gb *mem.Gateway
+				if err := json.Unmarshal(b, &gb); err != nil {
+					fmt.Println(err)
+				}
+
+				if result, err := mem.HasDiff(*ga, *gb); err != nil {
+					fmt.Println(err)
+				} else {
+					Expect(err).NotTo(HaveOccurred())
+					Expect(result).To(Equal(true))
+				}
+			})
+		})
+	})
+})
diff --git a/pkg/mem/gateway.go b/pkg/mem/gateway.go
index aad72e6..f41f3f0 100644
--- a/pkg/mem/gateway.go
+++ b/pkg/mem/gateway.go
@@ -17,12 +17,17 @@
 
 package mem
 
+import (
+	"fmt"
+)
+
 type GatewayDB struct {
 	Gateways []*Gateway
 }
 
 // insert Gateway to memDB
 func (db *GatewayDB) Insert() error {
+	fmt.Println(DB)
 	txn := DB.Txn(true)
 	defer txn.Abort()
 	for _, r := range db.Gateways {
@@ -34,6 +39,20 @@ func (db *GatewayDB) Insert() error {
 	return nil
 }
 
+func (g *Gateway) FindByFullName() (*Gateway, error) {
+	txn := DB.Txn(false)
+	defer txn.Abort()
+	if raw, err := txn.First(GatewayTable, "id", *g.FullName); err != nil {
+		return nil, err
+	} else {
+		if raw != nil {
+			currentGateway := raw.(*Gateway)
+			return currentGateway, nil
+		}
+		return nil, fmt.Errorf("NOT FOUND")
+	}
+}
+
 func (g *Gateway) Diff(t MemModel) bool {
 	return true
 }
diff --git a/pkg/mem/plugin.go b/pkg/mem/mem_suite_test.go
similarity index 69%
copy from pkg/mem/plugin.go
copy to pkg/mem/mem_suite_test.go
index 253d586..d2513c3 100644
--- a/pkg/mem/plugin.go
+++ b/pkg/mem/mem_suite_test.go
@@ -15,25 +15,16 @@
  * limitations under the License.
  */
 
-package mem
+package mem_test
 
-type PluginDB struct {
-	Plugins []*Plugin
-}
+import (
+	"testing"
 
-// insert Plugin to memDB
-func (db *PluginDB) Insert() error {
-	txn := DB.Txn(true)
-	defer txn.Abort()
-	for _, r := range db.Plugins {
-		if err := txn.Insert(PluginTable, r); err != nil {
-			return err
-		}
-	}
-	txn.Commit()
-	return nil
-}
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
 
-func (g *Plugin) Diff(t MemModel) bool {
-	return true
+func TestMem(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Mem Suite")
 }
diff --git a/pkg/mem/mem_test.go b/pkg/mem/mem_test.go
new file mode 100644
index 0000000..d8f7427
--- /dev/null
+++ b/pkg/mem/mem_test.go
@@ -0,0 +1,180 @@
+/*
+ * 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 mem_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/apache/apisix-control-plane/pkg/mem"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("Mem", func() {
+	Describe("mem object store", func() {
+		Context("gateway store", func() {
+			It("inert and find", func() {
+				b := []byte(`{
+					"kind": "Gateway",
+					"name": "g1",
+					"full_name": "Gateway:g1",
+					"servers": [
+						{
+							"hosts": ["www.foo.com", "foo.com"],
+							"port": {
+								"number": 8080,
+								"name": "http",
+								"protocol": "HTTP"
+							}
+						}
+					]
+				}`)
+				var g *mem.Gateway
+				if err := json.Unmarshal(b, &g); err != nil {
+					fmt.Println(err)
+				}
+				gatewayDB := &mem.GatewayDB{Gateways: []*mem.Gateway{g}}
+				if err := gatewayDB.Insert(); err != nil {
+					fmt.Println(err)
+				}
+				if gFromDB, err := g.FindByFullName(); err != nil {
+					fmt.Println(err)
+				} else {
+					Expect(gFromDB).NotTo(Equal(nil))
+					Expect(*gFromDB.FullName).To(Equal("Gateway:g1"))
+				}
+			})
+		})
+		Context("route store", func() {
+			It("inert and find", func() {
+				b := []byte(`{
+					"kind": "Route",
+					"name": "r1",
+					"full_name": "Route:r1",
+					"hosts": ["foo-server", "foo2-com"],
+					"match": [
+					{
+						"args": {
+							"name": {
+								"exact": "user"
+							},
+							"age": {
+								"greater": 18
+							}
+						}
+					},{
+						"uris": [{"prefix": "/"}]
+					}]
+				}`)
+				var g *mem.Route
+				if err := json.Unmarshal(b, &g); err != nil {
+					fmt.Println(err)
+				}
+				routeDB := &mem.RouteDB{Routes: []*mem.Route{g}}
+				if err := routeDB.Insert(); err != nil {
+					fmt.Println(err)
+				}
+				if gFromDB, err := g.FindByFullName(); err != nil {
+					fmt.Println(err)
+				} else {
+					Expect(gFromDB).NotTo(Equal(nil))
+					Expect(*gFromDB.FullName).To(Equal("Route:r1"))
+				}
+			})
+		})
+
+		Context("upstream store", func() {
+			It("inert and find", func() {
+				b := []byte(`{
+					"kind": "Upstream",
+					"name": "u1",
+					"full_name": "Upstream:u1",
+					"host": "foo-server",
+					"group": "foo",
+					"type": "Roundrobin",
+					"nodes": [
+						{"ip": "127.0.0.1", "port": 80, "weight": 100},
+						{"ip": "127.0.0.2", "port": 80, "weight": 100}
+					]
+				}`)
+				var g *mem.Upstream
+				if err := json.Unmarshal(b, &g); err != nil {
+					fmt.Println(err)
+				}
+				upstreamDB := &mem.UpstreamDB{Upstreams: []*mem.Upstream{g}}
+				if err := upstreamDB.Insert(); err != nil {
+					fmt.Println(err)
+				}
+				if gFromDB, err := g.FindByFullName(); err != nil {
+					fmt.Println(err)
+				} else {
+					Expect(gFromDB).NotTo(Equal(nil))
+					Expect(*gFromDB.FullName).To(Equal("Upstream:u1"))
+					Expect(len(gFromDB.Nodes)).To(Equal(2))
+				}
+			})
+		})
+
+		Context("Plugin store", func() {
+			It("inert and find", func() {
+				b := []byte(`{
+					"kind": "Plugin",
+					"name": "p1",
+					"full_name": "Plugin:p1",
+					"selector": {
+						"app": "foo-server"
+					},
+					"sets": [
+						{
+							"name": "proxy-rewrite",
+							"conf": {
+								"uri": "/test/home.html",
+								"scheme": "http",
+								"host": "foo.com",
+								"headers": {
+									"X-Api-Version:": "v1",
+									"X-Api-Engine": "apisix",
+									"X-Api-useless": ""
+								}
+							}
+						},
+						{
+							"name": "prometheus"
+						}
+					]
+				}`)
+				var g *mem.Plugin
+				if err := json.Unmarshal(b, &g); err != nil {
+					fmt.Println(err)
+				}
+				pluginDB := &mem.PluginDB{Plugins: []*mem.Plugin{g}}
+				if err := pluginDB.Insert(); err != nil {
+					fmt.Println(err)
+				}
+				if gFromDB, err := g.FindByFullName(); err != nil {
+					fmt.Println(err)
+				} else {
+					Expect(gFromDB).NotTo(Equal(nil))
+					Expect(*gFromDB.FullName).To(Equal("Plugin:p1"))
+					Expect(len(gFromDB.Sets)).To(Equal(2))
+					Expect(gFromDB.Selector["app"]).To(Equal("foo-server"))
+				}
+			})
+		})
+	})
+})
diff --git a/pkg/mem/model.go b/pkg/mem/model.go
index 93fcc4e..2c62855 100644
--- a/pkg/mem/model.go
+++ b/pkg/mem/model.go
@@ -61,6 +61,8 @@ type Node struct {
 type Plugin struct {
 	ID       *string           `json:"id,omitempty"`
 	Kind     *string           `json:"kind"`
+	Name     *string           `json:"name"`
+	FullName *string           `json:"full_name"`
 	Selector map[string]string `json:"selector"`
 	Sets     []*PluginSet      `json:"sets"`
 }
@@ -80,7 +82,7 @@ type Gateway struct {
 
 type Server struct {
 	Port  *Port    `json:"port,omitempty"`
-	Hosts []string `json:"host,omitempty"`
+	Hosts []string `json:"hosts,omitempty"`
 }
 
 type Port struct {
diff --git a/pkg/mem/plugin.go b/pkg/mem/plugin.go
index 253d586..648a83f 100644
--- a/pkg/mem/plugin.go
+++ b/pkg/mem/plugin.go
@@ -17,6 +17,8 @@
 
 package mem
 
+import "fmt"
+
 type PluginDB struct {
 	Plugins []*Plugin
 }
@@ -34,6 +36,20 @@ func (db *PluginDB) Insert() error {
 	return nil
 }
 
+func (g *Plugin) FindByFullName() (*Plugin, error) {
+	txn := DB.Txn(false)
+	defer txn.Abort()
+	if raw, err := txn.First(PluginTable, "id", *g.FullName); err != nil {
+		return nil, err
+	} else {
+		if raw != nil {
+			current := raw.(*Plugin)
+			return current, nil
+		}
+		return nil, fmt.Errorf("NOT FOUND")
+	}
+}
+
 func (g *Plugin) Diff(t MemModel) bool {
 	return true
 }
diff --git a/pkg/mem/route.go b/pkg/mem/route.go
index 5d94446..f467b19 100644
--- a/pkg/mem/route.go
+++ b/pkg/mem/route.go
@@ -17,6 +17,8 @@
 
 package mem
 
+import "fmt"
+
 type RouteDB struct {
 	Routes []*Route
 }
@@ -34,6 +36,20 @@ func (db *RouteDB) Insert() error {
 	return nil
 }
 
+func (g *Route) FindByFullName() (*Route, error) {
+	txn := DB.Txn(false)
+	defer txn.Abort()
+	if raw, err := txn.First(RouteTable, "id", *g.FullName); err != nil {
+		return nil, err
+	} else {
+		if raw != nil {
+			current := raw.(*Route)
+			return current, nil
+		}
+		return nil, fmt.Errorf("NOT FOUND")
+	}
+}
+
 func (g *Route) Diff(t MemModel) bool {
 	return true
 }
diff --git a/pkg/mem/store.go b/pkg/mem/store.go
index 0760217..8df0482 100644
--- a/pkg/mem/store.go
+++ b/pkg/mem/store.go
@@ -17,7 +17,9 @@
 
 package mem
 
-import "github.com/hashicorp/go-memdb"
+import (
+	"github.com/hashicorp/go-memdb"
+)
 
 var DB *memdb.MemDB
 
diff --git a/pkg/mem/upstream.go b/pkg/mem/upstream.go
index 327e8b1..ba72a72 100644
--- a/pkg/mem/upstream.go
+++ b/pkg/mem/upstream.go
@@ -17,6 +17,8 @@
 
 package mem
 
+import "fmt"
+
 type UpstreamDB struct {
 	Upstreams []*Upstream
 }
@@ -34,6 +36,20 @@ func (db *UpstreamDB) Insert() error {
 	return nil
 }
 
+func (g *Upstream) FindByFullName() (*Upstream, error) {
+	txn := DB.Txn(false)
+	defer txn.Abort()
+	if raw, err := txn.First(UpstreamTable, "id", *g.FullName); err != nil {
+		return nil, err
+	} else {
+		if raw != nil {
+			current := raw.(*Upstream)
+			return current, nil
+		}
+		return nil, fmt.Errorf("NOT FOUND")
+	}
+}
+
 func (g *Upstream) Diff(t MemModel) bool {
 	return true
 }