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:36 UTC

[servicecomb-kie] 25/29: SCB-1312 clear go lint (#12)

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 6e9a59f0495e7938e330ad225891745f5ddef024
Author: Shawn <xi...@gmail.com>
AuthorDate: Tue Jun 18 20:51:39 2019 +0800

    SCB-1312 clear go lint (#12)
---
 .travis.yml                            |  12 +--
 build/build_server.sh                  |   9 +-
 client/client.go                       |   9 +-
 client/options.go                      |   3 +
 cmd/{kie/cmd.go => kieserver/main.go}  |  25 +++++-
 cmd/main.go                            |  48 -----------
 pkg/common/common.go                   |   7 +-
 pkg/model/kv.go                        |   7 ++
 pkg/model/mongodb_doc.go               |   9 +-
 scripts/start.sh                       |   2 +-
 scripts/travis/start_deps.sh           |   4 +-
 server/config/config.go                |   2 +
 server/config/struct.go                |   3 +
 server/dao/kie_api.go                  | 148 ++++++++++++++-------------------
 server/dao/kv.go                       |  47 ++++++++---
 server/dao/label.go                    |   2 +
 server/dao/label_history.go            |   2 +
 server/dao/options.go                  |   4 +-
 server/dao/tool.go                     |  40 ++++++++-
 server/handler/noop_auth_handler.go    |   2 +
 server/resource/v1/common.go           |  14 +++-
 server/resource/v1/doc_struct.go       |  29 +++++++
 server/resource/v1/history_resource.go |   1 +
 server/resource/v1/kv_resource.go      |  66 +++++----------
 24 files changed, 276 insertions(+), 219 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 6e4794f..a5361b4 100755
--- a/.travis.yml
+++ b/.travis.yml
@@ -51,14 +51,10 @@ jobs:
         - bash scripts/travis/goCycloChecker.sh
     - stage: Unit Test
       script:
+        - GO111MODULE=on go mod download
+        - GO111MODULE=on go mod vendor
         - bash scripts/travis/start_deps.sh
+        - cd $HOME/gopath/src/github.com/apache/servicecomb-kie
         - go get github.com/mattn/goveralls
         - go get golang.org/x/tools/cmd/cover
-        - GO111MODULE=on go mod download
-        - GO111MODULE=on go mod vendor
-        - bash scripts/travis/unit_test.sh && $HOME/gopath/bin/goveralls -coverprofile=coverage.txt -service=travis-ci
-
-    - stage: Build
-      script:
-        - cd build
-        - ./build_server.sh
+        - bash scripts/travis/unit_test.sh
diff --git a/build/build_server.sh b/build/build_server.sh
index 2ffe85b..099f64c 100755
--- a/build/build_server.sh
+++ b/build/build_server.sh
@@ -1,4 +1,5 @@
 #!/usr/bin/env bash
+set -x
 # 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.
@@ -46,8 +47,7 @@ echo "build from ${GIT_COMMIT}"
 
 
 echo "building..."
-go build -o ${release_dir}/kie github.com/apache/servicecomb-kie/cmd/kie
-
+go build -o ${release_dir}/kie github.com/apache/servicecomb-kie/cmd/kieserver
 
 writeConfig(){
 echo "write template config..."
@@ -95,5 +95,6 @@ tar zcf ${pkg_name} conf kie
 
 echo "building docker..."
 cp ${PROJECT_DIR}/scripts/start.sh ./
-
-sudo docker build -t servicecomb/kie:${version} -f ${PROJECT_DIR}/build/docker/server/Dockerfile .
\ No newline at end of file
+cp ${PROJECT_DIR}/build/docker/server/Dockerfile ./
+sudo docker version
+sudo docker build -t servicecomb/kie:${version} .
\ No newline at end of file
diff --git a/client/client.go b/client/client.go
index 3bcf161..f9df929 100644
--- a/client/client.go
+++ b/client/client.go
@@ -33,25 +33,32 @@ import (
 	"net/url"
 )
 
+//const
 const (
 	APIPathKV = "v1/kv"
 )
 
+//client errors
 var (
 	ErrKeyNotExist = errors.New("can not find value")
 )
 
+//Client is the servicecomb kie rest client.
+//it is concurrency safe
 type Client struct {
 	opts   Config
 	cipher security.Cipher
 	c      *httpclient.URLClient
 }
+
+//Config is the config of client
 type Config struct {
 	Endpoint      string
 	DefaultLabels map[string]string
 	VerifyPeer    bool //TODO make it works, now just keep it false
 }
 
+//New create a client
 func New(config Config) (*Client, error) {
 	u, err := url.Parse(config.Endpoint)
 	if err != nil {
@@ -73,7 +80,7 @@ func New(config Config) (*Client, error) {
 	}, nil
 }
 
-//GetValue get value of a key
+//Get get value of a key
 func (c *Client) Get(ctx context.Context, key string, opts ...GetOption) ([]*model.KVDoc, error) {
 	options := GetOptions{}
 	for _, o := range opts {
diff --git a/client/options.go b/client/options.go
index 21f820b..e56b76e 100644
--- a/client/options.go
+++ b/client/options.go
@@ -17,7 +17,10 @@
 
 package client
 
+//GetOption is the functional option of client func
 type GetOption func(*GetOptions)
+
+//GetOptions is the options of client func
 type GetOptions struct {
 	Labels    map[string]string
 	MatchMode string
diff --git a/cmd/kie/cmd.go b/cmd/kieserver/main.go
similarity index 71%
rename from cmd/kie/cmd.go
rename to cmd/kieserver/main.go
index 4ddad70..f77434f 100644
--- a/cmd/kie/cmd.go
+++ b/cmd/kieserver/main.go
@@ -15,11 +15,16 @@
  * limitations under the License.
  */
 
-package kie
+package main
 
 import (
 	"os"
 
+	"github.com/apache/servicecomb-kie/server/config"
+	_ "github.com/apache/servicecomb-kie/server/handler"
+	"github.com/apache/servicecomb-kie/server/resource/v1"
+	"github.com/go-chassis/go-chassis"
+	"github.com/go-mesh/openlogging"
 	"github.com/urfave/cli"
 )
 
@@ -61,3 +66,21 @@ func Init() error {
 	Configs = &ConfigFromCmd{}
 	return parseConfigFromCmd(os.Args)
 }
+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)
+	}
+}
diff --git a/cmd/main.go b/cmd/main.go
deleted file mode 100644
index 4ce2316..0000000
--- a/cmd/main.go
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 (
-	"github.com/apache/servicecomb-kie/cmd/kie"
-	_ "github.com/apache/servicecomb-kie/server/handler"
-
-	"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"
-	"os"
-)
-
-func main() {
-	if err := kie.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(kie.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)
-	}
-}
diff --git a/pkg/common/common.go b/pkg/common/common.go
index 345086a..e39dca6 100644
--- a/pkg/common/common.go
+++ b/pkg/common/common.go
@@ -17,12 +17,15 @@
 
 package common
 
+//match mode
 const (
 	MatchGreedy = "greedy"
 	MatchExact  = "exact"
 )
 
+//http headers
 const (
-	HeaderMatch = "X-Match"
-	HeaderDepth = "X-Depth"
+	HeaderMatch  = "X-Match"
+	HeaderDepth  = "X-Depth"
+	HeaderTenant = "X-Domain-Name"
 )
diff --git a/pkg/model/kv.go b/pkg/model/kv.go
index 73d33e3..2635f6e 100644
--- a/pkg/model/kv.go
+++ b/pkg/model/kv.go
@@ -17,7 +17,14 @@
 
 package model
 
+//KVResponse represents the key value list
 type KVResponse struct {
 	LabelDoc *LabelDocResponse `json:"label"`
 	Data     []*KVDoc          `json:"data"`
 }
+
+//LabelDocResponse is label struct
+type LabelDocResponse struct {
+	LabelID string            `json:"label_id,omitempty"`
+	Labels  map[string]string `json:"labels,omitempty"`
+}
diff --git a/pkg/model/mongodb_doc.go b/pkg/model/mongodb_doc.go
index 71a9483..ce32c5d 100644
--- a/pkg/model/mongodb_doc.go
+++ b/pkg/model/mongodb_doc.go
@@ -19,16 +19,15 @@ package model
 
 import "go.mongodb.org/mongo-driver/bson/primitive"
 
-type LabelDocResponse struct {
-	LabelID string            `json:"label_id,omitempty"`
-	Labels  map[string]string `json:"labels,omitempty"`
-}
+//LabelDoc is database struct to store labels
 type LabelDoc struct {
 	ID       primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
 	Labels   map[string]string  `json:"labels,omitempty"`
 	Revision int                `json:"revision,omitempty"`
 	Domain   string             `json:"domain,omitempty"` //tenant info
 }
+
+//KVDoc is database struct to store kv
 type KVDoc struct {
 	ID        primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
 	LabelID   string             `json:"label_id,omitempty" bson:"label_id,omitempty"`
@@ -41,6 +40,8 @@ type KVDoc struct {
 	Domain   string            `json:"domain,omitempty"` //redundant
 	Revision int               `json:"revision,omitempty" bson:"-"`
 }
+
+//LabelRevisionDoc is database struct to store label history stats
 type LabelRevisionDoc struct {
 	ID       primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
 	LabelID  string             `json:"label_id,omitempty"  bson:"label_id,omitempty"`
diff --git a/scripts/start.sh b/scripts/start.sh
index 682634c..9abf470 100755
--- a/scripts/start.sh
+++ b/scripts/start.sh
@@ -45,7 +45,7 @@ logger_level: ${LOG_LEVEL}
 
 logger_file: log/chassis.log
 
-log_format_text: true
+log_format_text: false
 
 rollingPolicy: size
 
diff --git a/scripts/travis/start_deps.sh b/scripts/travis/start_deps.sh
index b4587ef..d2236f6 100755
--- a/scripts/travis/start_deps.sh
+++ b/scripts/travis/start_deps.sh
@@ -15,4 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-docker-compose up -f $GOPATH/src/github.com/apache/servicecomb-kie/deployments/docker/docker-compose.yaml
\ No newline at end of file
+cd build
+bash build_server.sh
+sudo docker-compose -f $GOPATH/src/github.com/apache/servicecomb-kie/deployments/docker/docker-compose.yaml up -d
\ No newline at end of file
diff --git a/server/config/config.go b/server/config/config.go
index 7e03731..76571de 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -26,6 +26,7 @@ import (
 
 var configurations *Config
 
+//Init initiate config files
 func Init(file string) error {
 	if err := archaius.AddFile(file, archaius.WithFileHandler(filesource.UseFileNameAsKeyContentAsValue)); err != nil {
 		return err
@@ -39,6 +40,7 @@ func Init(file string) error {
 	return nil
 }
 
+//GetDB return db configs
 func GetDB() DB {
 	return configurations.DB
 }
diff --git a/server/config/struct.go b/server/config/struct.go
index cbfb644..0e2cd55 100644
--- a/server/config/struct.go
+++ b/server/config/struct.go
@@ -17,9 +17,12 @@
 
 package config
 
+//Config is yaml file struct
 type Config struct {
 	DB DB `yaml:"db"`
 }
+
+//DB is yaml file struct to set mongodb config
 type DB struct {
 	URI      string   `yaml:"uri"`
 	PoolSize int      `yaml:"poolSize"`
diff --git a/server/dao/kie_api.go b/server/dao/kie_api.go
index 323d8e7..a5cca3d 100644
--- a/server/dao/kie_api.go
+++ b/server/dao/kie_api.go
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+//Package dao is the data access layer
 package dao
 
 import (
@@ -32,6 +33,7 @@ import (
 
 var client *mongo.Client
 
+//const for dao
 const (
 	DB                      = "kie"
 	CollectionLabel         = "label"
@@ -41,11 +43,17 @@ const (
 	DefaultValueType        = "text"
 )
 
+//MongodbService operate data in mongodb
 type MongodbService struct {
 	c       *mongo.Client
 	timeout time.Duration
 }
 
+//CreateOrUpdate will create or update a key value record
+//it first check label exists or not, and create labels if labels is first posted.
+//if label exists, then get its latest revision, and update current revision,
+//save the current label and its all key values to history collection
+//then check key exists or not, then create or update it
 func (s *MongodbService) CreateOrUpdate(ctx context.Context, domain string, kv *model.KVDoc) (*model.KVDoc, error) {
 	if domain == "" {
 		return nil, ErrMissingDomain
@@ -176,9 +184,8 @@ func (s *MongodbService) FindKVByLabelID(ctx context.Context, domain, labelID, k
 	filter := bson.M{"label_id": labelID, "domain": domain}
 	if key != "" {
 		return s.findOneKey(ctx, filter, key)
-	} else {
-		return s.findKeys(ctx, filter, true)
 	}
+	return s.findKeys(ctx, filter, true)
 
 }
 
@@ -193,107 +200,74 @@ func (s *MongodbService) FindKV(ctx context.Context, domain string, options ...F
 	if domain == "" {
 		return nil, ErrMissingDomain
 	}
-	collection := s.c.Database(DB).Collection(CollectionKV)
-	ctx, _ = context.WithTimeout(ctx, DefaultTimeout)
-	filter := bson.M{"domain": domain}
-	if opts.Key != "" {
-		filter["key"] = opts.Key
-	}
-	for k, v := range opts.Labels {
-		filter["labels."+k] = v
-	}
 
-	cur, err := collection.Find(ctx, filter)
+	cur, err := s.findKV(ctx, domain, opts)
 	if err != nil {
-		if err.Error() == context.DeadlineExceeded.Error() {
-			return nil, ErrAction("find", filter, fmt.Errorf("can not reach mongodb in %s", s.timeout))
-		}
 		return nil, err
 	}
 	defer cur.Close(ctx)
-	if cur.Err() != nil {
-		return nil, err
-	}
+
 	kvResp := make([]*model.KVResponse, 0)
 	if opts.ExactLabels {
-		openlogging.Debug(fmt.Sprintf("find one [%s] with lables [%s] in [%s]", opts.Key, opts.Labels, domain))
-		curKV := &model.KVDoc{} //reuse this pointer to reduce GC, only clear label
-
-		//check label length to get the exact match
-		for cur.Next(ctx) { //although complexity is O(n), but there won't be so much labels for one key
-			curKV.Labels = nil
-			err := cur.Decode(curKV)
-			if err != nil {
-				openlogging.Error("decode error: " + err.Error())
-				return nil, err
-			}
-			if len(curKV.Labels) == len(opts.Labels) {
-				openlogging.Debug("hit exact labels")
-				labelGroup := &model.KVResponse{
-					LabelDoc: &model.LabelDocResponse{
-						Labels:  opts.Labels,
-						LabelID: curKV.LabelID,
-					},
-					Data: make([]*model.KVDoc, 0),
-				}
-				clearKV(curKV)
-				labelGroup.Data = append(labelGroup.Data, curKV)
-				kvResp = append(kvResp, labelGroup)
-				return kvResp, nil
-			}
+		openlogging.Debug("find one key", openlogging.WithTags(
+			map[string]interface{}{
+				"key":    opts.Key,
+				"label":  opts.Labels,
+				"domain": domain,
+			},
+		))
+		return cursorToOneKV(ctx, cur, opts.Labels)
+	}
+	if opts.Depth == 0 {
+		opts.Depth = 1
+	}
+	for cur.Next(ctx) {
+		curKV := &model.KVDoc{}
 
+		if err := cur.Decode(curKV); err != nil {
+			openlogging.Error("decode to KVs error: " + err.Error())
+			return nil, err
 		}
-		return nil, ErrKeyNotExists
-	} else {
-		if opts.Depth == 0 {
-			opts.Depth = 1
+		if (len(curKV.Labels) - len(opts.Labels)) > opts.Depth {
+			//because it is query by labels, so result can not be minus
+			//so many labels,then continue
+			openlogging.Debug("so deep, skip this key")
+			continue
 		}
-		for cur.Next(ctx) {
-			curKV := &model.KVDoc{}
-
-			if err := cur.Decode(curKV); err != nil {
-				openlogging.Error("decode to KVs error: " + err.Error())
-				return nil, err
-			}
-			if (len(curKV.Labels) - len(opts.Labels)) > opts.Depth {
-				//because it is query by labels, so result can not be minus
-				//so many labels,then continue
-				openlogging.Debug("so deep, skip this key")
-				continue
-			}
-			openlogging.Info(fmt.Sprintf("%v", curKV))
-			var groupExist bool
-			var labelGroup *model.KVResponse
-			for _, labelGroup = range kvResp {
-				if reflect.DeepEqual(labelGroup.LabelDoc.Labels, curKV.Labels) {
-					groupExist = true
-					clearKV(curKV)
-					labelGroup.Data = append(labelGroup.Data, curKV)
-					break
-				}
-
-			}
-			if !groupExist {
-				labelGroup = &model.KVResponse{
-					LabelDoc: &model.LabelDocResponse{
-						Labels:  curKV.Labels,
-						LabelID: curKV.LabelID,
-					},
-					Data: []*model.KVDoc{curKV},
-				}
+		openlogging.Info(fmt.Sprintf("%v", curKV))
+		var groupExist bool
+		var labelGroup *model.KVResponse
+		for _, labelGroup = range kvResp {
+			if reflect.DeepEqual(labelGroup.LabelDoc.Labels, curKV.Labels) {
+				groupExist = true
 				clearKV(curKV)
-				openlogging.Debug("add new label group")
-				kvResp = append(kvResp, labelGroup)
+				labelGroup.Data = append(labelGroup.Data, curKV)
+				break
 			}
 
 		}
-		if len(kvResp) == 0 {
-			return nil, ErrKeyNotExists
+		if !groupExist {
+			labelGroup = &model.KVResponse{
+				LabelDoc: &model.LabelDocResponse{
+					Labels:  curKV.Labels,
+					LabelID: curKV.LabelID,
+				},
+				Data: []*model.KVDoc{curKV},
+			}
+			clearKV(curKV)
+			openlogging.Debug("add new label group")
+			kvResp = append(kvResp, labelGroup)
 		}
-		return kvResp, nil
+
+	}
+	if len(kvResp) == 0 {
+		return nil, ErrKeyNotExists
 	}
+	return kvResp, nil
 
 }
+
+//DeleteByID delete a key value by collection ID
 func (s *MongodbService) DeleteByID(id string) error {
 	collection := s.c.Database(DB).Collection(CollectionKV)
 	hex, err := primitive.ObjectIDFromHex(id)
@@ -312,6 +286,8 @@ func (s *MongodbService) DeleteByID(id string) error {
 	return nil
 }
 
+//Delete remove a list of key values for a tenant
+//domain=tenant
 func (s *MongodbService) Delete(ids []string, domain string) error {
 	if len(ids) == 0 {
 		openlogging.Warn("delete error,ids is blank")
@@ -351,6 +327,8 @@ func (s *MongodbService) Delete(ids []string, domain string) error {
 	}
 	return nil
 }
+
+//NewMongoService create a new mongo db service
 func NewMongoService(opts Options) (*MongodbService, error) {
 	if opts.Timeout == 0 {
 		opts.Timeout = DefaultTimeout
diff --git a/server/dao/kv.go b/server/dao/kv.go
index c48774b..41aba48 100644
--- a/server/dao/kv.go
+++ b/server/dao/kv.go
@@ -15,13 +15,14 @@
  * limitations under the License.
  */
 
-//package dao is a persis layer of kie
+//Package dao is a persis layer of kie
 package dao
 
 import (
 	"context"
 	"crypto/tls"
 	"errors"
+	"fmt"
 	"github.com/apache/servicecomb-kie/pkg/model"
 	"github.com/apache/servicecomb-kie/server/config"
 	"github.com/go-mesh/openlogging"
@@ -31,6 +32,7 @@ import (
 	"time"
 )
 
+//db errors
 var (
 	ErrMissingDomain    = errors.New("domain info missing, illegal access")
 	ErrKeyNotExists     = errors.New("key with labels does not exits")
@@ -40,6 +42,7 @@ var (
 	ErrRevisionNotExist = errors.New("label revision not exist")
 )
 
+//Options mongodb options
 type Options struct {
 	URI      string
 	PoolSize int
@@ -48,6 +51,8 @@ type Options struct {
 	Timeout  time.Duration
 }
 
+//NewKVService create a kv service
+//TODO, multiple config server
 func NewKVService() (*MongodbService, error) {
 	opts := Options{
 		URI:      config.GetDB().URI,
@@ -55,7 +60,7 @@ func NewKVService() (*MongodbService, error) {
 		SSL:      config.GetDB().SSL,
 	}
 	if opts.SSL {
-
+		//TODO tls config
 	}
 	return NewMongoService(opts)
 }
@@ -90,16 +95,36 @@ func (s *MongodbService) KVExist(ctx context.Context, domain, key string, option
 			return primitive.NilObjectID, err
 		}
 		return kvs[0].ID, nil
-	} else {
-		kvs, err := s.FindKV(ctx, domain, WithExactLabels(), WithLabels(opts.Labels), WithKey(key))
-		if err != nil {
-			return primitive.NilObjectID, err
-		}
-		if len(kvs) != 1 {
-			return primitive.NilObjectID, ErrTooMany
-		}
+	}
+	kvs, err := s.FindKV(ctx, domain, WithExactLabels(), WithLabels(opts.Labels), WithKey(key))
+	if err != nil {
+		return primitive.NilObjectID, err
+	}
+	if len(kvs) != 1 {
+		return primitive.NilObjectID, ErrTooMany
+	}
 
-		return kvs[0].Data[0].ID, nil
+	return kvs[0].Data[0].ID, nil
+
+}
+
+func (s *MongodbService) findKV(ctx context.Context, domain string, opts FindOptions) (*mongo.Cursor, error) {
+	collection := s.c.Database(DB).Collection(CollectionKV)
+	ctx, _ = context.WithTimeout(ctx, DefaultTimeout)
+	filter := bson.M{"domain": domain}
+	if opts.Key != "" {
+		filter["key"] = opts.Key
+	}
+	for k, v := range opts.Labels {
+		filter["labels."+k] = v
 	}
 
+	cur, err := collection.Find(ctx, filter)
+	if err != nil {
+		if err.Error() == context.DeadlineExceeded.Error() {
+			return nil, ErrAction("find", filter, fmt.Errorf("can not reach mongodb in %s", s.timeout))
+		}
+		return nil, err
+	}
+	return cur, err
 }
diff --git a/server/dao/label.go b/server/dao/label.go
index 3fddd2f..742c3dc 100644
--- a/server/dao/label.go
+++ b/server/dao/label.go
@@ -58,6 +58,8 @@ func (s *MongodbService) findOneLabels(ctx context.Context, filter bson.M) (*mod
 	}
 	return l, nil
 }
+
+//LabelsExist check label exists or not and return label ID
 func (s *MongodbService) LabelsExist(ctx context.Context, domain string, labels map[string]string) (primitive.ObjectID, error) {
 	l, err := s.FindLabels(ctx, domain, labels)
 	if err != nil {
diff --git a/server/dao/label_history.go b/server/dao/label_history.go
index fb8164f..a3809d6 100644
--- a/server/dao/label_history.go
+++ b/server/dao/label_history.go
@@ -56,6 +56,8 @@ func (s *MongodbService) getLatestLabel(ctx context.Context, labelID string) (*m
 	}
 	return h, nil
 }
+
+//AddHistory get latest labels revision and plus 1  and save current label stats to history, then update current revision to db
 func (s *MongodbService) AddHistory(ctx context.Context, labelID string, labels map[string]string, domain string) (int, error) {
 	r, err := s.getLatestLabel(ctx, labelID)
 	if err != nil {
diff --git a/server/dao/options.go b/server/dao/options.go
index 7e33798..fbbe031 100644
--- a/server/dao/options.go
+++ b/server/dao/options.go
@@ -17,6 +17,7 @@
 
 package dao
 
+//FindOptions is option to find key value
 type FindOptions struct {
 	ExactLabels bool
 	Depth       int
@@ -26,6 +27,7 @@ type FindOptions struct {
 	ClearLabel  bool
 }
 
+//FindOption is functional option to find key value
 type FindOption func(*FindOptions)
 
 //WithExactLabels tell model service to return only one kv matches the labels
@@ -49,7 +51,7 @@ func WithLabels(labels map[string]string) FindOption {
 	}
 }
 
-//WithLabels find kv by labelID
+//WithLabelID find kv by labelID
 func WithLabelID(label string) FindOption {
 	return func(o *FindOptions) {
 		o.LabelID = label
diff --git a/server/dao/tool.go b/server/dao/tool.go
index b3d5ea4..08ff602 100644
--- a/server/dao/tool.go
+++ b/server/dao/tool.go
@@ -17,7 +17,12 @@
 
 package dao
 
-import "github.com/apache/servicecomb-kie/pkg/model"
+import (
+	"context"
+	"github.com/apache/servicecomb-kie/pkg/model"
+	"github.com/go-mesh/openlogging"
+	"go.mongodb.org/mongo-driver/mongo"
+)
 
 //clearKV clean attr which don't need to return to client side
 func clearKV(kv *model.KVDoc) {
@@ -25,3 +30,36 @@ func clearKV(kv *model.KVDoc) {
 	kv.Labels = nil
 	kv.LabelID = ""
 }
+
+func cursorToOneKV(ctx context.Context, cur *mongo.Cursor, labels map[string]string) ([]*model.KVResponse, error) {
+	kvResp := make([]*model.KVResponse, 0)
+	curKV := &model.KVDoc{} //reuse this pointer to reduce GC, only clear label
+	//check label length to get the exact match
+	for cur.Next(ctx) { //although complexity is O(n), but there won't be so much labels for one key
+		if cur.Err() != nil {
+			return nil, cur.Err()
+		}
+		curKV.Labels = nil
+		err := cur.Decode(curKV)
+		if err != nil {
+			openlogging.Error("decode error: " + err.Error())
+			return nil, err
+		}
+		if len(curKV.Labels) == len(labels) {
+			openlogging.Debug("hit exact labels")
+			labelGroup := &model.KVResponse{
+				LabelDoc: &model.LabelDocResponse{
+					Labels:  labels,
+					LabelID: curKV.LabelID,
+				},
+				Data: make([]*model.KVDoc, 0),
+			}
+			clearKV(curKV)
+			labelGroup.Data = append(labelGroup.Data, curKV)
+			kvResp = append(kvResp, labelGroup)
+			return kvResp, nil
+		}
+
+	}
+	return nil, ErrKeyNotExists
+}
diff --git a/server/handler/noop_auth_handler.go b/server/handler/noop_auth_handler.go
index b2c4a20..b4a3833 100644
--- a/server/handler/noop_auth_handler.go
+++ b/server/handler/noop_auth_handler.go
@@ -26,6 +26,7 @@ import (
 //developer can extend authenticate and authorization by set new handler in chassis.yaml
 type NoopAuthHandler struct{}
 
+//Handle set local attribute to http request
 func (bk *NoopAuthHandler) Handle(chain *handler.Chain, inv *invocation.Invocation, cb invocation.ResponseCallBack) {
 	inv.SetMetadata("domain", "default")
 	chain.Next(inv, cb)
@@ -35,6 +36,7 @@ func newDomainResolver() handler.Handler {
 	return &NoopAuthHandler{}
 }
 
+//Name is handler name
 func (bk *NoopAuthHandler) Name() string {
 	return "auth-handler"
 }
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
index eca9fef..48baa22 100644
--- a/server/resource/v1/common.go
+++ b/server/resource/v1/common.go
@@ -27,11 +27,8 @@ import (
 	"strconv"
 )
 
+//const of server
 const (
-	HeaderTenant = "X-Domain-Name"
-
-	FindExact               = "exact"
-	FindMany                = "greedy"
 	MsgDomainMustNotBeEmpty = "domain must not be empty"
 	MsgIllegalFindPolicy    = "value of header " + common.HeaderMatch + " can be greedy or exact"
 	MsgIllegalLabels        = "label's value can not be empty, " +
@@ -40,9 +37,12 @@ const (
 	ErrIDMustNotEmpty = "must supply id if you want to remove key"
 )
 
+//ReadDomain get domain info from attribute
 func ReadDomain(context *restful.Context) interface{} {
 	return context.ReadRestfulRequest().Attribute("domain")
 }
+
+//ReadFindDepth get find depth
 func ReadFindDepth(context *restful.Context) (int, error) {
 	d := context.ReadRestfulRequest().HeaderParameter(common.HeaderDepth)
 	if d == "" {
@@ -54,6 +54,8 @@ func ReadFindDepth(context *restful.Context) (int, error) {
 	}
 	return depth, nil
 }
+
+//ReadMatchPolicy get match policy
 func ReadMatchPolicy(context *restful.Context) string {
 	policy := context.ReadRestfulRequest().HeaderParameter(common.HeaderMatch)
 	if policy == "" {
@@ -62,16 +64,20 @@ func ReadMatchPolicy(context *restful.Context) string {
 	}
 	return policy
 }
+
+//WriteErrResponse write error message to client
 func WriteErrResponse(context *restful.Context, status int, msg string) {
 	context.WriteHeader(status)
 	b, _ := json.MarshalIndent(&ErrorMsg{Msg: msg}, "", " ")
 	context.Write(b)
 }
 
+//ErrLog record error
 func ErrLog(action string, kv *model.KVDoc, err error) {
 	openlogging.Error(fmt.Sprintf("[%s] [%v] err:%s", action, kv, err.Error()))
 }
 
+//InfoLog record info
 func InfoLog(action string, kv *model.KVDoc) {
 	openlogging.Info(
 		fmt.Sprintf("[%s] [%s:%s] in [%s] success", action, kv.Key, kv.Value, kv.Domain))
diff --git a/server/resource/v1/doc_struct.go b/server/resource/v1/doc_struct.go
index a0402ae..37f7432 100644
--- a/server/resource/v1/doc_struct.go
+++ b/server/resource/v1/doc_struct.go
@@ -17,12 +17,41 @@
 
 package v1
 
+import (
+	"github.com/apache/servicecomb-kie/pkg/common"
+	goRestful "github.com/emicklei/go-restful"
+	"github.com/go-chassis/go-chassis/server/restful"
+)
+
+//swagger doc elements
+var (
+	DocHeaderDepth = &restful.Parameters{
+		DataType:  "string",
+		Name:      common.HeaderDepth,
+		ParamType: goRestful.HeaderParameterKind,
+		Desc:      "integer, default is 1, if you set match policy, you can set,depth to decide label number",
+	}
+	DocPathKey = &restful.Parameters{
+		DataType:  "string",
+		Name:      "key",
+		ParamType: goRestful.PathParameterKind,
+	}
+	DocHeaderMath = &restful.Parameters{
+		DataType:  "string",
+		Name:      common.HeaderMatch,
+		ParamType: goRestful.HeaderParameterKind,
+		Desc:      "greedy or exact",
+	}
+)
+
+//KVBody is open api doc
 type KVBody struct {
 	Labels    map[string]string `json:"labels"`
 	ValueType string            `json:"valueType"`
 	Value     string            `json:"value"`
 }
 
+//ErrorMsg is open api doc
 type ErrorMsg struct {
 	Msg string `json:"msg"`
 }
diff --git a/server/resource/v1/history_resource.go b/server/resource/v1/history_resource.go
index 3952a19..0bca053 100644
--- a/server/resource/v1/history_resource.go
+++ b/server/resource/v1/history_resource.go
@@ -17,5 +17,6 @@
 
 package v1
 
+//HistoryResource TODO
 type HistoryResource struct {
 }
diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go
index 7832f23..a1f94b6 100644
--- a/server/resource/v1/kv_resource.go
+++ b/server/resource/v1/kv_resource.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-//v1 package hold http rest v1 API
+//Package v1 hold http rest v1 API
 package v1
 
 import (
@@ -31,9 +31,11 @@ import (
 	"strings"
 )
 
+//KVResource has API about kv operations
 type KVResource struct {
 }
 
+//Put create or update kv
 func (r *KVResource) Put(context *restful.Context) {
 	var err error
 	key := context.ReadPathParameter("key")
@@ -64,6 +66,8 @@ func (r *KVResource) Put(context *restful.Context) {
 	context.WriteHeaderAndJSON(http.StatusOK, kv, goRestful.MIME_JSON)
 
 }
+
+//FindWithKey search key by label and key
 func (r *KVResource) FindWithKey(context *restful.Context) {
 	var err error
 	key := context.ReadPathParameter("key")
@@ -121,6 +125,8 @@ func (r *KVResource) FindWithKey(context *restful.Context) {
 	}
 
 }
+
+//FindByLabels search key only by label
 func (r *KVResource) FindByLabels(context *restful.Context) {
 	var err error
 	values := context.ReadRequest().URL.Query()
@@ -169,6 +175,8 @@ func (r *KVResource) FindByLabels(context *restful.Context) {
 	}
 
 }
+
+//Delete deletes key by ids
 func (r *KVResource) Delete(context *restful.Context) {
 	domain := ReadDomain(context)
 	if domain == nil {
@@ -203,16 +211,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
 			ResourceFuncName: "Put",
 			FuncDesc:         "create or update key value",
 			Parameters: []*restful.Parameters{
-				{
-					DataType:  "string",
-					Name:      "key",
-					ParamType: goRestful.PathParameterKind,
-				}, {
-					DataType:  "string",
-					Name:      HeaderTenant,
-					ParamType: goRestful.HeaderParameterKind,
-					Desc:      "set kv to other tenant",
-				}, {
+				DocPathKey, {
 					DataType:  "string",
 					Name:      "X-Realm",
 					ParamType: goRestful.HeaderParameterKind,
@@ -234,20 +233,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
 			ResourceFuncName: "FindWithKey",
 			FuncDesc:         "get key values by key and labels",
 			Parameters: []*restful.Parameters{
-				{
-					DataType:  "string",
-					Name:      "key",
-					ParamType: goRestful.PathParameterKind,
-				}, {
-					DataType:  "string",
-					Name:      HeaderTenant,
-					ParamType: goRestful.HeaderParameterKind,
-				}, {
-					DataType:  "string",
-					Name:      common.HeaderMatch,
-					ParamType: goRestful.HeaderParameterKind,
-					Desc:      "greedy or exact",
-				},
+				DocPathKey, DocHeaderMath, DocHeaderDepth,
 			},
 			Returns: []*restful.Returns{
 				{
@@ -265,16 +251,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
 			ResourceFuncName: "FindByLabels",
 			FuncDesc:         "find key values only by labels",
 			Parameters: []*restful.Parameters{
-				{
-					DataType:  "string",
-					Name:      HeaderTenant,
-					ParamType: goRestful.HeaderParameterKind,
-				}, {
-					DataType:  "string",
-					Name:      common.HeaderMatch,
-					ParamType: goRestful.HeaderParameterKind,
-					Desc:      "greedy or exact",
-				},
+				DocHeaderMath, DocHeaderDepth,
 			},
 			Returns: []*restful.Returns{
 				{
@@ -290,18 +267,13 @@ func (r *KVResource) URLPatterns() []restful.Route {
 			Path:             "/v1/kv/{ids}",
 			ResourceFuncName: "Delete",
 			FuncDesc:         "delete key by id,separated by ','",
-			Parameters: []*restful.Parameters{
-				{
-					DataType:  "string",
-					Name:      HeaderTenant,
-					ParamType: goRestful.HeaderParameterKind,
-				}, {
-					DataType:  "string",
-					Name:      "ids",
-					ParamType: goRestful.PathParameterKind,
-					Desc: "The id strings to be removed are separated by ',',If the actual number of deletions " +
-						"and the number of parameters are not equal, no error will be returned and only warn log will be printed.",
-				},
+			Parameters: []*restful.Parameters{{
+				DataType:  "string",
+				Name:      "ids",
+				ParamType: goRestful.PathParameterKind,
+				Desc: "The id strings to be removed are separated by ',',If the actual number of deletions " +
+					"and the number of parameters are not equal, no error will be returned and only warn log will be printed.",
+			},
 			},
 			Returns: []*restful.Returns{
 				{