You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2022/01/06 10:04:57 UTC

[skywalking-cli] branch master updated: Adapt OAP V9 query protocol (#133)

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

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git


The following commit(s) were added to refs/heads/master by this push:
     new b3e4023  Adapt OAP V9 query protocol (#133)
b3e4023 is described below

commit b3e40234952ffd69dfe767bb0b18f0e6e40d2cad
Author: mrproliu <74...@qq.com>
AuthorDate: Thu Jan 6 18:04:48 2022 +0800

    Adapt OAP V9 query protocol (#133)
---
 .github/workflows/command-tests.yml                |   7 +-
 CHANGES.md                                         |   2 +
 .../version.graphql}                               |   4 +-
 .../metadata/{ => v1}/AllBrowserServices.graphql   |   0
 .../graphqls/metadata/{ => v1}/AllServices.graphql |   0
 .../graphqls/metadata/{ => v1}/Instances.graphql   |   0
 .../metadata/{ => v1}/SearchBrowserService.graphql |   0
 .../metadata/{ => v1}/SearchEndpoints.graphql      |   0
 .../metadata/{ => v1}/SearchService.graphql        |   0
 .../metadata/{ => v1}/ServerTimeInfo.graphql       |   0
 .../metadata/{ => v2}/AllBrowserServices.graphql   |   2 +-
 .../graphqls/metadata/{ => v2}/AllServices.graphql |   2 +-
 .../graphqls/metadata/{ => v2}/Instances.graphql   |   1 +
 .../ListLayers.graphql}                            |   6 +-
 .../ListService.graphql}                           |   6 +-
 .../metadata/{ => v2}/SearchBrowserService.graphql |   2 +-
 .../metadata/{ => v2}/SearchService.graphql        |   2 +-
 .../metadata/{ => v2}/ServerTimeInfo.graphql       |   0
 cmd/swctl/main.go                                  |   2 +
 go.mod                                             |   4 +-
 go.sum                                             |  54 ++++++++---
 .../{service/service.go => layer/layer.go}         |  11 +--
 .../commands/{service/service.go => layer/list.go} |  29 ++++--
 internal/commands/service/{service.go => layer.go} |  36 +++++--
 internal/commands/service/service.go               |   1 +
 .../service.go => pkg/graphql/common/common.go     |  22 +++--
 pkg/graphql/metadata/metadata.go                   |  92 ++++++++++++++++--
 test/{ => base}/docker-compose.yml                 |   6 --
 test/{ => base}/services/consumer.py               |   0
 test/{ => base}/services/provider.py               |   0
 .../8.8.1/docker-compose.yml}                      |  53 ++++++-----
 .../8.8.1}/expected/dashboard-global-metrics.yml   |   0
 .../8.8.1}/expected/dashboard-global.yml           |   0
 .../8.8.1}/expected/dependency-endpoint.yml        |   0
 .../8.8.1}/expected/dependency-instance.yml        |   0
 .../8.8.1}/expected/dependency-service.yml         |   0
 test/{ => cases/8.8.1}/expected/empty-array.yml    |   0
 test/{ => cases/8.8.1}/expected/endpoint-list.yml  |   0
 test/{ => cases/8.8.1}/expected/instance-list.yml  |   1 +
 .../8.8.1}/expected/metrics-has-value.yml          |   0
 .../expected/metrics-top-endpoint-sla-provider.yml |   0
 .../8.8.1}/expected/metrics-top-service-sla.yml    |   0
 .../8.8.1}/expected/service-endpoint.yml           |   0
 .../8.8.1}/expected/service-provider.yml           |   2 +
 test/{ => cases/8.8.1}/expected/service.yml        |   4 +
 .../8.8.1}/expected/trace-users-detail.yml         |   0
 test/{ => cases/8.8.1}/expected/traces-list.yml    |   0
 test/{ => cases/8.8.1}/expected/value.yml          |   0
 test/cases/8.8.1/test.yaml                         |  99 ++++++++++++++++++++
 test/{ => cases/9.0.0}/docker-compose.yml          |  45 +++------
 .../9.0.0}/expected/dashboard-global-metrics.yml   |   0
 .../9.0.0}/expected/dashboard-global.yml           |   0
 .../9.0.0}/expected/dependency-endpoint.yml        |   0
 .../9.0.0}/expected/dependency-instance.yml        |   0
 .../9.0.0}/expected/dependency-service.yml         |   0
 test/{ => cases/9.0.0}/expected/empty-array.yml    |   0
 test/{ => cases/9.0.0}/expected/endpoint-list.yml  |   0
 test/{ => cases/9.0.0}/expected/instance-list.yml  |   1 +
 .../9.0.0/expected/layer-list.yml}                 |   2 +-
 .../9.0.0}/expected/metrics-has-value.yml          |   0
 .../expected/metrics-top-endpoint-sla-provider.yml |   0
 .../9.0.0}/expected/metrics-top-service-sla.yml    |   0
 .../9.0.0}/expected/service-endpoint.yml           |   0
 .../9.0.0}/expected/service-provider.yml           |   3 +
 test/{ => cases/9.0.0}/expected/service.yml        |   6 ++
 .../9.0.0}/expected/trace-users-detail.yml         |   0
 test/{ => cases/9.0.0}/expected/traces-list.yml    |   0
 test/{ => cases/9.0.0}/expected/value.yml          |   0
 test/cases/9.0.0/test.yaml                         | 104 +++++++++++++++++++++
 69 files changed, 482 insertions(+), 129 deletions(-)

diff --git a/.github/workflows/command-tests.yml b/.github/workflows/command-tests.yml
index cd81384..f8203dc 100644
--- a/.github/workflows/command-tests.yml
+++ b/.github/workflows/command-tests.yml
@@ -28,6 +28,11 @@ on:
 jobs:
   command-test:
     runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        oap:
+          - 8.8.1
+          - 9.0.0
     steps:
       - uses: actions/checkout@v2
       - name: Check for go file changes
@@ -47,4 +52,4 @@ jobs:
         if: steps.changes.outputs.src == 'true'
         uses: apache/skywalking-infra-e2e@d714677324e31cd4ab45782e58cea0946a065132
         with:
