You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by as...@apache.org on 2019/06/27 11:53:18 UTC

[servicecomb-kie] 07/29: add rest api for put and find, (#3)

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

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

commit 78341650e6617d4fd97df6feec86c0918f21772b
Author: Shawn <xi...@gmail.com>
AuthorDate: Mon May 13 10:00:45 2019 +0800

    add rest api for put and find, (#3)
    
    add example for local dev
---
 .gitignore                                         |   4 +-
 README.md                                          |   6 +-
 cmd/kie/cmd.go                                     |  63 ++++++
 server/kv/model_suite_test.go => cmd/kie/main.go   |  43 ++--
 examples/dev/REAMDME.md                            |  27 +++
 examples/dev/conf/chassis.yaml                     |  24 ++
 examples/dev/conf/microservice.yaml                |   4 +
 examples/dev/docker-compose.yaml                   |  35 +++
 examples/dev/kie-conf.yaml                         |   7 +
 go.mod                                             |   5 +-
 server/{kv => dao}/errors.go                       |   2 +-
 server/{kv => dao}/kv.go                           |   9 +-
 server/{kv => dao}/kv_test.go                      |  30 +--
 server/{kv => dao}/model_suite_test.go             |   2 +-
 server/{kv => dao}/mongodb.go                      |   8 +-
 server/{kv => dao}/options.go                      |  18 +-
 .../noop_auth_handler.go}                          |  39 ++--
 server/resource/v1/common.go                       |  60 +++++
 server/{kv/errors.go => resource/v1/doc_struct.go} |  23 +-
 server/resource/v1/kv_resource.go                  | 249 +++++++++++++++++++++
 20 files changed, 563 insertions(+), 95 deletions(-)

diff --git a/.gitignore b/.gitignore
index 901a1e4..fcf40a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,12 +10,10 @@ output
 **/*junit.xml
 **/*.exe
 **/*.tgz
-
 vendor/**
 !vendor/manifest
 
-# for local UT
-**/conf/
+**/conf/servicecomb-kie/
 !etc/conf/
 etc/data/
 etc/ssl/
diff --git a/README.md b/README.md
index 244cf63..c427b6a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Apache-ServiceComb-Kie
 
-A service for key value management in distributed system.
+A service for configuration management in distributed system.
 
 ## Conceptions
 
@@ -63,6 +63,10 @@ to a distributed system in separated views.
 - kv change history: all kv changes is recorded and can be easily roll back by UI
 ## Quick Start
 
+
+## Development
+To see how to build a local dev environment, check [here](examples/dev)
+
 ## Contact
 
 Bugs: [issues](https://issues.apache.org/jira/browse/SCB)
diff --git a/cmd/kie/cmd.go b/cmd/kie/cmd.go
new file mode 100644
index 0000000..45ffb60
--- /dev/null
+++ b/cmd/kie/cmd.go
@@ -0,0 +1,63 @@
+/*
+ * 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 main
+
+import (
+	"os"
+
+	"github.com/urfave/cli"
+)
+
+const (
+	defaultConfigFile = "/etc/servicecomb-kie/kie-conf.yaml"
+)
+
+//ConfigFromCmd store cmd params
+type ConfigFromCmd struct {
+	ConfigFile string
+}
+
+//Configs is a pointer of struct ConfigFromCmd
+var Configs *ConfigFromCmd
+
+// parseConfigFromCmd
+func parseConfigFromCmd(args []string) (err error) {
+	app := cli.NewApp()
+	app.HideVersion = true
+	app.Usage = "servicecomb-kie server cmd line."
+	app.Flags = []cli.Flag{
+		cli.StringFlag{
+			Name:        "config",
+			Usage:       "config file, example: --config=kie-conf.yaml",
+			Destination: &Configs.ConfigFile,
+			Value:       defaultConfigFile,
+		},
+	}
+	app.Action = func(c *cli.Context) error {
+		return nil
+	}
+
+	err = app.Run(args)
+	return
+}
+
+//Init get config and parses those command
+func Init() error {
+	Configs = &ConfigFromCmd{}
+	return parseConfigFromCmd(os.Args)
+}
diff --git a/server/kv/model_suite_test.go b/cmd/kie/main.go
similarity index 56%
copy from server/kv/model_suite_test.go
copy to cmd/kie/main.go
index 965802e..a70ab9e 100644
--- a/server/kv/model_suite_test.go
+++ b/cmd/kie/main.go
@@ -15,30 +15,33 @@
  * limitations under the License.
  */
 
-package kv_test
+package main
 
 import (
-	"testing"
+	_ "github.com/apache/servicecomb-kie/server/handler"
 
-	"github.com/go-chassis/paas-lager"
+	"github.com/apache/servicecomb-kie/server/config"
+	"github.com/apache/servicecomb-kie/server/resource/v1"
+	"github.com/go-chassis/go-chassis"
 	"github.com/go-mesh/openlogging"
-	. "github.com/onsi/ginkgo"
-	"github.com/onsi/ginkgo/reporters"
-	. "github.com/onsi/gomega"
+	"os"
 )
 
-func TestModel(t *testing.T) {
-	RegisterFailHandler(Fail)
-	junitReporter := reporters.NewJUnitReporter("junit.xml")
-	RunSpecsWithDefaultAndCustomReporters(t, "Model Suite", []Reporter{junitReporter})
+func main() {
+	if err := Init(); err != nil {
+		openlogging.Fatal(err.Error())
+	}
+	chassis.RegisterSchema("rest", &v1.KVResource{})
+	if err := chassis.Init(); err != nil {
+		openlogging.Error(err.Error())
+		os.Exit(1)
+	}
+	if err := config.Init(Configs.ConfigFile); err != nil {
+		openlogging.Error(err.Error())
+		os.Exit(1)
+	}
+	if err := chassis.Run(); err != nil {
+		openlogging.Error("service exit: " + err.Error())
+		os.Exit(1)
+	}
 }
-
-var _ = BeforeSuite(func() {
-	log.Init(log.Config{
-		Writers:     []string{"stdout"},
-		LoggerLevel: "DEBUG",
-	})
-
-	logger := log.NewLogger("ut")
-	openlogging.SetLogger(logger)
-})
diff --git a/examples/dev/REAMDME.md b/examples/dev/REAMDME.md
new file mode 100644
index 0000000..5b770d1
--- /dev/null
+++ b/examples/dev/REAMDME.md
@@ -0,0 +1,27 @@
+# Intro
+that is a simple example to run kie in you local machine
+
+you only need to set up a mongodb and config credential 
+and related info in kie-conf.yaml
+
+you can setup a simple mongodb alone with admin UI by docker compose 
+
+# Get started
+
+1.Build 
+```bash
+cd examples/dev
+go build github.com/apache/servicecomb-kie/cmd/kie
+```
+
+2.Run mongodb and servicecomb-kie
+```bash
+sudo docker-compose up
+./kie --config kie-conf.yaml
+```
+
+3. check service API document
+```bash
+cd examples/dev/conf/servicecomb-kie/schema
+```
+you can copy it to https://editor.swagger.io/ to see full API document
diff --git a/examples/dev/conf/chassis.yaml b/examples/dev/conf/chassis.yaml
new file mode 100755
index 0000000..d3d4c4b
--- /dev/null
+++ b/examples/dev/conf/chassis.yaml
@@ -0,0 +1,24 @@
+---
+cse:
+  service:
+    registry:
+      disabled: true
+      address: http://127.0.0.1:30100
+  protocols:
+    rest:
+      listenAddress: 127.0.0.1:30108
+    rest-consul: #consul compatible API
+      listenAddress: 127.0.0.1:8500
+  handler:
+    chain:
+      Provider:
+        default: auth-handler,ratelimiter-provider
+# ssl:
+#   Provider.cipherPlugin: default
+#   Provider.verifyPeer: false
+#   Provider.cipherSuits: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+#   Provider.protocol: TLSv1.2
+#   Provider.caFile:
+#   Provider.certFile:
+#   Provider.keyFile:
+#   Provider.certPwdFile:
diff --git a/examples/dev/conf/microservice.yaml b/examples/dev/conf/microservice.yaml
new file mode 100755
index 0000000..38aad04
--- /dev/null
+++ b/examples/dev/conf/microservice.yaml
@@ -0,0 +1,4 @@
+---
+service_description:
+  name: servicecomb-kie
+  version: 0.0.1
diff --git a/examples/dev/docker-compose.yaml b/examples/dev/docker-compose.yaml
new file mode 100644
index 0000000..700fa28
--- /dev/null
+++ b/examples/dev/docker-compose.yaml
@@ -0,0 +1,35 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+version: '3.1'
+services:
+  mongo:
+    image: mongo
+    restart: always
+    ports:
+      - 27017:27017
+    environment:
+      MONGO_INITDB_ROOT_USERNAME: kie
+      MONGO_INITDB_ROOT_PASSWORD: 123
+
+  mongo-express:
+    image: mongo-express
+    restart: always
+    ports:
+      - 8081:8081
+    environment:
+      ME_CONFIG_MONGODB_ADMINUSERNAME: kie
+      ME_CONFIG_MONGODB_ADMINPASSWORD: 123
\ No newline at end of file
diff --git a/examples/dev/kie-conf.yaml b/examples/dev/kie-conf.yaml
new file mode 100644
index 0000000..5de5ee0
--- /dev/null
+++ b/examples/dev/kie-conf.yaml
@@ -0,0 +1,7 @@
+db:
+  uri: mongodb://admin:123@127.0.0.1:27017/kie
+  type: mongodb
+  poolSize: 10
+  ssl: false
+  sslCA:
+  sslCert:
\ No newline at end of file
diff --git a/go.mod b/go.mod
index da3d3eb..368d3a8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,14 +1,15 @@
 module github.com/apache/servicecomb-kie
 
 require (
+	github.com/emicklei/go-restful v2.8.0+incompatible
 	github.com/go-chassis/go-archaius v0.14.0
-	github.com/go-chassis/go-chassis v1.4.0 // indirect
+	github.com/go-chassis/go-chassis v1.4.0
 	github.com/go-chassis/paas-lager v1.0.2-0.20190328010332-cf506050ddb2
 	github.com/go-mesh/openlogging v1.0.1-0.20181205082104-3d418c478b2d
 	github.com/onsi/ginkgo v1.8.0
 	github.com/onsi/gomega v1.5.0
 	github.com/stretchr/testify v1.2.2
-	github.com/urfave/cli v1.20.0 // indirect
+	github.com/urfave/cli v1.20.0
 	github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
 	github.com/xdg/stringprep v1.0.0 // indirect
 	go.mongodb.org/mongo-driver v1.0.0
diff --git a/server/kv/errors.go b/server/dao/errors.go
similarity index 99%
copy from server/kv/errors.go
copy to server/dao/errors.go
index 958a015..31074f8 100644
--- a/server/kv/errors.go
+++ b/server/dao/errors.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package kv
+package dao
 
 import (
 	"errors"
diff --git a/server/kv/kv.go b/server/dao/kv.go
similarity index 91%
rename from server/kv/kv.go
rename to server/dao/kv.go
index 9513e60..de32cc3 100644
--- a/server/kv/kv.go
+++ b/server/dao/kv.go
@@ -15,7 +15,8 @@
  * limitations under the License.
  */
 
-package kv
+//package dao is a persis layer of kie
+package dao
 
 import (
 	"crypto/tls"
@@ -30,13 +31,13 @@ var ErrNotExists = errors.New("key with labels does not exits")
 var ErrTooMany = errors.New("key with labels should be only one")
 var ErrKeyMustNotEmpty = errors.New("must supply key if you want to get exact one result")
 
-type Service interface {
+type KV interface {
 	CreateOrUpdate(kv *model.KV) (*model.KV, error)
 	//do not use primitive.ObjectID as return to decouple with mongodb, we can afford perf lost
 	Exist(key, domain string, labels model.Labels) (string, error)
 	DeleteByID(id string) error
 	Delete(key, domain string, labels model.Labels) error
-	Find(domain string, options ...CallOption) ([]*model.KV, error)
+	Find(domain string, options ...FindOption) ([]*model.KV, error)
 	AddHistory(kv *model.KV) error
 	//RollBack(kv *KV, version string) error
 }
@@ -49,7 +50,7 @@ type Options struct {
 	Timeout  time.Duration
 }
 
-func NewKVService() (Service, error) {
+func NewKVService() (KV, error) {
 	opts := Options{
 		URI:      config.GetDB().URI,
 		PoolSize: config.GetDB().PoolSize,
diff --git a/server/kv/kv_test.go b/server/dao/kv_test.go
similarity index 86%
rename from server/kv/kv_test.go
rename to server/dao/kv_test.go
index 4bdd043..9cb23ad 100644
--- a/server/kv/kv_test.go
+++ b/server/dao/kv_test.go
@@ -15,21 +15,21 @@
  * limitations under the License.
  */
 
-package kv_test
+package dao_test
 
 import (
+	"github.com/apache/servicecomb-kie/pkg/model"
 	. "github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/apache/servicecomb-kie/server/dao"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	"github.com/apache/servicecomb-kie/pkg/model"
-	"github.com/apache/servicecomb-kie/server/kv"
 )
 
 var _ = Describe("Kv mongodb service", func() {
-	var s kv.Service
+	var s dao.KV
 	var err error
 	Describe("connecting db", func() {
-		s, err = kv.NewMongoService(kv.Options{
+		s, err = dao.NewMongoService(dao.Options{
 			URI: "mongodb://kie:123@127.0.0.1:27017",
 		})
 		It("should not return err", func() {
@@ -100,9 +100,9 @@ var _ = Describe("Kv mongodb service", func() {
 			It("should not return err", func() {
 				Expect(err).Should(BeNil())
 			})
-			kvs1, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{
+			kvs1, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{
 				"app": "mall",
-			}), kv.WithExactLabels())
+			}), dao.WithExactLabels())
 			It("should be 1s", func() {
 				Expect(kvs1[0].Value).Should(Equal(beforeKV.Value))
 			})
@@ -123,9 +123,9 @@ var _ = Describe("Kv mongodb service", func() {
 			It("should exists", func() {
 				Expect(oid).Should(Equal(beforeKV.ID.Hex()))
 			})
-			kvs, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{
+			kvs, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{
 				"app": "mall",
-			}), kv.WithExactLabels())
+			}), dao.WithExactLabels())
 			It("should be 3s", func() {
 				Expect(kvs[0].Value).Should(Equal(afterKV.Value))
 			})
@@ -134,7 +134,7 @@ var _ = Describe("Kv mongodb service", func() {
 
 	Describe("greedy find by kv and labels", func() {
 		Context("with labels app ", func() {
-			kvs, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{
+			kvs, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{
 				"app": "mall",
 			}))
 			It("should not return err", func() {
@@ -148,9 +148,9 @@ var _ = Describe("Kv mongodb service", func() {
 	})
 	Describe("exact find by kv and labels", func() {
 		Context("with labels app ", func() {
-			kvs, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{
+			kvs, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{
 				"app": "mall",
-			}), kv.WithExactLabels())
+			}), dao.WithExactLabels())
 			It("should not return err", func() {
 				Expect(err).Should(BeNil())
 			})
@@ -162,9 +162,9 @@ var _ = Describe("Kv mongodb service", func() {
 	})
 	Describe("exact find by labels", func() {
 		Context("with labels app ", func() {
-			kvs, err := s.Find("default", kv.WithLabels(map[string]string{
+			kvs, err := s.Find("default", dao.WithLabels(map[string]string{
 				"app": "mall",
-			}), kv.WithExactLabels())
+			}), dao.WithExactLabels())
 			It("should not return err", func() {
 				Expect(err).Should(BeNil())
 			})
@@ -176,7 +176,7 @@ var _ = Describe("Kv mongodb service", func() {
 	})
 	Describe("greedy find by labels", func() {
 		Context("with labels app ans service ", func() {
-			kvs, err := s.Find("default", kv.WithLabels(map[string]string{
+			kvs, err := s.Find("default", dao.WithLabels(map[string]string{
 				"app":     "mall",
 				"service": "cart",
 			}))
diff --git a/server/kv/model_suite_test.go b/server/dao/model_suite_test.go
similarity index 98%
copy from server/kv/model_suite_test.go
copy to server/dao/model_suite_test.go
index 965802e..a57389e 100644
--- a/server/kv/model_suite_test.go
+++ b/server/dao/model_suite_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package kv_test
+package dao_test
 
 import (
 	"testing"
diff --git a/server/kv/mongodb.go b/server/dao/mongodb.go
similarity index 98%
rename from server/kv/mongodb.go
rename to server/dao/mongodb.go
index 37664ae..cbaabad 100644
--- a/server/kv/mongodb.go
+++ b/server/dao/mongodb.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package kv
+package dao
 
 import (
 	"context"
@@ -141,8 +141,8 @@ func (s *MongodbService) Exist(key, domain string, labels model.Labels) (string,
 //Find get kvs by key, labels
 //because labels has a a lot of combination,
 //you can use WithExactLabels to return only one kv which's labels exactly match the criteria
-func (s *MongodbService) Find(domain string, options ...CallOption) ([]*model.KV, error) {
-	opts := CallOptions{}
+func (s *MongodbService) Find(domain string, options ...FindOption) ([]*model.KV, error) {
+	opts := FindOptions{}
 	for _, o := range options {
 		o(&opts)
 	}
@@ -271,7 +271,7 @@ func (s *MongodbService) getLatest(id primitive.ObjectID) (*model.KVHistory, err
 	}
 	return h, nil
 }
-func NewMongoService(opts Options) (Service, error) {
+func NewMongoService(opts Options) (KV, error) {
 	if opts.Timeout == 0 {
 		opts.Timeout = DefaultTimeout
 	}
diff --git a/server/kv/options.go b/server/dao/options.go
similarity index 79%
rename from server/kv/options.go
rename to server/dao/options.go
index aabe1ec..2116b67 100644
--- a/server/kv/options.go
+++ b/server/dao/options.go
@@ -15,35 +15,35 @@
  * limitations under the License.
  */
 
-package kv
+package dao
 
 import "github.com/apache/servicecomb-kie/pkg/model"
 
-type CallOptions struct {
+type FindOptions struct {
 	ExactLabels bool
 	Key         string
 	Labels      model.Labels
 }
 
-type CallOption func(*CallOptions)
+type FindOption func(*FindOptions)
 
 //WithExactLabels tell model service to return only one kv matches the labels
-func WithExactLabels() CallOption {
-	return func(o *CallOptions) {
+func WithExactLabels() FindOption {
+	return func(o *FindOptions) {
 		o.ExactLabels = true
 	}
 }
 
 //WithKey find by key
-func WithKey(key string) CallOption {
-	return func(o *CallOptions) {
+func WithKey(key string) FindOption {
+	return func(o *FindOptions) {
 		o.Key = key
 	}
 }
 
 //WithLabels find kv by labels
-func WithLabels(labels model.Labels) CallOption {
-	return func(o *CallOptions) {
+func WithLabels(labels model.Labels) FindOption {
+	return func(o *FindOptions) {
 		o.Labels = labels
 	}
 }
diff --git a/server/kv/model_suite_test.go b/server/handler/noop_auth_handler.go
similarity index 54%
rename from server/kv/model_suite_test.go
rename to server/handler/noop_auth_handler.go
index 965802e..b2c4a20 100644
--- a/server/kv/model_suite_test.go
+++ b/server/handler/noop_auth_handler.go
@@ -15,30 +15,29 @@
  * limitations under the License.
  */
 
-package kv_test
+package handler
 
 import (
-	"testing"
-
-	"github.com/go-chassis/paas-lager"
-	"github.com/go-mesh/openlogging"
-	. "github.com/onsi/ginkgo"
-	"github.com/onsi/ginkgo/reporters"
-	. "github.com/onsi/gomega"
+	"github.com/go-chassis/go-chassis/core/handler"
+	"github.com/go-chassis/go-chassis/core/invocation"
 )
 
-func TestModel(t *testing.T) {
-	RegisterFailHandler(Fail)
-	junitReporter := reporters.NewJUnitReporter("junit.xml")
-	RunSpecsWithDefaultAndCustomReporters(t, "Model Suite", []Reporter{junitReporter})
+//NoopAuthHandler not need implement any logic
+//developer can extend authenticate and authorization by set new handler in chassis.yaml
+type NoopAuthHandler struct{}
+
+func (bk *NoopAuthHandler) Handle(chain *handler.Chain, inv *invocation.Invocation, cb invocation.ResponseCallBack) {
+	inv.SetMetadata("domain", "default")
+	chain.Next(inv, cb)
 }
 
-var _ = BeforeSuite(func() {
-	log.Init(log.Config{
-		Writers:     []string{"stdout"},
-		LoggerLevel: "DEBUG",
-	})
+func newDomainResolver() handler.Handler {
+	return &NoopAuthHandler{}
+}
 
-	logger := log.NewLogger("ut")
-	openlogging.SetLogger(logger)
-})
+func (bk *NoopAuthHandler) Name() string {
+	return "auth-handler"
+}
+func init() {
+	handler.RegisterHandler("auth-handler", newDomainResolver)
+}
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
new file mode 100644
index 0000000..5d7bceb
--- /dev/null
+++ b/server/resource/v1/common.go
@@ -0,0 +1,60 @@
+/*
+ * 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 v1
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/go-chassis/go-chassis/server/restful"
+	"github.com/go-mesh/openlogging"
+)
+
+const (
+	FindExact               = "exact"
+	FindMany                = "greedy"
+	MsgDomainMustNotBeEmpty = "domain must not be empty"
+	MsgIllegalFindPolicy    = "value of header X-Find can be greedy or exact"
+	MsgIllegalLabels        = "label's value can not be empty, " +
+		"label can not be duplicated, please check your query parameters"
+)
+
+func ReadDomain(context *restful.Context) interface{} {
+	return context.ReadRestfulRequest().Attribute("domain")
+}
+func ReadFindPolicy(context *restful.Context) string {
+	policy := context.ReadRestfulRequest().HeaderParameter("X-Find")
+	if policy == "" {
+		return FindMany
+	}
+	return policy
+}
+func WriteErrResponse(context *restful.Context, status int, msg string) {
+	context.WriteHeader(status)
+	b, _ := json.MarshalIndent(&ErrorMsg{Msg: msg}, "", " ")
+	context.Write(b)
+}
+
+func ErrLog(action string, kv *model.KV, err error) {
+	openlogging.Error(fmt.Sprintf("[%s] [%v] err:%s", action, kv, err.Error()))
+}
+
+func InfoLog(action string, kv *model.KV) {
+	openlogging.Info(
+		fmt.Sprintf("[%s] [%s:%s] in [%s] success", action, kv.Key, kv.Value, kv.Domain))
+}
diff --git a/server/kv/errors.go b/server/resource/v1/doc_struct.go
similarity index 61%
rename from server/kv/errors.go
rename to server/resource/v1/doc_struct.go
index 958a015..a0402ae 100644
--- a/server/kv/errors.go
+++ b/server/resource/v1/doc_struct.go
@@ -15,21 +15,14 @@
  * limitations under the License.
  */
 
-package kv
+package v1
 
-import (
-	"errors"
-	"fmt"
-
-	"github.com/apache/servicecomb-kie/pkg/model"
-	"github.com/go-mesh/openlogging"
-)
-
-//ErrAction will wrap raw error to biz error and return
-//it record audit log for mongodb operation failure like find, insert, update, deletion
-func ErrAction(action, key string, labels model.Labels, domain string, err error) error {
-	msg := fmt.Sprintf("can not [%s] [%s] in [%s] with [%s],err: %s", action, key, domain, labels, err.Error())
-	openlogging.Error(msg)
-	return errors.New(msg)
+type KVBody struct {
+	Labels    map[string]string `json:"labels"`
+	ValueType string            `json:"valueType"`
+	Value     string            `json:"value"`
+}
 
+type ErrorMsg struct {
+	Msg string `json:"msg"`
 }
diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go
new file mode 100644
index 0000000..7a9d66d
--- /dev/null
+++ b/server/resource/v1/kv_resource.go
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+//v1 package hold http rest v1 API
+package v1
+
+import (
+	"encoding/json"
+	"github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/apache/servicecomb-kie/server/dao"
+	goRestful "github.com/emicklei/go-restful"
+	"github.com/go-chassis/go-chassis/server/restful"
+	"github.com/go-mesh/openlogging"
+	"net/http"
+)
+
+type KVResource struct {
+}
+
+func (r *KVResource) Put(context *restful.Context) {
+	var err error
+	key := context.ReadPathParameter("key")
+	kv := new(model.KV)
+	decoder := json.NewDecoder(context.ReadRequest().Body)
+	if err = decoder.Decode(kv); err != nil {
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
+		return
+	}
+	domain := ReadDomain(context)
+	if domain == nil {
+		WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty)
+	}
+	kv.Key = key
+	kv.Domain = domain.(string)
+	s, err := dao.NewKVService()
+	if err != nil {
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
+		return
+	}
+	kv, err = s.CreateOrUpdate(kv)
+	if err != nil {
+		ErrLog("put", kv, err)
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
+		return
+	}
+	InfoLog("put", kv)
+	context.WriteHeader(http.StatusOK)
+	context.WriteHeaderAndJSON(http.StatusOK, kv, goRestful.MIME_JSON)
+
+}
+func (r *KVResource) Find(context *restful.Context) {
+	var err error
+	key := context.ReadPathParameter("key")
+	if key == "" {
+		WriteErrResponse(context, http.StatusForbidden, "key must not be empty")
+		return
+	}
+	values := context.ReadRequest().URL.Query()
+	labels := make(map[string]string, len(values))
+	for k, v := range values {
+		if len(v) != 1 {
+			WriteErrResponse(context, http.StatusBadRequest, MsgIllegalLabels)
+			return
+		}
+		labels[k] = v[0]
+	}
+	s, err := dao.NewKVService()
+	if err != nil {
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
+		return
+	}
+	domain := ReadDomain(context)
+	if domain == nil {
+		WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty)
+		return
+	}
+	policy := ReadFindPolicy(context)
+	var kvs []*model.KV
+	switch policy {
+	case FindMany:
+		kvs, err = s.Find(domain.(string), dao.WithKey(key), dao.WithLabels(labels))
+	case FindExact:
+		kvs, err = s.Find(domain.(string), dao.WithKey(key), dao.WithLabels(labels),
+			dao.WithExactLabels())
+	default:
+		WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy)
+		return
+	}
+	if err != nil {
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
+		return
+	}
+	err = context.WriteHeaderAndJSON(http.StatusOK, kvs, goRestful.MIME_JSON)
+	if err != nil {
+		openlogging.Error(err.Error())
+	}
+
+}
+func (r *KVResource) FindByLabels(context *restful.Context) {
+	var err error
+	values := context.ReadRequest().URL.Query()
+	labels := make(map[string]string, len(values))
+	for k, v := range values {
+		if len(v) != 1 {
+			WriteErrResponse(context, http.StatusBadRequest, MsgIllegalLabels)
+			return
+		}
+		labels[k] = v[0]
+	}
+	s, err := dao.NewKVService()
+	if err != nil {
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error())
+		return
+	}
+	domain := ReadDomain(context)
+	if domain == nil {
+		WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty)
+		return
+	}
+	policy := ReadFindPolicy(context)
+	var kvs []*model.KV
+	switch policy {
+	case FindMany:
+		kvs, err = s.Find(domain.(string), dao.WithLabels(labels))
+	case FindExact:
+		kvs, err = s.Find(domain.(string), dao.WithLabels(labels),
+			dao.WithExactLabels())
+	default:
+		WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy)
+		return
+	}
+	err = context.WriteHeaderAndJSON(http.StatusOK, kvs, goRestful.MIME_JSON)
+	if err != nil {
+		openlogging.Error(err.Error())
+	}
+
+}
+func (r *KVResource) Delete(context *restful.Context) {
+
+}
+
+//URLPatterns defined config operations
+func (r *KVResource) URLPatterns() []restful.Route {
+	return []restful.Route{
+		{
+			Method:           http.MethodPut,
+			Path:             "/v1/kv/{key}",
+			ResourceFuncName: "Put",
+			FuncDesc:         "create or update key value",
+			Parameters: []*restful.Parameters{
+				{
+					DataType:  "string",
+					Name:      "key",
+					ParamType: goRestful.PathParameterKind,
+				}, {
+					DataType:  "string",
+					Name:      "X-Domain-Name",
+					ParamType: goRestful.HeaderParameterKind,
+					Desc:      "set kv to other tenant",
+				}, {
+					DataType:  "string",
+					Name:      "X-Realm",
+					ParamType: goRestful.HeaderParameterKind,
+					Desc:      "set kv to heterogeneous config server",
+				},
+			},
+			Returns: []*restful.Returns{
+				{
+					Code:    http.StatusOK,
+					Message: "true",
+				},
+			},
+			Consumes: []string{"application/json"},
+			Produces: []string{"application/json"},
+			Read:     &KVBody{},
+		}, {
+			Method:           http.MethodGet,
+			Path:             "/v1/kv/{key}",
+			ResourceFuncName: "Find",
+			FuncDesc:         "get key values by key and labels",
+			Parameters: []*restful.Parameters{
+				{
+					DataType:  "string",
+					Name:      "key",
+					ParamType: goRestful.PathParameterKind,
+				}, {
+					DataType:  "string",
+					Name:      "X-Domain-Name",
+					ParamType: goRestful.HeaderParameterKind,
+				}, {
+					DataType:  "string",
+					Name:      "X-Find",
+					ParamType: goRestful.HeaderParameterKind,
+					Desc:      "greedy or exact",
+				},
+			},
+			Returns: []*restful.Returns{
+				{
+					Code:    http.StatusOK,
+					Message: "get key value success",
+					Model:   []*KVBody{},
+				},
+			},
+			Consumes: []string{"application/json"},
+			Produces: []string{"application/json"},
+			Read:     &KVBody{},
+		}, {
+			Method:           http.MethodGet,
+			Path:             "/v1/kv",
+			ResourceFuncName: "FindByLabels",
+			FuncDesc:         "find key values only by labels",
+			Parameters: []*restful.Parameters{
+				{
+					DataType:  "string",
+					Name:      "X-Domain-Name",
+					ParamType: goRestful.HeaderParameterKind,
+				}, {
+					DataType:  "string",
+					Name:      "X-Find",
+					ParamType: goRestful.HeaderParameterKind,
+					Desc:      "greedy or exact",
+				},
+			},
+			Returns: []*restful.Returns{
+				{
+					Code:    http.StatusOK,
+					Message: "get key value success",
+					Model:   []*KVBody{},
+				},
+			},
+			Consumes: []string{"application/json"},
+			Produces: []string{"application/json"},
+		},
+	}
+}