-          e2e-file: test/test.yaml
+          e2e-file: test/cases/${{ matrix.oap }}/test.yaml
diff --git a/CHANGES.md b/CHANGES.md
index 3017d4e..32119ba 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,6 +8,8 @@ Release Notes.
 ### Features
 
 - Add the sub-command `dependency instance` to query instance relationships (#117)
+- Add the sub-command `service layer` to query services according to layer.(#133)
+- Add the sub-command `layer list` to query layer list.(#133)
 
 ### Bug Fixes
 
diff --git a/assets/graphqls/metadata/ServerTimeInfo.graphql b/assets/graphqls/common/version.graphql
similarity index 92%
copy from assets/graphqls/metadata/ServerTimeInfo.graphql
copy to assets/graphqls/common/version.graphql
index d00d8a9..8a57514 100644
--- a/assets/graphqls/metadata/ServerTimeInfo.graphql
+++ b/assets/graphqls/common/version.graphql
@@ -16,7 +16,5 @@
 # under the License.
 
 query {
-    result: getTimeInfo {
-        timezone, currentTimestamp
-    }
+    result: version
 }
diff --git a/assets/graphqls/metadata/AllBrowserServices.graphql b/assets/graphqls/metadata/v1/AllBrowserServices.graphql
similarity index 100%
copy from assets/graphqls/metadata/AllBrowserServices.graphql
copy to assets/graphqls/metadata/v1/AllBrowserServices.graphql
diff --git a/assets/graphqls/metadata/AllServices.graphql b/assets/graphqls/metadata/v1/AllServices.graphql
similarity index 100%
copy from assets/graphqls/metadata/AllServices.graphql
copy to assets/graphqls/metadata/v1/AllServices.graphql
diff --git a/assets/graphqls/metadata/Instances.graphql b/assets/graphqls/metadata/v1/Instances.graphql
similarity index 100%
copy from assets/graphqls/metadata/Instances.graphql
copy to assets/graphqls/metadata/v1/Instances.graphql
diff --git a/assets/graphqls/metadata/SearchBrowserService.graphql b/assets/graphqls/metadata/v1/SearchBrowserService.graphql
similarity index 100%
copy from assets/graphqls/metadata/SearchBrowserService.graphql
copy to assets/graphqls/metadata/v1/SearchBrowserService.graphql
diff --git a/assets/graphqls/metadata/SearchEndpoints.graphql b/assets/graphqls/metadata/v1/SearchEndpoints.graphql
similarity index 100%
rename from assets/graphqls/metadata/SearchEndpoints.graphql
rename to assets/graphqls/metadata/v1/SearchEndpoints.graphql
diff --git a/assets/graphqls/metadata/SearchService.graphql b/assets/graphqls/metadata/v1/SearchService.graphql
similarity index 100%
copy from assets/graphqls/metadata/SearchService.graphql
copy to assets/graphqls/metadata/v1/SearchService.graphql
diff --git a/assets/graphqls/metadata/ServerTimeInfo.graphql b/assets/graphqls/metadata/v1/ServerTimeInfo.graphql
similarity index 100%
copy from assets/graphqls/metadata/ServerTimeInfo.graphql
copy to assets/graphqls/metadata/v1/ServerTimeInfo.graphql
diff --git a/assets/graphqls/metadata/AllBrowserServices.graphql b/assets/graphqls/metadata/v2/AllBrowserServices.graphql
similarity index 95%
rename from assets/graphqls/metadata/AllBrowserServices.graphql
rename to assets/graphqls/metadata/v2/AllBrowserServices.graphql
index 08c0c19..c477c82 100644
--- a/assets/graphqls/metadata/AllBrowserServices.graphql
+++ b/assets/graphqls/metadata/v2/AllBrowserServices.graphql
@@ -17,6 +17,6 @@
 
 query ($duration: Duration!) {
     result: getAllBrowserServices(duration: $duration) {
-        id name
+        id name group shortName layers
     }
 }
diff --git a/assets/graphqls/metadata/AllServices.graphql b/assets/graphqls/metadata/v2/AllServices.graphql
similarity index 95%
rename from assets/graphqls/metadata/AllServices.graphql
rename to assets/graphqls/metadata/v2/AllServices.graphql
index d3a3f86..6229982 100644
--- a/assets/graphqls/metadata/AllServices.graphql
+++ b/assets/graphqls/metadata/v2/AllServices.graphql
@@ -17,6 +17,6 @@
 
 query ($duration: Duration!) {
     result: getAllServices(duration: $duration) {
-        id name
+        id name group shortName layers
     }
 }
diff --git a/assets/graphqls/metadata/Instances.graphql b/assets/graphqls/metadata/v2/Instances.graphql
similarity index 98%
rename from assets/graphqls/metadata/Instances.graphql
rename to assets/graphqls/metadata/v2/Instances.graphql
index 4bb4cbc..0a78de3 100644
--- a/assets/graphqls/metadata/Instances.graphql
+++ b/assets/graphqls/metadata/v2/Instances.graphql
@@ -22,6 +22,7 @@ query ($serviceId: ID!, $duration: Duration!) {
         name
         language
         instanceUUID
+        layer
         attributes {
             name
             value
diff --git a/assets/graphqls/metadata/ServerTimeInfo.graphql b/assets/graphqls/metadata/v2/ListLayers.graphql
similarity index 92%
copy from assets/graphqls/metadata/ServerTimeInfo.graphql
copy to assets/graphqls/metadata/v2/ListLayers.graphql
index d00d8a9..57dfc30 100644
--- a/assets/graphqls/metadata/ServerTimeInfo.graphql
+++ b/assets/graphqls/metadata/v2/ListLayers.graphql
@@ -16,7 +16,5 @@
 # under the License.
 
 query {
-    result: getTimeInfo {
-        timezone, currentTimestamp
-    }
-}
+    result: listLayers
+}
\ No newline at end of file
diff --git a/assets/graphqls/metadata/ServerTimeInfo.graphql b/assets/graphqls/metadata/v2/ListService.graphql
similarity index 88%
copy from assets/graphqls/metadata/ServerTimeInfo.graphql
copy to assets/graphqls/metadata/v2/ListService.graphql
index d00d8a9..d0fe6a2 100644
--- a/assets/graphqls/metadata/ServerTimeInfo.graphql
+++ b/assets/graphqls/metadata/v2/ListService.graphql
@@ -15,8 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-query {
-    result: getTimeInfo {
-        timezone, currentTimestamp
+query ($layer: String!) {
+    result: listServices(layer: $layer) {
+        id name group shortName layers
     }
 }
diff --git a/assets/graphqls/metadata/SearchBrowserService.graphql b/assets/graphqls/metadata/v2/SearchBrowserService.graphql
similarity index 95%
rename from assets/graphqls/metadata/SearchBrowserService.graphql
rename to assets/graphqls/metadata/v2/SearchBrowserService.graphql
index b7cbeb5..7de17f1 100644
--- a/assets/graphqls/metadata/SearchBrowserService.graphql
+++ b/assets/graphqls/metadata/v2/SearchBrowserService.graphql
@@ -17,6 +17,6 @@
 
 query ($serviceCode: String!) {
     result: searchBrowserService(serviceCode: $serviceCode) {
-        id name
+        id name group shortName layers
     }
 }
diff --git a/assets/graphqls/metadata/SearchService.graphql b/assets/graphqls/metadata/v2/SearchService.graphql
similarity index 95%
rename from assets/graphqls/metadata/SearchService.graphql
rename to assets/graphqls/metadata/v2/SearchService.graphql
index 525446e..f40d2c1 100644
--- a/assets/graphqls/metadata/SearchService.graphql
+++ b/assets/graphqls/metadata/v2/SearchService.graphql
@@ -17,6 +17,6 @@
 
 query searchService($serviceCode: String!) {
     result: searchService(serviceCode: $serviceCode) {
-        id name
+        id name group shortName layers
     }
 }
diff --git a/assets/graphqls/metadata/ServerTimeInfo.graphql b/assets/graphqls/metadata/v2/ServerTimeInfo.graphql
similarity index 100%
rename from assets/graphqls/metadata/ServerTimeInfo.graphql
rename to assets/graphqls/metadata/v2/ServerTimeInfo.graphql
diff --git a/cmd/swctl/main.go b/cmd/swctl/main.go
index f52ea7e..27f9b2e 100644
--- a/cmd/swctl/main.go
+++ b/cmd/swctl/main.go
@@ -32,6 +32,7 @@ import (
 	"github.com/apache/skywalking-cli/internal/commands/install"
 	"github.com/apache/skywalking-cli/internal/commands/instance"
 	"github.com/apache/skywalking-cli/internal/commands/interceptor"
+	"github.com/apache/skywalking-cli/internal/commands/layer"
 	"github.com/apache/skywalking-cli/internal/commands/logs"
 	"github.com/apache/skywalking-cli/internal/commands/metrics"
 	"github.com/apache/skywalking-cli/internal/commands/profile"
@@ -98,6 +99,7 @@ services, service instances, etc.`
 		completion.Command,
 		dependency.Command,
 		alarm.Command,
+		layer.Command,
 	}
 
 	app.Before = interceptor.BeforeChain(
diff --git a/go.mod b/go.mod
index b92ea26..463d752 100644
--- a/go.mod
+++ b/go.mod
@@ -16,9 +16,9 @@ require (
 	github.com/spf13/viper v1.7.0
 	github.com/urfave/cli/v2 v2.3.0
 	golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect
-	google.golang.org/grpc v1.38.0
+	google.golang.org/grpc v1.40.0
 	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/apimachinery v0.21.1
 	sigs.k8s.io/controller-runtime v0.7.0
-	skywalking.apache.org/repo/goapi v0.0.0-20210820070710-e10b78bbf481
+	skywalking.apache.org/repo/goapi v0.0.0-20220105035505-48cad5270f49
 )
diff --git a/go.sum b/go.sum
index d213767..b361ae8 100644
--- a/go.sum
+++ b/go.sum
@@ -67,6 +67,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/apache/skywalking-cli v0.0.0-20201125155244-ffee47d2e83d/go.mod h1:kUqj2ESaiYz89JFJUr+cL6EDmzlEYSrlEMOkWg9wuS4=
 github.com/apache/skywalking-swck v0.2.0 h1:/D4EZQxHWc4CEvCr0YuYMxrzz/Qmmh7+LRYk6cyEbzA=
 github.com/apache/skywalking-swck v0.2.0/go.mod h1:kU75U5Tb3aEi1Vk1KX7hzyh8Hv7sUMNx0Djq5PLEPWI=
@@ -93,7 +94,9 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -128,7 +131,10 @@ github.com/emicklei/go-restful v2.14.3+incompatible/go.mod h1:otzb+WCGbkyDHkqmQm
 github.com/emicklei/go-restful-swagger12 v0.0.0-20201014110547-68ccff494617/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
@@ -235,8 +241,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -245,8 +252,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -277,6 +285,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -452,6 +461,7 @@ github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULU
 github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -516,6 +526,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@@ -528,6 +539,7 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
@@ -577,8 +589,9 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -587,6 +600,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -618,10 +632,13 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
 golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk=
+golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -635,6 +652,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -664,7 +682,6 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -677,8 +694,12 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M=
 golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw=
+golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -687,8 +708,10 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -740,8 +763,9 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
 golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
+golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -784,9 +808,11 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
 google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
 google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 h1:R1r5J0u6Cx+RNl/6mezTw6oA14cmKC96FeUwL6A9bd4=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -797,8 +823,11 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -810,6 +839,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -910,5 +940,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
-skywalking.apache.org/repo/goapi v0.0.0-20210820070710-e10b78bbf481 h1:K8jQuADJdwsl4+3P6g/nFjRo9ADNhal2MWUW2R4D8Xk=
-skywalking.apache.org/repo/goapi v0.0.0-20210820070710-e10b78bbf481/go.mod h1:2abOB2LaQEsJLmollzCt5kNfVMWFGKE58905uYzs+sc=
+skywalking.apache.org/repo/goapi v0.0.0-20220105035505-48cad5270f49 h1:qg8EPR0uBCuxQ0ZLhFYhX8OjtzHKyD17ZMTHRifYcY4=
+skywalking.apache.org/repo/goapi v0.0.0-20220105035505-48cad5270f49/go.mod h1:4KrWd+Oi4lkB+PtxZgIlf+3T6EECPru4fOWNMEHjxRk=
diff --git a/internal/commands/service/service.go b/internal/commands/layer/layer.go
similarity index 85%
copy from internal/commands/service/service.go
copy to internal/commands/layer/layer.go
index 9e85b2a..a77371c 100644
--- a/internal/commands/service/service.go
+++ b/internal/commands/layer/layer.go
@@ -15,17 +15,16 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package service
+package layer
 
 import (
 	"github.com/urfave/cli/v2"
 )
 
 var Command = &cli.Command{
-	Name:    "service",
-	Aliases: []string{"s", "svc"},
-	Usage:   "Service related sub-command",
-	Subcommands: cli.Commands{
-		ListCommand,
+	Name:  "layer",
+	Usage: "Layer related sub-command",
+	Subcommands: []*cli.Command{
+		listCommand,
 	},
 }
diff --git a/internal/commands/service/service.go b/internal/commands/layer/list.go
similarity index 61%
copy from internal/commands/service/service.go
copy to internal/commands/layer/list.go
index 9e85b2a..fb9d500 100644
--- a/internal/commands/service/service.go
+++ b/internal/commands/layer/list.go
@@ -15,17 +15,32 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package service
+package layer
 
 import (
+	"github.com/apache/skywalking-cli/pkg/display"
+	"github.com/apache/skywalking-cli/pkg/display/displayable"
+	"github.com/apache/skywalking-cli/pkg/graphql/metadata"
+
 	"github.com/urfave/cli/v2"
 )
 
-var Command = &cli.Command{
-	Name:    "service",
-	Aliases: []string{"s", "svc"},
-	Usage:   "Service related sub-command",
-	Subcommands: cli.Commands{
-		ListCommand,
+var listCommand = &cli.Command{
+	Name:    "list",
+	Aliases: []string{"ls"},
+	Usage:   "List layers",
+	UsageText: `List layers
+
+Examples:
+1. List all layers:
+$ swctl layer list
+`,
+	Action: func(ctx *cli.Context) error {
+		layers, err := metadata.ListLayers(ctx)
+		if err != nil {
+			return err
+		}
+
+		return display.Display(ctx, &displayable.Displayable{Data: layers})
 	},
 }
diff --git a/internal/commands/service/service.go b/internal/commands/service/layer.go
similarity index 50%
copy from internal/commands/service/service.go
copy to internal/commands/service/layer.go
index 9e85b2a..ec5ce3e 100644
--- a/internal/commands/service/service.go
+++ b/internal/commands/service/layer.go
@@ -18,14 +18,38 @@
 package service
 
 import (
+	"fmt"
+
 	"github.com/urfave/cli/v2"
+
+	api "skywalking.apache.org/repo/goapi/query"
+
+	"github.com/apache/skywalking-cli/pkg/display"
+	"github.com/apache/skywalking-cli/pkg/display/displayable"
+	"github.com/apache/skywalking-cli/pkg/graphql/metadata"
 )
 
-var Command = &cli.Command{
-	Name:    "service",
-	Aliases: []string{"s", "svc"},
-	Usage:   "Service related sub-command",
-	Subcommands: cli.Commands{
-		ListCommand,
+var LayerCommand = &cli.Command{
+	Name:      "layer",
+	Aliases:   []string{"ly"},
+	Usage:     `list the service list according to layer`,
+	ArgsUsage: "<layer name>",
+	UsageText: `This command lists the services matching the given "<layer name>".
+
+Examples:
+2. List services in "GENERAL" layer:
+$ swctl svc ly GENERAL`,
+	Action: func(ctx *cli.Context) error {
+		var services []api.Service
+
+		if args := ctx.Args(); args.Len() == 0 {
+			return fmt.Errorf("layer must be provide")
+		}
+		services, err := metadata.ListLayerService(ctx, ctx.Args().First())
+		if err != nil {
+			return err
+		}
+
+		return display.Display(ctx, &displayable.Displayable{Data: services})
 	},
 }
diff --git a/internal/commands/service/service.go b/internal/commands/service/service.go
index 9e85b2a..adbaf9b 100644
--- a/internal/commands/service/service.go
+++ b/internal/commands/service/service.go
@@ -27,5 +27,6 @@ var Command = &cli.Command{
 	Usage:   "Service related sub-command",
 	Subcommands: cli.Commands{
 		ListCommand,
+		LayerCommand,
 	},
 }
diff --git a/internal/commands/service/service.go b/pkg/graphql/common/common.go
similarity index 68%
copy from internal/commands/service/service.go
copy to pkg/graphql/common/common.go
index 9e85b2a..4a17d19 100644
--- a/internal/commands/service/service.go
+++ b/pkg/graphql/common/common.go
@@ -15,17 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package service
+package common
 
 import (
+	"github.com/machinebox/graphql"
+
 	"github.com/urfave/cli/v2"
+
+	"github.com/apache/skywalking-cli/assets"
+	"github.com/apache/skywalking-cli/pkg/graphql/client"
 )
 
-var Command = &cli.Command{
-	Name:    "service",
-	Aliases: []string{"s", "svc"},
-	Usage:   "Service related sub-command",
-	Subcommands: cli.Commands{
-		ListCommand,
-	},
+func Version(ctx *cli.Context) (string, error) {
+	var response map[string]string
+
+	request := graphql.NewRequest(assets.Read("graphqls/common/version.graphql"))
+
+	err := client.ExecuteQuery(ctx, request, &response)
+
+	return response["result"], err
 }
diff --git a/pkg/graphql/metadata/metadata.go b/pkg/graphql/metadata/metadata.go
index 8b7e60a..af5a171 100644
--- a/pkg/graphql/metadata/metadata.go
+++ b/pkg/graphql/metadata/metadata.go
@@ -19,6 +19,10 @@ package metadata
 
 import (
 	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/apache/skywalking-cli/pkg/graphql/common"
 
 	api "skywalking.apache.org/repo/goapi/query"
 
@@ -33,10 +37,14 @@ import (
 func AllServices(cliCtx *cli.Context, duration api.Duration) ([]api.Service, error) {
 	var response map[string][]api.Service
 
-	request := graphql.NewRequest(assets.Read("graphqls/metadata/AllServices.graphql"))
+	version, err := protocolVersion(cliCtx)
+	if err != nil {
+		return nil, err
+	}
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/" + version + "/AllServices.graphql"))
 	request.Var("duration", duration)
 
-	err := client.ExecuteQuery(cliCtx, request, &response)
+	err = client.ExecuteQuery(cliCtx, request, &response)
 
 	return response["result"], err
 }
@@ -44,7 +52,11 @@ func AllServices(cliCtx *cli.Context, duration api.Duration) ([]api.Service, err
 func SearchService(cliCtx *cli.Context, serviceCode string) (service api.Service, err error) {
 	var response map[string]api.Service
 
-	request := graphql.NewRequest(assets.Read("graphqls/metadata/SearchService.graphql"))
+	version, err := protocolVersion(cliCtx)
+	if err != nil {
+		return api.Service{}, err
+	}
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/" + version + "/SearchService.graphql"))
 	request.Var("serviceCode", serviceCode)
 
 	err = client.ExecuteQuery(cliCtx, request, &response)
@@ -61,10 +73,14 @@ func SearchService(cliCtx *cli.Context, serviceCode string) (service api.Service
 func AllBrowserServices(cliCtx *cli.Context, duration api.Duration) ([]api.Service, error) {
 	var response map[string][]api.Service
 
-	request := graphql.NewRequest(assets.Read("graphqls/metadata/AllBrowserServices.graphql"))
+	version, err := protocolVersion(cliCtx)
+	if err != nil {
+		return nil, err
+	}
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/" + version + "/AllBrowserServices.graphql"))
 	request.Var("duration", duration)
 
-	err := client.ExecuteQuery(cliCtx, request, &response)
+	err = client.ExecuteQuery(cliCtx, request, &response)
 
 	return response["result"], err
 }
@@ -72,7 +88,11 @@ func AllBrowserServices(cliCtx *cli.Context, duration api.Duration) ([]api.Servi
 func SearchBrowserService(cliCtx *cli.Context, serviceCode string) (service api.Service, err error) {
 	var response map[string]api.Service
 
-	request := graphql.NewRequest(assets.Read("graphqls/metadata/SearchBrowserService.graphql"))
+	version, err := protocolVersion(cliCtx)
+	if err != nil {
+		return api.Service{}, err
+	}
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/" + version + "/SearchBrowserService.graphql"))
 	request.Var("serviceCode", serviceCode)
 
 	err = client.ExecuteQuery(cliCtx, request, &response)
@@ -89,7 +109,7 @@ func SearchBrowserService(cliCtx *cli.Context, serviceCode string) (service api.
 func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit int) ([]api.Endpoint, error) {
 	var response map[string][]api.Endpoint
 
-	request := graphql.NewRequest(assets.Read("graphqls/metadata/SearchEndpoints.graphql"))
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/v1/SearchEndpoints.graphql"))
 	request.Var("serviceId", serviceID)
 	request.Var("keyword", keyword)
 	request.Var("limit", limit)
@@ -102,11 +122,15 @@ func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit int)
 func Instances(cliCtx *cli.Context, serviceID string, duration api.Duration) ([]api.ServiceInstance, error) {
 	var response map[string][]api.ServiceInstance
 
-	request := graphql.NewRequest(assets.Read("graphqls/metadata/Instances.graphql"))
+	version, err := protocolVersion(cliCtx)
+	if err != nil {
+		return nil, err
+	}
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/" + version + "/Instances.graphql"))
 	request.Var("serviceId", serviceID)
 	request.Var("duration", duration)
 
-	err := client.ExecuteQuery(cliCtx, request, &response)
+	err = client.ExecuteQuery(cliCtx, request, &response)
 
 	return response["result"], err
 }
@@ -114,7 +138,7 @@ func Instances(cliCtx *cli.Context, serviceID string, duration api.Duration) ([]
 func ServerTimeInfo(cliCtx *cli.Context) (api.TimeInfo, error) {
 	var response map[string]api.TimeInfo
 
-	request := graphql.NewRequest(assets.Read("graphqls/metadata/ServerTimeInfo.graphql"))
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/v2/ServerTimeInfo.graphql"))
 
 	if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
 		return api.TimeInfo{}, err
@@ -122,3 +146,51 @@ func ServerTimeInfo(cliCtx *cli.Context) (api.TimeInfo, error) {
 
 	return response["result"], nil
 }
+
+func ListLayers(cliCtx *cli.Context) ([]string, error) {
+	var response map[string][]string
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/v2/ListLayers.graphql"))
+
+	if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
+		return make([]string, 0), err
+	}
+
+	return response["result"], nil
+}
+
+func ListLayerService(cliCtx *cli.Context, layer string) ([]api.Service, error) {
+	var response map[string][]api.Service
+
+	request := graphql.NewRequest(assets.Read("graphqls/metadata/v2/ListService.graphql"))
+	request.Var("layer", layer)
+
+	err := client.ExecuteQuery(cliCtx, request, &response)
+
+	return response["result"], err
+}
+
+func protocolVersion(cliCtx *cli.Context) (string, error) {
+	if majorVersion, err := backendMajorVersion(cliCtx); err != nil {
+		return "", err
+	} else if majorVersion >= 9 {
+		return "v2", nil
+	}
+	return "v1", nil
+}
+
+func backendMajorVersion(cliCtx *cli.Context) (int, error) {
+	version, err := common.Version(cliCtx)
+	if err != nil {
+		return 0, err
+	}
+	if version == "" {
+		return 0, fmt.Errorf("failed to detect OAP version")
+	}
+	majorVersion := version[:strings.Index(version, ".")]
+	atoi, err := strconv.Atoi(majorVersion)
+	if err != nil {
+		return 0, err
+	}
+	return atoi, nil
+}
diff --git a/test/docker-compose.yml b/test/base/docker-compose.yml
similarity index 94%
copy from test/docker-compose.yml
copy to test/base/docker-compose.yml
index 0db3d4b..cf06d9b 100644
--- a/test/docker-compose.yml
+++ b/test/base/docker-compose.yml
@@ -42,9 +42,6 @@ services:
     volumes:
       - ./services/provider.py:/app.py
     entrypoint: [ "sw-python", "run", "python", "/app.py" ]
-    depends_on:
-      oap:
-        condition: service_healthy
     networks:
       - test
     healthcheck:
@@ -64,9 +61,6 @@ services:
     volumes:
       - ./services/consumer.py:/app.py
     entrypoint: [ "sw-python", "run", "python", "/app.py" ]
-    depends_on:
-      provider:
-        condition: service_healthy
     networks:
       - test
 
diff --git a/test/services/consumer.py b/test/base/services/consumer.py
similarity index 100%
rename from test/services/consumer.py
rename to test/base/services/consumer.py
diff --git a/test/services/provider.py b/test/base/services/provider.py
similarity index 100%
rename from test/services/provider.py
rename to test/base/services/provider.py
diff --git a/test/expected/dependency-service.yml b/test/cases/8.8.1/docker-compose.yml
similarity index 59%
copy from test/expected/dependency-service.yml
copy to test/cases/8.8.1/docker-compose.yml
index ef6ca45..ebd113c 100644
--- a/test/expected/dependency-service.yml
+++ b/test/cases/8.8.1/docker-compose.yml
@@ -13,25 +13,34 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-nodes:
-  {{- contains .nodes }}
-- id: {{ b64enc "provider" }}.1
-  name: provider
-  type: Python
-  isreal: true
-- id: {{ b64enc "consumer" }}.1
-  name: consumer
-  type: Python
-  isreal: true
-  {{- end }}
-calls:
-  {{- contains .calls }}
-- source: {{ b64enc "consumer" }}.1
-  sourcecomponents: [ ]
-  target: {{ b64enc "provider" }}.1
-  targetcomponents: [ ]
-  id: {{ b64enc "consumer" }}.1-{{ b64enc "provider"}}.1
-  detectpoints:
-    - CLIENT
-    - SERVER
-  {{- end }}
+version: '2.1'
+
+services:
+  oap:
+    extends:
+      file: ../../base/docker-compose.yml
+      service: oap
+    image: apache/skywalking-oap-server:8.8.1
+    ports:
+      - 12800
+
+  provider:
+    extends:
+      file: ../../base/docker-compose.yml
+      service: provider
+    depends_on:
+      oap:
+        condition: service_healthy
+
+  consumer:
+    extends:
+      file: ../../base/docker-compose.yml
+      service: consumer
+    ports:
+      - 9090
+    depends_on:
+      provider:
+        condition: service_healthy
+
+networks:
+  test:
diff --git a/test/expected/dashboard-global-metrics.yml b/test/cases/8.8.1/expected/dashboard-global-metrics.yml
similarity index 100%
copy from test/expected/dashboard-global-metrics.yml
copy to test/cases/8.8.1/expected/dashboard-global-metrics.yml
diff --git a/test/expected/dashboard-global.yml b/test/cases/8.8.1/expected/dashboard-global.yml
similarity index 100%
copy from test/expected/dashboard-global.yml
copy to test/cases/8.8.1/expected/dashboard-global.yml
diff --git a/test/expected/dependency-endpoint.yml b/test/cases/8.8.1/expected/dependency-endpoint.yml
similarity index 100%
copy from test/expected/dependency-endpoint.yml
copy to test/cases/8.8.1/expected/dependency-endpoint.yml
diff --git a/test/expected/dependency-instance.yml b/test/cases/8.8.1/expected/dependency-instance.yml
similarity index 100%
copy from test/expected/dependency-instance.yml
copy to test/cases/8.8.1/expected/dependency-instance.yml
diff --git a/test/expected/dependency-service.yml b/test/cases/8.8.1/expected/dependency-service.yml
similarity index 100%
copy from test/expected/dependency-service.yml
copy to test/cases/8.8.1/expected/dependency-service.yml
diff --git a/test/expected/empty-array.yml b/test/cases/8.8.1/expected/empty-array.yml
similarity index 100%
copy from test/expected/empty-array.yml
copy to test/cases/8.8.1/expected/empty-array.yml
diff --git a/test/expected/endpoint-list.yml b/test/cases/8.8.1/expected/endpoint-list.yml
similarity index 100%
copy from test/expected/endpoint-list.yml
copy to test/cases/8.8.1/expected/endpoint-list.yml
diff --git a/test/expected/instance-list.yml b/test/cases/8.8.1/expected/instance-list.yml
similarity index 98%
copy from test/expected/instance-list.yml
copy to test/cases/8.8.1/expected/instance-list.yml
index 310b83b..dc11872 100644
--- a/test/expected/instance-list.yml
+++ b/test/cases/8.8.1/expected/instance-list.yml
@@ -23,4 +23,5 @@
     {{- end }}
   language: {{ .language }}
   instanceuuid: {{ b64enc "provider" }}.1_{{ b64enc "provider1" }}
+  layer: ""
   {{- end }}
diff --git a/test/expected/metrics-has-value.yml b/test/cases/8.8.1/expected/metrics-has-value.yml
similarity index 100%
copy from test/expected/metrics-has-value.yml
copy to test/cases/8.8.1/expected/metrics-has-value.yml
diff --git a/test/expected/metrics-top-endpoint-sla-provider.yml b/test/cases/8.8.1/expected/metrics-top-endpoint-sla-provider.yml
similarity index 100%
copy from test/expected/metrics-top-endpoint-sla-provider.yml
copy to test/cases/8.8.1/expected/metrics-top-endpoint-sla-provider.yml
diff --git a/test/expected/metrics-top-service-sla.yml b/test/cases/8.8.1/expected/metrics-top-service-sla.yml
similarity index 100%
copy from test/expected/metrics-top-service-sla.yml
copy to test/cases/8.8.1/expected/metrics-top-service-sla.yml
diff --git a/test/expected/service-endpoint.yml b/test/cases/8.8.1/expected/service-endpoint.yml
similarity index 100%
copy from test/expected/service-endpoint.yml
copy to test/cases/8.8.1/expected/service-endpoint.yml
diff --git a/test/expected/service-provider.yml b/test/cases/8.8.1/expected/service-provider.yml
similarity index 96%
copy from test/expected/service-provider.yml
copy to test/cases/8.8.1/expected/service-provider.yml
index 0a4d631..7b5669a 100644
--- a/test/expected/service-provider.yml
+++ b/test/cases/8.8.1/expected/service-provider.yml
@@ -16,3 +16,5 @@
 - id: {{ b64enc "provider" }}.1
   name: provider
   group: ""
+  shortname: ""
+  layers: []
diff --git a/test/expected/service.yml b/test/cases/8.8.1/expected/service.yml
similarity index 94%
copy from test/expected/service.yml
copy to test/cases/8.8.1/expected/service.yml
index 598cedd..29e52a1 100644
--- a/test/expected/service.yml
+++ b/test/cases/8.8.1/expected/service.yml
@@ -17,7 +17,11 @@
 - id: {{ b64enc "provider" }}.1
   name: provider
   group: ""
+  shortname: ""
+  layers: []
 - id: {{ b64enc "consumer" }}.1
   name: consumer
   group: ""
+  shortname: ""
+  layers: []
   {{- end }}
diff --git a/test/expected/trace-users-detail.yml b/test/cases/8.8.1/expected/trace-users-detail.yml
similarity index 100%
copy from test/expected/trace-users-detail.yml
copy to test/cases/8.8.1/expected/trace-users-detail.yml
diff --git a/test/expected/traces-list.yml b/test/cases/8.8.1/expected/traces-list.yml
similarity index 100%
copy from test/expected/traces-list.yml
copy to test/cases/8.8.1/expected/traces-list.yml
diff --git a/test/expected/value.yml b/test/cases/8.8.1/expected/value.yml
similarity index 100%
copy from test/expected/value.yml
copy to test/cases/8.8.1/expected/value.yml
diff --git a/test/cases/8.8.1/test.yaml b/test/cases/8.8.1/test.yaml
new file mode 100644
index 0000000..4cfc28c
--- /dev/null
+++ b/test/cases/8.8.1/test.yaml
@@ -0,0 +1,99 @@
+# 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.
+
+# This file is used to show how to write configuration files and can be used to test.
+
+setup:
+  env: compose
+  file: docker-compose.yml
+  timeout: 1200
+  steps:
+    - name: install yq
+      command: yq > /dev/null 2>&1 || go install github.com/mikefarah/yq/v4@latest
+    - name: install swctl
+      command: make install DESTDIR=/usr/local/bin > /dev/null 2>&1
+
+trigger:
+  action: http
+  interval: 3s
+  times: 10
+  url: http://${consumer_host}:${consumer_9090}/users
+  method: POST
+
+verify:
+  retry:
+    count: 200
+    interval: 5s
+  cases:
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dashboard global
+      expected: expected/dashboard-global.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dashboard global-metrics
+      expected: expected/dashboard-global-metrics.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dependency endpoint --service-name provider --endpoint-name /users
+      expected: expected/dependency-endpoint.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dependency instance --service-name consumer --dest-service-name provider
+      expected: expected/dependency-instance.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dependency service --service-name consumer
+      expected: expected/dependency-service.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql endpoint ls --service-name provider
+      expected: expected/endpoint-list.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql instance ls --service-name provider
+      expected: expected/instance-list.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql instance search --service-name provider --regex ".*vid.*"
+      expected: expected/instance-list.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql instance search --service-name provider --regex not-exist
+      expected: expected/empty-array.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name service_sla 5
+      expected: expected/metrics-top-service-sla.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name endpoint_sla --service-name provider 5
+      expected: expected/metrics-top-endpoint-sla-provider.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_instance_resp_time --service-name provider --instance-name provider1 | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_resp_time --service-name provider | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=endpoint_sla --service-name provider --endpoint-name /users | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_relation_client_cpm --service-name consumer --dest-service-name provider | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_instance_relation_client_cpm --service-name consumer --instance-name consumer1 --dest-service-name provider --dest-instance-name provider1 | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name endpoint_cpm --service-name provider --endpoint-name /users
+      expected: expected/value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name service_cpm --service-name provider
+      expected: expected/value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name endpoint_cpm --service-name provider --endpoint-name /users
+      expected: expected/value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name service_instance_cpm --service-name provider --instance-name provider1
+      expected: expected/value.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql service list
+      expected: expected/service.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql service list provider
+      expected: expected/service-provider.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls
+      expected: expected/traces-list.yml
+    - query: |
+        swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace $( \
+          swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls \
+            | yq e '.traces | select(.[].endpointnames[0]=="/users") | .[0].traceids[0]' -
+        )
+      expected: expected/trace-users-detail.yml
diff --git a/test/docker-compose.yml b/test/cases/9.0.0/docker-compose.yml
similarity index 53%
rename from test/docker-compose.yml
rename to test/cases/9.0.0/docker-compose.yml
index 0db3d4b..d334b97 100644
--- a/test/docker-compose.yml
+++ b/test/cases/9.0.0/docker-compose.yml
@@ -17,58 +17,35 @@ version: '2.1'
 
 services:
   oap:
-    image: apache/skywalking-oap-server:8.8.1
+    extends:
+      file: ../../base/docker-compose.yml
+      service: oap
+    image: ghcr.io/apache/skywalking/oap:e97b2d2165703c9d5bcdb0556134aa7cbd1382f5
     ports:
-      - 11800
       - 12800
-    networks:
-      - test
-    environment:
-      - SW_STORAGE=h2
-      - SW_HEALTH_CHECKER=default
-      - SW_TELEMETRY=prometheus
     healthcheck:
-      test: [ "CMD", "sh", "-c", "nc -zn 127.0.0.1 11800" ]
+      test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/12800" ]
       interval: 5s
       timeout: 60s
       retries: 120
 
   provider:
-    image: apache/skywalking-python:0.7.0-grpc-py3.9
-    environment:
-      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=oap:11800
-      - SW_AGENT_NAME=provider
-      - SW_AGENT_INSTANCE=provider1
-    volumes:
-      - ./services/provider.py:/app.py
-    entrypoint: [ "sw-python", "run", "python", "/app.py" ]
+    extends:
+      file: ../../base/docker-compose.yml
+      service: provider
     depends_on:
       oap:
         condition: service_healthy
-    networks:
-      - test
-    healthcheck:
-      test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9091" ]
-      interval: 5s
-      timeout: 60s
-      retries: 120
 
   consumer:
-    image: apache/skywalking-python:0.7.0-grpc-py3.9
+    extends:
+      file: ../../base/docker-compose.yml
+      service: consumer
     ports:
       - 9090
-    environment:
-      - SW_AGENT_COLLECTOR_BACKEND_SERVICES=oap:11800
-      - SW_AGENT_NAME=consumer
-      - SW_AGENT_INSTANCE=consumer1
-    volumes:
-      - ./services/consumer.py:/app.py
-    entrypoint: [ "sw-python", "run", "python", "/app.py" ]
     depends_on:
       provider:
         condition: service_healthy
-    networks:
-      - test
 
 networks:
   test:
diff --git a/test/expected/dashboard-global-metrics.yml b/test/cases/9.0.0/expected/dashboard-global-metrics.yml
similarity index 100%
rename from test/expected/dashboard-global-metrics.yml
rename to test/cases/9.0.0/expected/dashboard-global-metrics.yml
diff --git a/test/expected/dashboard-global.yml b/test/cases/9.0.0/expected/dashboard-global.yml
similarity index 100%
rename from test/expected/dashboard-global.yml
rename to test/cases/9.0.0/expected/dashboard-global.yml
diff --git a/test/expected/dependency-endpoint.yml b/test/cases/9.0.0/expected/dependency-endpoint.yml
similarity index 100%
rename from test/expected/dependency-endpoint.yml
rename to test/cases/9.0.0/expected/dependency-endpoint.yml
diff --git a/test/expected/dependency-instance.yml b/test/cases/9.0.0/expected/dependency-instance.yml
similarity index 100%
rename from test/expected/dependency-instance.yml
rename to test/cases/9.0.0/expected/dependency-instance.yml
diff --git a/test/expected/dependency-service.yml b/test/cases/9.0.0/expected/dependency-service.yml
similarity index 100%
rename from test/expected/dependency-service.yml
rename to test/cases/9.0.0/expected/dependency-service.yml
diff --git a/test/expected/empty-array.yml b/test/cases/9.0.0/expected/empty-array.yml
similarity index 100%
copy from test/expected/empty-array.yml
copy to test/cases/9.0.0/expected/empty-array.yml
diff --git a/test/expected/endpoint-list.yml b/test/cases/9.0.0/expected/endpoint-list.yml
similarity index 100%
rename from test/expected/endpoint-list.yml
rename to test/cases/9.0.0/expected/endpoint-list.yml
diff --git a/test/expected/instance-list.yml b/test/cases/9.0.0/expected/instance-list.yml
similarity index 98%
rename from test/expected/instance-list.yml
rename to test/cases/9.0.0/expected/instance-list.yml
index 310b83b..870f57b 100644
--- a/test/expected/instance-list.yml
+++ b/test/cases/9.0.0/expected/instance-list.yml
@@ -23,4 +23,5 @@
     {{- end }}
   language: {{ .language }}
   instanceuuid: {{ b64enc "provider" }}.1_{{ b64enc "provider1" }}
+  layer: GENERAL
   {{- end }}
diff --git a/test/expected/empty-array.yml b/test/cases/9.0.0/expected/layer-list.yml
similarity index 98%
rename from test/expected/empty-array.yml
rename to test/cases/9.0.0/expected/layer-list.yml
index 8d30b98..9ab52ca 100644
--- a/test/expected/empty-array.yml
+++ b/test/cases/9.0.0/expected/layer-list.yml
@@ -13,4 +13,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-[ ]
+- GENERAL
diff --git a/test/expected/metrics-has-value.yml b/test/cases/9.0.0/expected/metrics-has-value.yml
similarity index 100%
rename from test/expected/metrics-has-value.yml
rename to test/cases/9.0.0/expected/metrics-has-value.yml
diff --git a/test/expected/metrics-top-endpoint-sla-provider.yml b/test/cases/9.0.0/expected/metrics-top-endpoint-sla-provider.yml
similarity index 100%
rename from test/expected/metrics-top-endpoint-sla-provider.yml
rename to test/cases/9.0.0/expected/metrics-top-endpoint-sla-provider.yml
diff --git a/test/expected/metrics-top-service-sla.yml b/test/cases/9.0.0/expected/metrics-top-service-sla.yml
similarity index 100%
rename from test/expected/metrics-top-service-sla.yml
rename to test/cases/9.0.0/expected/metrics-top-service-sla.yml
diff --git a/test/expected/service-endpoint.yml b/test/cases/9.0.0/expected/service-endpoint.yml
similarity index 100%
rename from test/expected/service-endpoint.yml
rename to test/cases/9.0.0/expected/service-endpoint.yml
diff --git a/test/expected/service-provider.yml b/test/cases/9.0.0/expected/service-provider.yml
similarity index 94%
rename from test/expected/service-provider.yml
rename to test/cases/9.0.0/expected/service-provider.yml
index 0a4d631..e81ad75 100644
--- a/test/expected/service-provider.yml
+++ b/test/cases/9.0.0/expected/service-provider.yml
@@ -16,3 +16,6 @@
 - id: {{ b64enc "provider" }}.1
   name: provider
   group: ""
+  shortname: provider
+  layers:
+    - GENERAL
diff --git a/test/expected/service.yml b/test/cases/9.0.0/expected/service.yml
similarity index 91%
rename from test/expected/service.yml
rename to test/cases/9.0.0/expected/service.yml
index 598cedd..b1b7cbf 100644
--- a/test/expected/service.yml
+++ b/test/cases/9.0.0/expected/service.yml
@@ -17,7 +17,13 @@
 - id: {{ b64enc "provider" }}.1
   name: provider
   group: ""
+  shortname: provider
+  layers:
+    - GENERAL
 - id: {{ b64enc "consumer" }}.1
   name: consumer
   group: ""
+  shortname: consumer
+  layers:
+    - GENERAL
   {{- end }}
diff --git a/test/expected/trace-users-detail.yml b/test/cases/9.0.0/expected/trace-users-detail.yml
similarity index 100%
rename from test/expected/trace-users-detail.yml
rename to test/cases/9.0.0/expected/trace-users-detail.yml
diff --git a/test/expected/traces-list.yml b/test/cases/9.0.0/expected/traces-list.yml
similarity index 100%
rename from test/expected/traces-list.yml
rename to test/cases/9.0.0/expected/traces-list.yml
diff --git a/test/expected/value.yml b/test/cases/9.0.0/expected/value.yml
similarity index 100%
rename from test/expected/value.yml
rename to test/cases/9.0.0/expected/value.yml
diff --git a/test/cases/9.0.0/test.yaml b/test/cases/9.0.0/test.yaml
new file mode 100644
index 0000000..b40d07f
--- /dev/null
+++ b/test/cases/9.0.0/test.yaml
@@ -0,0 +1,104 @@
+# 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.
+
+# This file is used to show how to write configuration files and can be used to test.
+
+setup:
+  env: compose
+  file: docker-compose.yml
+  timeout: 1200
+  steps:
+    - name: install yq
+      command: yq > /dev/null 2>&1 || go install github.com/mikefarah/yq/v4@latest
+    - name: install swctl
+      command: make install DESTDIR=/usr/local/bin > /dev/null 2>&1
+
+trigger:
+  action: http
+  interval: 3s
+  times: 10
+  url: http://${consumer_host}:${consumer_9090}/users
+  method: POST
+
+verify:
+  retry:
+    count: 200
+    interval: 5s
+  cases:
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dashboard global
+      expected: expected/dashboard-global.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dashboard global-metrics
+      expected: expected/dashboard-global-metrics.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dependency endpoint --service-name provider --endpoint-name /users
+      expected: expected/dependency-endpoint.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dependency instance --service-name consumer --dest-service-name provider
+      expected: expected/dependency-instance.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql dependency service --service-name consumer
+      expected: expected/dependency-service.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql endpoint ls --service-name provider
+      expected: expected/endpoint-list.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql instance ls --service-name provider
+      expected: expected/instance-list.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql instance search --service-name provider --regex ".*vid.*"
+      expected: expected/instance-list.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql instance search --service-name provider --regex not-exist
+      expected: expected/empty-array.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name service_sla 5
+      expected: expected/metrics-top-service-sla.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name endpoint_sla --service-name provider 5
+      expected: expected/metrics-top-endpoint-sla-provider.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_instance_resp_time --service-name provider --instance-name provider1 | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_resp_time --service-name provider | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=endpoint_sla --service-name provider --endpoint-name /users | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_relation_client_cpm --service-name consumer --dest-service-name provider | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics linear --name=service_instance_relation_client_cpm --service-name consumer --instance-name consumer1 --dest-service-name provider --dest-instance-name provider1 | yq e 'to_entries' -
+      expected: expected/metrics-has-value.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name endpoint_cpm --service-name provider --endpoint-name /users
+      expected: expected/value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name service_cpm --service-name provider
+      expected: expected/value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name endpoint_cpm --service-name provider --endpoint-name /users
+      expected: expected/value.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics single --name service_instance_cpm --service-name provider --instance-name provider1
+      expected: expected/value.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql layer list
+      expected: expected/layer-list.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql service list
+      expected: expected/service.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql service list provider
+      expected: expected/service-provider.yml
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql service layer GENERAL
+      expected: expected/service.yml
+
+    - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls
+      expected: expected/traces-list.yml
+    - query: |
+        swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace $( \
+          swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql trace ls \
+            | yq e '.traces | select(.[].endpointnames[0]=="/users") | .[0].traceids[0]' -
+        )
+      expected: expected/trace-users-detail.yml