You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sh...@apache.org on 2021/07/20 22:48:26 UTC

[apisix] branch master updated: ci: refactor chaos test with ginkgo (#4560)

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

shuyangw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 5d7753e  ci: refactor chaos test with ginkgo (#4560)
5d7753e is described below

commit 5d7753ef6ab93509372b1f8f0b0f255e3511c4fc
Author: Shuyang Wu <wo...@gmail.com>
AuthorDate: Tue Jul 20 18:48:21 2021 -0400

    ci: refactor chaos test with ginkgo (#4560)
---
 .github/actions/action-tmate             |   1 +
 .github/workflows/chaos.yml              |  46 +++++---
 .gitmodules                              |   3 +
 t/chaos/e2e.go                           |  24 ++++
 t/chaos/e2e_test.go                      |  31 +++++
 t/chaos/go.mod                           |  15 ++-
 t/chaos/go.sum                           |  63 ++++------
 t/chaos/kill-etcd.yaml                   |  29 -----
 t/chaos/kill-etcd_test.go                | 127 ---------------------
 t/chaos/killetcd/killetcd.go             | 190 +++++++++++++++++++++++++++++++
 t/chaos/{ => utils}/kube_utils.go        |  50 ++++----
 t/chaos/{ => utils}/setup_chaos_utils.sh |  26 +----
 t/chaos/{ => utils}/utils.go             |  70 ++++++------
 13 files changed, 388 insertions(+), 287 deletions(-)

diff --git a/.github/actions/action-tmate b/.github/actions/action-tmate
new file mode 160000
index 0000000..079a16b
--- /dev/null
+++ b/.github/actions/action-tmate
@@ -0,0 +1 @@
+Subproject commit 079a16b22b8bcc5dd231a42d9a5e8e48db564688
diff --git a/.github/workflows/chaos.yml b/.github/workflows/chaos.yml
index 15998a3..8f2ac08 100644
--- a/.github/workflows/chaos.yml
+++ b/.github/workflows/chaos.yml
@@ -19,14 +19,14 @@ jobs:
         with:
           submodules: recursive
 
-      - name: setup go
+      - name: Setup go
         uses: actions/setup-go@v2.1.3
         with:
           go-version: "1.14"
 
       - name: Creating minikube cluster
         run: |
-          bash ./t/chaos/setup_chaos_utils.sh start_minikube
+          bash ./t/chaos/utils/setup_chaos_utils.sh start_minikube
           wget https://raw.githubusercontent.com/apache/apisix-docker/master/alpine-local/Dockerfile
           mkdir logs
           docker build -t apache/apisix:alpine-local --build-arg APISIX_PATH=. -f Dockerfile .
@@ -40,30 +40,42 @@ jobs:
           kubectl get pods -n kube-system
           kubectl version
 
-      - name: Deploy Etcd Operator
+      - name: Deploy etcd with Helm
         run: |
-          git clone https://github.com/api7/etcd-operator.git --depth 1
-          bash etcd-operator/example/rbac/create_role.sh
-          kubectl create -f etcd-operator/example/deployment.yaml
-          bash ./t/chaos/setup_chaos_utils.sh ensure_pods_ready etcd-operator "True" 30
-          kubectl create -f etcd-operator/example/example-etcd-cluster.yaml
-          bash ./t/chaos/setup_chaos_utils.sh ensure_pods_ready etcd "True True True" 30
+          curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
+          helm repo add bitnami https://charts.bitnami.com/bitnami
+          helm install etcd bitnami/etcd --set replicaCount=3 --set auth.rbac.enabled=false
+          kubectl wait pods -l app.kubernetes.io/instance=etcd --for=condition=Ready --timeout=300s --all
+
 
       - name: Deploy APISIX
         run: |
-          bash ./t/chaos/setup_chaos_utils.sh modify_config
+          bash ./t/chaos/utils/setup_chaos_utils.sh modify_config
           kubectl create configmap apisix-gw-config.yaml --from-file=./conf/config.yaml
           kubectl apply -f ./kubernetes/deployment.yaml
           kubectl apply -f ./kubernetes/service.yaml
-          bash ./t/chaos/setup_chaos_utils.sh ensure_pods_ready apisix-gw "True" 30
+          kubectl wait pods -l app=apisix-gw --for=condition=Ready --timeout=300s
           kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml
-          bash ./t/chaos/setup_chaos_utils.sh ensure_pods_ready httpbin "True" 30
-          nohup kubectl port-forward svc/apisix-gw-lb 9080:9080 >/dev/null 2>&1 &
+          kubectl wait pods -l app=httpbin --for=condition=Ready --timeout=300s
+          bash ./t/chaos/utils/setup_chaos_utils.sh port_forward
+
+      - name: Deploy Chaos Mesh
+        run: |
+          curl -sSL https://mirrors.chaos-mesh.org/latest/install.sh | bash
 
-      - name: Deploy Chaos mesh
+
+      - name: Install Ginkgo
         run: |
-          curl -sSL https://mirrors.chaos-mesh.org/v1.1.1/install.sh | bash
+          go get -u github.com/onsi/ginkgo/ginkgo
+          sudo cp ~/go/bin/ginkgo /usr/local/bin
 
-      - name: run test
+      - name: Run test
         working-directory: ./t/chaos
-        run: go test -v
+        run: ginkgo -r --v --progress --trace
+
+      # Debug via SSH if previous steps failed
+      - name: Set up tmate session
+        if: ${{ failure() }}
+        uses: ./.github/actions/action-tmate
+        with:
+          timeout-minutes: 15
diff --git a/.gitmodules b/.gitmodules
index beb354b..78dcdd8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "t/toolkit"]
 	path = t/toolkit
 	url = https://github.com/api7/test-toolkit.git
+[submodule ".github/actions/action-tmate"]
+	path = .github/actions/action-tmate
+	url = https://github.com/mxschmitt/action-tmate
diff --git a/t/chaos/e2e.go b/t/chaos/e2e.go
new file mode 100644
index 0000000..d0cd543
--- /dev/null
+++ b/t/chaos/e2e.go
@@ -0,0 +1,24 @@
+/*
+ * 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 e2e
+
+import (
+	_ "github.com/apache/apisix/t/chaos/killetcd"
+)
+
+func runChaos() {}
diff --git a/t/chaos/e2e_test.go b/t/chaos/e2e_test.go
new file mode 100644
index 0000000..2a1a420
--- /dev/null
+++ b/t/chaos/e2e_test.go
@@ -0,0 +1,31 @@
+/*
+ * 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 e2e
+
+import (
+	"testing"
+
+	"github.com/onsi/ginkgo"
+	"github.com/onsi/gomega"
+)
+
+func TestRunChaos(t *testing.T) {
+	runChaos()
+	gomega.RegisterFailHandler(ginkgo.Fail)
+	ginkgo.RunSpecs(t, "chaos test suites")
+}
diff --git a/t/chaos/go.mod b/t/chaos/go.mod
index 336be32..efd5cc7 100644
--- a/t/chaos/go.mod
+++ b/t/chaos/go.mod
@@ -1,14 +1,27 @@
 module github.com/apache/apisix/t/chaos
 
 require (
+	github.com/ajg/form v1.5.1 // indirect
 	github.com/chaos-mesh/chaos-mesh v1.1.1
-	github.com/gavv/httpexpect/v2 v2.1.0
+	github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
+	github.com/fatih/structs v1.1.0 // indirect
+	github.com/gavv/httpexpect v2.0.0+incompatible
+	github.com/imkira/go-interpol v1.1.0 // indirect
+	github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
+	github.com/moul/http2curl v1.0.0 // indirect
+	github.com/onsi/ginkgo v1.12.0
 	github.com/onsi/gomega v1.9.0
 	github.com/pkg/errors v0.9.1
+	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+	github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
+	github.com/yudai/gojsondiff v1.0.0 // indirect
+	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
+	github.com/yudai/pp v2.0.1+incompatible // indirect
 	k8s.io/api v0.17.0
 	k8s.io/apimachinery v0.17.0
 	k8s.io/client-go v0.17.0
 	k8s.io/kubectl v0.0.0
+	k8s.io/kubernetes v1.17.2
 	sigs.k8s.io/controller-runtime v0.4.0
 )
 
diff --git a/t/chaos/go.sum b/t/chaos/go.sum
index 5820e86..22e5383 100644
--- a/t/chaos/go.sum
+++ b/t/chaos/go.sum
@@ -128,7 +128,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
 github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -162,20 +161,20 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
 github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
 github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
-github.com/fasthttp/websocket v1.4.2 h1:AU/zSiIIAuJjBMf5o+vO0syGOnEfvZRu40xIhW/3RuM=
-github.com/fasthttp/websocket v1.4.2/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y=
+github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc=
+github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
 github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
-github.com/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU=
-github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/gavv/httpexpect/v2 v2.1.0 h1:Q7xnFuKqBY2si4DsqxdbWBt9rfrbVTT2/9YSomc9tEw=
-github.com/gavv/httpexpect/v2 v2.1.0/go.mod h1:lnd0TqJLrP+wkJk3SFwtrpSlOAZQ7HaaIFuOYbgqgUM=
+github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
+github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
@@ -298,7 +297,6 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
 github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -358,12 +356,11 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC
 github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
 github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
 github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.0.0 h1:J/mA+d2LqcDKjAEhQjXDHt9/e7Cnm+oBUwgHp5C6XDg=
-github.com/gorilla/websocket v1.0.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -409,8 +406,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imkira/go-interpol v1.0.0 h1:HrmLyvOLJyjR0YofMw8QGdCIuYOs4TJUBDNU5sJC09E=
-github.com/imkira/go-interpol v1.0.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
+github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
+github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
 github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
@@ -428,6 +425,7 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
 github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
@@ -439,13 +437,11 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
 github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E=
 github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
-github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
-github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -480,14 +476,12 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
 github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
@@ -532,6 +526,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
 github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
+github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
 github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
 github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -549,10 +545,8 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
 github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
 github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
@@ -561,7 +555,6 @@ github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5
 github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
@@ -635,11 +628,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY=
-github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
-github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44 h1:tB9NOR21++IjLyVx3/PCPhWMwqGNCMQEH96A6dMZ/gc=
 github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -655,8 +645,10 @@ github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjM
 github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
@@ -689,7 +681,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -721,9 +712,8 @@ github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2
 github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.2.0 h1:dzZJf2IuMiclVjdw0kkT+f9u4YdrapbNyGAN47E/qnk=
 github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
-github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
-github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
 github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
@@ -734,8 +724,8 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg=
-github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
@@ -829,7 +819,6 @@ golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 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 h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -853,7 +842,6 @@ golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -866,7 +854,6 @@ golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -879,7 +866,6 @@ golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5h
 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=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -890,7 +876,6 @@ golang.org/x/sys v0.0.0-20190122071731-054c452bb702/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -916,7 +901,6 @@ golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa h1:mQTN3ECqfsViCNBgq+A40vdwh
 golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -975,7 +959,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200309202150-20ab64c0d93f h1:NbrfHxef+IfdI86qCgO/1Siq1BuMH2xG0NqgvCguRhQ=
 golang.org/x/tools v0.0.0-20200309202150-20ab64c0d93f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
 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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@@ -1026,7 +1009,6 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4
 gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM7FIKyzVzGhjB/qfYo=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
@@ -1052,10 +1034,8 @@ gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1078,6 +1058,7 @@ k8s.io/apiextensions-apiserver v0.17.0 h1:+XgcGxqaMztkbbvsORgCmHIb4uImHKvTjNyu7b
 k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
 k8s.io/apimachinery v0.17.1-beta.0 h1:0Wl/KpAiFOMe9to5h8x2Y6JnjV+BEWJiTcUk1Vx7zdE=
 k8s.io/apimachinery v0.17.1-beta.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
+k8s.io/apiserver v0.17.0 h1:XhUix+FKFDcBygWkQNp7wKKvZL030QUlH1o8vFeSgZA=
 k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
 k8s.io/cli-runtime v0.17.0/go.mod h1:1E5iQpMODZq2lMWLUJELwRu2MLWIzwvMgDBpn3Y81Qo=
 k8s.io/client-go v0.17.0 h1:8QOGvUGdqDMFrm9sD6IUFl256BcffynGoe80sxgTEDg=
@@ -1085,6 +1066,7 @@ k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
 k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE=
 k8s.io/cluster-bootstrap v0.17.0/go.mod h1:KnxktBWGyKlBDaHLC8zzu0EPt/HJ9Lcs7bNM2WvUHSs=
 k8s.io/code-generator v0.17.1-beta.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
+k8s.io/component-base v0.17.0 h1:BnDFcmBDq+RPpxXjmuYnZXb59XNN9CaFrX8ba9+3xrA=
 k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
 k8s.io/cri-api v0.17.1-beta.0/go.mod h1:BzAkbBHHp81d+aXzbiIcUbilLkbXa40B8mUHOk6EX3s=
 k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls=
@@ -1104,6 +1086,7 @@ k8s.io/kube-scheduler v0.17.0/go.mod h1:mZVsEg++qnq6xWm9DTh2bw9v2i9XPdkEQGDafcjG
 k8s.io/kubectl v0.17.0 h1:xD4EWlL+epc/JTO1gvSjmV9yiYF0Z2wiHK2DIek6URY=
 k8s.io/kubectl v0.17.0/go.mod h1:jIPrUAW656Vzn9wZCCe0PC+oTcu56u2HgFD21Xbfk1s=
 k8s.io/kubelet v0.17.0/go.mod h1:e/JBCxucKuEV6JO6zYW+e72ib9eMsGO2Fah3iT5tiiI=
+k8s.io/kubernetes v1.17.2 h1:g1UFZqFQsYx88xMUks4PKC6tsNcekxe0v06fcVGRwVE=
 k8s.io/kubernetes v1.17.2/go.mod h1:NbNV+69yL3eKiKDJ+ZEjqOplN3BFXKBeunzkoOy8WLo=
 k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8=
 k8s.io/metrics v0.17.0/go.mod h1:EH1D3YAwN6d7bMelrElnLhLg72l/ERStyv2SIQVt6Do=
@@ -1118,8 +1101,6 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
 modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
 modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
-moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e h1:C7q+e9M5nggAvWfVg9Nl66kebKeuJlP3FD58V4RR5wo=
-moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e/go.mod h1:nejbQVfXh96n9dSF6cH3Jsk/QI1Z2oEL7sSI2ifXFNA=
 mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
 mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
 mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
diff --git a/t/chaos/kill-etcd.yaml b/t/chaos/kill-etcd.yaml
deleted file mode 100644
index 98c41f6..0000000
--- a/t/chaos/kill-etcd.yaml
+++ /dev/null
@@ -1,29 +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.
-#
-
-apiVersion: chaos-mesh.org/v1alpha1
-kind: PodChaos
-metadata:
-  name: kill-etcd
-spec:
-  action: pod-kill
-  mode: all
-  selector:
-    labelSelectors:
-      "app": "etcd"
-  scheduler:
-      cron: "@every 10m"
diff --git a/t/chaos/kill-etcd_test.go b/t/chaos/kill-etcd_test.go
deleted file mode 100644
index 44bd9d5..0000000
--- a/t/chaos/kill-etcd_test.go
+++ /dev/null
@@ -1,127 +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 chaos
-
-import (
-	"context"
-	"net/http"
-	"strings"
-	"testing"
-	"time"
-
-	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
-	"github.com/gavv/httpexpect/v2"
-	. "github.com/onsi/gomega"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-)
-
-func createEtcdKillChaos(g *WithT, cli client.Client) {
-	chaos := &v1alpha1.PodChaos{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "kill-etcd",
-			Namespace: metav1.NamespaceDefault,
-		},
-		Spec: v1alpha1.PodChaosSpec{
-			Selector: v1alpha1.SelectorSpec{
-				LabelSelectors: map[string]string{"app": "etcd"},
-			},
-			Action: v1alpha1.PodKillAction,
-			Mode:   v1alpha1.AllPodMode,
-			Scheduler: &v1alpha1.SchedulerSpec{
-				Cron: "@every 10m",
-			},
-		},
-	}
-
-	err := cli.Create(context.Background(), chaos)
-	g.Expect(err).To(BeNil())
-}
-
-func TestGetSuccessWhenEtcdKilled(t *testing.T) {
-	g := NewWithT(t)
-	e := httpexpect.New(t, host)
-	cliSet := initClientSet(g)
-
-	eSilent := httpexpect.WithConfig(httpexpect.Config{
-		BaseURL:  host,
-		Reporter: httpexpect.NewAssertReporter(t),
-		Printers: []httpexpect.Printer{
-			newSilentPrinter(t),
-		},
-	})
-
-	// check if everything works
-	setRoute(e, http.StatusCreated)
-	getRouteList(e, http.StatusOK)
-
-	// to avoid route haven't been set yet
-	time.Sleep(10 * time.Second)
-	getRoute(e, http.StatusOK)
-	testPrometheusEtcdMetric(e, 1)
-
-	// run in background
-	go func() {
-		for {
-			go getRoute(eSilent, http.StatusOK)
-			time.Sleep(100 * time.Millisecond)
-		}
-	}()
-
-	// wait 1 seconds to let first route access returns
-	time.Sleep(1 * time.Second)
-	bandwidthBefore, durationBefore := getEgressBandwidthPerSecond(e, g)
-	bpsBefore := bandwidthBefore / durationBefore
-	g.Expect(bpsBefore).NotTo(BeZero())
-
-	listOption := client.MatchingLabels{"app": "apisix-gw"}
-	apisixPod := getPod(g, cliSet.ctrlCli, metav1.NamespaceDefault, listOption)
-
-	t.Run("error log not contains etcd error", func(t *testing.T) {
-		errorLog, err := log(apisixPod, cliSet.kubeCli)
-		g.Expect(err).To(BeNil())
-		g.Expect(strings.Contains(errorLog, "failed to fetch data from etcd")).To(BeFalse())
-	})
-
-	// apply chaos to kill all etcd pods
-	t.Run("kill all etcd pods", func(t *testing.T) {
-		createEtcdKillChaos(g, cliSet.ctrlCli)
-		time.Sleep(3 * time.Second)
-	})
-
-	// fail to set route since etcd is all killed
-	// while get route could still succeed
-	setRoute(e, http.StatusInternalServerError)
-	getRoute(e, http.StatusOK)
-	testPrometheusEtcdMetric(e, 0)
-
-	t.Run("error log contains etcd error", func(t *testing.T) {
-		errorLog, err := log(apisixPod, cliSet.kubeCli)
-		g.Expect(err).To(BeNil())
-		g.Expect(strings.Contains(errorLog, "failed to fetch data from etcd")).To(BeTrue())
-	})
-
-	bandwidthAfter, durationAfter := getEgressBandwidthPerSecond(e, g)
-	bpsAfter := bandwidthAfter / durationAfter
-	t.Run("ingress bandwidth per second not change much", func(t *testing.T) {
-		t.Logf("bandwidth before: %f, after: %f", bandwidthBefore, bandwidthAfter)
-		t.Logf("duration before: %f, after: %f", durationBefore, durationAfter)
-		t.Logf("bps before: %f, after: %f", bpsBefore, bpsAfter)
-		g.Expect(roughCompare(bpsBefore, bpsAfter)).To(BeTrue())
-	})
-}
diff --git a/t/chaos/killetcd/killetcd.go b/t/chaos/killetcd/killetcd.go
new file mode 100644
index 0000000..b5170cd
--- /dev/null
+++ b/t/chaos/killetcd/killetcd.go
@@ -0,0 +1,190 @@
+/*
+ * 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 killetcd
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
+	"github.com/gavv/httpexpect"
+	"github.com/onsi/ginkgo"
+	"github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/kubernetes/pkg/api/v1/pod"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/apache/apisix/t/chaos/utils"
+)
+
+var (
+	bandwidthBefore float64
+	durationBefore  float64
+	bpsBefore       float64
+	bandwidthAfter  float64
+	durationAfter   float64
+	bpsAfter        float64
+)
+
+func getEtcdKillChaos() *v1alpha1.PodChaos {
+	return &v1alpha1.PodChaos{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "kill-etcd",
+			Namespace: metav1.NamespaceDefault,
+		},
+		Spec: v1alpha1.PodChaosSpec{
+			Selector: v1alpha1.SelectorSpec{
+				LabelSelectors: map[string]string{"app.kubernetes.io/instance": "etcd"},
+			},
+			Action: v1alpha1.PodKillAction,
+			Mode:   v1alpha1.AllPodMode,
+			Scheduler: &v1alpha1.SchedulerSpec{
+				Cron: "@every 10m",
+			},
+		},
+	}
+}
+
+var _ = ginkgo.Describe("Test Get Success When Etcd Got Killed", func() {
+	e := httpexpect.New(ginkgo.GinkgoT(), utils.Host)
+	eSilent := httpexpect.WithConfig(httpexpect.Config{
+		BaseURL:  utils.Host,
+		Reporter: httpexpect.NewAssertReporter(ginkgo.GinkgoT()),
+		Printers: []httpexpect.Printer{
+			utils.NewSilentPrinter(ginkgo.GinkgoT()),
+		},
+	})
+
+	var cliSet *utils.ClientSet
+	var apisixPod *v1.Pod
+	var err error
+	ginkgo.It("init client set", func() {
+		cliSet, err = utils.InitClientSet()
+		gomega.Expect(err).To(gomega.BeNil())
+		listOption := client.MatchingLabels{"app": "apisix-gw"}
+		apisixPods, err := utils.GetPods(cliSet.CtrlCli, metav1.NamespaceDefault, listOption)
+		gomega.Expect(err).To(gomega.BeNil())
+		gomega.Ω(len(apisixPods)).Should(gomega.BeNumerically(">", 0))
+		apisixPod = &apisixPods[0]
+	})
+
+	stopChan := make(chan bool)
+	defer ginkgo.It("restore test environment", func() {
+		stopChan <- true
+		cliSet.CtrlCli.Delete(context.Background(), getEtcdKillChaos())
+		utils.DeleteRoute(e)
+	})
+
+	ginkgo.It("check if everything works", func() {
+		utils.SetRoute(e, httpexpect.Status2xx)
+		utils.GetRouteList(e, http.StatusOK)
+		time.Sleep(1 * time.Second)
+		utils.GetRoute(e, http.StatusOK)
+		utils.TestPrometheusEtcdMetric(e, 1)
+	})
+
+	ginkgo.It("run request in background", func() {
+		go func() {
+			defer ginkgo.GinkgoRecover()
+			for {
+				go func() {
+					defer ginkgo.GinkgoRecover()
+					utils.GetRoute(eSilent, http.StatusOK)
+				}()
+				time.Sleep(100 * time.Millisecond)
+				stopLoop := false
+				select {
+				case <-stopChan:
+					stopLoop = true
+				default:
+				}
+				if stopLoop {
+					break
+				}
+			}
+		}()
+	})
+	// wait 1 seconds to let first route access returns
+	time.Sleep(1 * time.Second)
+
+	ginkgo.It("get stats before kill etcd", func() {
+		timeStart := time.Now()
+		bandwidthBefore, durationBefore = utils.GetEgressBandwidthPerSecond(e)
+		bpsBefore = bandwidthBefore / durationBefore
+		gomega.Expect(bpsBefore).NotTo(gomega.BeZero())
+
+		errorLog, err := utils.Log(apisixPod, cliSet.KubeCli, timeStart)
+		gomega.Expect(err).To(gomega.BeNil())
+		gomega.Ω(errorLog).ShouldNot(gomega.ContainSubstring("no healthy etcd endpoint available"))
+	})
+
+	// apply chaos to kill all etcd pods
+	ginkgo.It("kill all etcd pods", func() {
+		chaos := getEtcdKillChaos()
+		err := cliSet.CtrlCli.Create(context.Background(), chaos.DeepCopy())
+		gomega.Expect(err).To(gomega.BeNil())
+		time.Sleep(3 * time.Second)
+	})
+
+	// fail to set route since etcd is all killed
+	// while get route could still succeed
+	ginkgo.It("get stats after kill etcd", func() {
+		timeStart := time.Now()
+		utils.SetRoute(e, httpexpect.Status5xx)
+		utils.GetRoute(e, http.StatusOK)
+		utils.TestPrometheusEtcdMetric(e, 0)
+
+		bandwidthAfter, durationAfter = utils.GetEgressBandwidthPerSecond(e)
+		bpsAfter = bandwidthAfter / durationAfter
+
+		errorLog, err := utils.Log(apisixPod, cliSet.KubeCli, timeStart)
+		gomega.Expect(err).To(gomega.BeNil())
+		gomega.Ω(errorLog).Should(gomega.ContainSubstring("no healthy etcd endpoint available"))
+	})
+
+	ginkgo.It("ingress bandwidth per second not change much", func() {
+		fmt.Fprintf(ginkgo.GinkgoWriter, "bandwidth before: %f, after: %f", bandwidthBefore, bandwidthAfter)
+		fmt.Fprintf(ginkgo.GinkgoWriter, "duration before: %f, after: %f", durationBefore, durationAfter)
+		fmt.Fprintf(ginkgo.GinkgoWriter, "bps before: %f, after: %f", bpsBefore, bpsAfter)
+		gomega.Expect(utils.RoughCompare(bpsBefore, bpsAfter)).To(gomega.BeTrue())
+	})
+
+	ginkgo.It("wait till etcd return to normal", func() {
+		listOption := client.MatchingLabels{"app.kubernetes.io/instance": "etcd"}
+		var etcdPod []v1.Pod
+		successCount := 0
+		for i := 0; i < 6; i++ {
+			etcdPods, err := utils.GetPods(cliSet.CtrlCli, metav1.NamespaceDefault, listOption)
+			gomega.Expect(err).To(gomega.BeNil())
+			for _, p := range etcdPods {
+				if pod.IsPodReady(&p) {
+					successCount++
+				}
+			}
+			if successCount == len(etcdPod) {
+				break
+			}
+			time.Sleep(5 * time.Second)
+			successCount = 0
+		}
+		gomega.Ω(successCount).Should(gomega.BeNumerically("==", len(etcdPod)))
+	})
+})
diff --git a/t/chaos/kube_utils.go b/t/chaos/utils/kube_utils.go
similarity index 72%
rename from t/chaos/kube_utils.go
rename to t/chaos/utils/kube_utils.go
index 13e5b8a..f21036e 100644
--- a/t/chaos/kube_utils.go
+++ b/t/chaos/utils/kube_utils.go
@@ -15,19 +15,18 @@
  * limitations under the License.
  */
 
-package chaos
+package utils
 
 import (
 	"bytes"
 	"context"
-	"fmt"
 	"io"
-	"strings"
+	"time"
 
 	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
-	. "github.com/onsi/gomega"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/kubernetes"
 	clientScheme "k8s.io/client-go/kubernetes/scheme"
@@ -37,33 +36,39 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/config"
 )
 
-type clientSet struct {
-	ctrlCli client.Client
-	kubeCli *kubernetes.Clientset
+type ClientSet struct {
+	CtrlCli client.Client
+	KubeCli *kubernetes.Clientset
 }
 
-func initClientSet(g *WithT) *clientSet {
+func InitClientSet() (*ClientSet, error) {
 	scheme := runtime.NewScheme()
 	v1alpha1.AddToScheme(scheme)
 	clientScheme.AddToScheme(scheme)
 
 	restConfig := config.GetConfigOrDie()
 	ctrlCli, err := client.New(restConfig, client.Options{Scheme: scheme})
-	g.Expect(err).To(BeNil())
+	if err != nil {
+		return nil, err
+	}
 	kubeCli, err := kubernetes.NewForConfig(restConfig)
-	g.Expect(err).To(BeNil())
+	if err != nil {
+		return nil, err
+	}
 
-	return &clientSet{ctrlCli, kubeCli}
+	return &ClientSet{ctrlCli, kubeCli}, nil
 }
 
-func getPod(g *WithT, cli client.Client, ns string, listOption client.MatchingLabels) *corev1.Pod {
+func GetPods(cli client.Client, ns string, listOption client.MatchingLabels) ([]corev1.Pod, error) {
 	podList := &corev1.PodList{}
 	err := cli.List(context.Background(), podList, client.InNamespace(ns), listOption)
-	g.Expect(err).To(BeNil())
-	return &podList.Items[0]
+	if err != nil {
+		return nil, err
+	}
+	return podList.Items, nil
 }
 
-func execInPod(g *WithT, cli *kubernetes.Clientset, pod *corev1.Pod, cmd string) string {
+func ExecInPod(cli *kubernetes.Clientset, pod *corev1.Pod, cmd string) (string, error) {
 	name := pod.GetName()
 	namespace := pod.GetNamespace()
 	// only get the first container, no harm for now
@@ -87,7 +92,7 @@ func execInPod(g *WithT, cli *kubernetes.Clientset, pod *corev1.Pod, cmd string)
 	var stdout, stderr bytes.Buffer
 	exec, err := remotecommand.NewSPDYExecutor(config.GetConfigOrDie(), "POST", req.URL())
 	if err != nil {
-		panic(fmt.Sprintf("error: %s\nin creating NewSPDYExecutor for pod %s/%s", err.Error(), namespace, name))
+		return "", errors.Wrapf(err, "error in creating NewSPDYExecutor for pod %s in ns: %s", name, namespace)
 	}
 	err = exec.Stream(remotecommand.StreamOptions{
 		Stdin:  nil,
@@ -95,16 +100,21 @@ func execInPod(g *WithT, cli *kubernetes.Clientset, pod *corev1.Pod, cmd string)
 		Stderr: &stderr,
 	})
 	if stderr.String() != "" {
-		panic(fmt.Sprintf("error: %s\npod: %s\ncommand: %s", strings.TrimSuffix(stderr.String(), "\n"), pod.Name, cmd))
+		stderror := errors.New(stderr.String())
+		return "", errors.Wrapf(stderror, "pod: %s\ncommand: %s", name, cmd)
 	}
 	if err != nil {
-		panic(fmt.Sprintf("error: %s\nin streaming remotecommand: pod: %s/%s, command: %s", err.Error(), namespace, pod.Name, cmd))
+		return "", errors.Wrapf(err, "error in streaming remote command: pod: %s in ns: %s\n command: %s", name, namespace, cmd)
 	}
-	return stdout.String()
+	return stdout.String(), nil
 }
 
-func log(pod *corev1.Pod, c *kubernetes.Clientset) (string, error) {
+// Log print log of pod
+func Log(pod *corev1.Pod, c *kubernetes.Clientset, sinceTime time.Time) (string, error) {
 	podLogOpts := corev1.PodLogOptions{}
+	if !sinceTime.IsZero() {
+		podLogOpts.SinceTime = &metav1.Time{Time: sinceTime}
+	}
 
 	req := c.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts)
 	podLogs, err := req.Stream()
diff --git a/t/chaos/setup_chaos_utils.sh b/t/chaos/utils/setup_chaos_utils.sh
similarity index 71%
rename from t/chaos/setup_chaos_utils.sh
rename to t/chaos/utils/setup_chaos_utils.sh
index 373b99f..0c6d97f 100755
--- a/t/chaos/setup_chaos_utils.sh
+++ b/t/chaos/utils/setup_chaos_utils.sh
@@ -35,7 +35,7 @@ modify_config() {
   - ${DNS_IP}
 etcd:
   host:
-  - \"http://etcd-cluster-client.default.svc.cluster.local:2379\"
+  - \"http://etcd.default.svc.cluster.local:2379\"
 plugin_attr:
   prometheus:
     enable_export_server: false
@@ -43,25 +43,11 @@ plugin_attr:
     sed -i -e 's/apisix:latest/apisix:alpine-local/g' kubernetes/deployment.yaml
 }
 
-ensure_pods_ready() {
-    local app=$1
-    local status=$2
-    local retries=$3
-
-    count=0
-    while [[ $(kubectl get pods -l app=${app} -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != ${status} ]];
-    do
-        echo "Waiting for pod running" && sleep 10;
-
-        ((count=count+1))
-        if [ $count -gt ${retries} ]; then
-            printf "Waiting for pod status running timeout\n"
-            kubectl describe pod -l app=${app}
-            printf "\n\n"
-            kubectl logs -l app=${app}
-            exit 1
-        fi
-    done
+port_forward() {
+    apisix_pod_name=$(kubectl get pod -l app=apisix-gw -o 'jsonpath={.items[0].metadata.name}')
+    nohup kubectl port-forward svc/apisix-gw-lb 9080:9080 >/dev/null 2>&1 &
+    nohup kubectl port-forward $apisix_pod_name 9091:9091 >/dev/null 2>&1 &
+    ps aux | grep '[p]ort-forward'
 }
 
 "$@"
diff --git a/t/chaos/utils.go b/t/chaos/utils/utils.go
similarity index 71%
rename from t/chaos/utils.go
rename to t/chaos/utils/utils.go
index b226e79..9309a4c 100644
--- a/t/chaos/utils.go
+++ b/t/chaos/utils/utils.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package chaos
+package utils
 
 import (
 	"fmt"
@@ -24,23 +24,24 @@ import (
 	"strings"
 	"time"
 
-	"github.com/gavv/httpexpect/v2"
-	. "github.com/onsi/gomega"
+	"github.com/gavv/httpexpect"
+	"github.com/onsi/gomega"
 )
 
 var (
 	token = "edd1c9f034335f136f87ad84b625c8f1"
-	host  = "http://127.0.0.1:9080"
+	Host  = "http://127.0.0.1:9080"
 )
 
 type httpTestCase struct {
-	E            *httpexpect.Expect
-	Method       string
-	Path         string
-	Body         string
-	Headers      map[string]string
-	ExpectStatus int
-	ExpectBody   string
+	E                 *httpexpect.Expect
+	Method            string
+	Path              string
+	Body              string
+	Headers           map[string]string
+	ExpectStatus      int
+	ExpectBody        string
+	ExpectStatusRange httpexpect.StatusRange
 }
 
 func caseCheck(tc httpTestCase) *httpexpect.Response {
@@ -51,6 +52,8 @@ func caseCheck(tc httpTestCase) *httpexpect.Response {
 		req = e.GET(tc.Path)
 	case http.MethodPut:
 		req = e.PUT(tc.Path)
+	case http.MethodDelete:
+		req = e.DELETE(tc.Path)
 	default:
 		panic("invalid HTTP method")
 	}
@@ -70,6 +73,10 @@ func caseCheck(tc httpTestCase) *httpexpect.Response {
 		resp.Status(tc.ExpectStatus)
 	}
 
+	if tc.ExpectStatusRange != 0 {
+		resp.StatusRange(tc.ExpectStatusRange)
+	}
+
 	if tc.ExpectBody != "" {
 		resp.Body().Contains(tc.ExpectBody)
 	}
@@ -77,7 +84,7 @@ func caseCheck(tc httpTestCase) *httpexpect.Response {
 	return resp
 }
 
-func setRoute(e *httpexpect.Expect, expectStatus int) {
+func SetRoute(e *httpexpect.Expect, expectStatusRange httpexpect.StatusRange) {
 	caseCheck(httpTestCase{
 		E:       e,
 		Method:  http.MethodPut,
@@ -95,11 +102,11 @@ func setRoute(e *httpexpect.Expect, expectStatus int) {
 				 "type": "roundrobin"
 			 }
 		 }`,
-		ExpectStatus: expectStatus,
+		ExpectStatusRange: expectStatusRange,
 	})
 }
 
-func getRoute(e *httpexpect.Expect, expectStatus int) {
+func GetRoute(e *httpexpect.Expect, expectStatus int) {
 	caseCheck(httpTestCase{
 		E:            e,
 		Method:       http.MethodGet,
@@ -108,7 +115,7 @@ func getRoute(e *httpexpect.Expect, expectStatus int) {
 	})
 }
 
-func getRouteList(e *httpexpect.Expect, expectStatus int) {
+func GetRouteList(e *httpexpect.Expect, expectStatus int) {
 	caseCheck(httpTestCase{
 		E:            e,
 		Method:       http.MethodGet,
@@ -119,17 +126,16 @@ func getRouteList(e *httpexpect.Expect, expectStatus int) {
 	})
 }
 
-func deleteRoute(e *httpexpect.Expect, expectStatus int) {
+func DeleteRoute(e *httpexpect.Expect) {
 	caseCheck(httpTestCase{
-		E:            e,
-		Method:       http.MethodDelete,
-		Path:         "/apisix/admin/routes/1",
-		Headers:      map[string]string{"X-API-KEY": token},
-		ExpectStatus: expectStatus,
+		E:       e,
+		Method:  http.MethodDelete,
+		Path:    "/apisix/admin/routes/1",
+		Headers: map[string]string{"X-API-KEY": token},
 	})
 }
 
-func testPrometheusEtcdMetric(e *httpexpect.Expect, expectEtcd int) {
+func TestPrometheusEtcdMetric(e *httpexpect.Expect, expectEtcd int) {
 	caseCheck(httpTestCase{
 		E:          e,
 		Method:     http.MethodGet,
@@ -139,7 +145,7 @@ func testPrometheusEtcdMetric(e *httpexpect.Expect, expectEtcd int) {
 }
 
 // get the first line which contains the key
-func getPrometheusMetric(e *httpexpect.Expect, g *WithT, key string) string {
+func getPrometheusMetric(e *httpexpect.Expect, key string) string {
 	resp := caseCheck(httpTestCase{
 		E:      e,
 		Method: http.MethodGet,
@@ -154,29 +160,29 @@ func getPrometheusMetric(e *httpexpect.Expect, g *WithT, key string) string {
 		}
 	}
 	targetSlice := strings.Fields(targetLine)
-	g.Expect(len(targetSlice) == 2).To(BeTrue())
+	gomega.Ω(len(targetSlice)).Should(gomega.BeNumerically("==", 2))
 	return targetSlice[1]
 }
 
-func getEgressBandwidthPerSecond(e *httpexpect.Expect, g *WithT) (float64, float64) {
+func GetEgressBandwidthPerSecond(e *httpexpect.Expect) (float64, float64) {
 	key := "apisix_bandwidth{type=\"egress\","
-	bandWidthString := getPrometheusMetric(e, g, key)
+	bandWidthString := getPrometheusMetric(e, key)
 	bandWidthStart, err := strconv.ParseFloat(bandWidthString, 64)
-	g.Expect(err).To(BeNil())
+	gomega.Expect(err).To(gomega.BeNil())
 	// after etcd got killed, it would take longer time to get the metrics
 	// so need to calculate the duration
 	timeStart := time.Now()
 
 	time.Sleep(10 * time.Second)
-	bandWidthString = getPrometheusMetric(e, g, key)
+	bandWidthString = getPrometheusMetric(e, key)
 	bandWidthEnd, err := strconv.ParseFloat(bandWidthString, 64)
-	g.Expect(err).To(BeNil())
-	duration := time.Now().Sub(timeStart)
+	gomega.Expect(err).To(gomega.BeNil())
+	duration := time.Since(timeStart)
 
 	return bandWidthEnd - bandWidthStart, duration.Seconds()
 }
 
-func roughCompare(a float64, b float64) bool {
+func RoughCompare(a float64, b float64) bool {
 	ratio := a / b
 	if ratio < 1.3 && ratio > 0.7 {
 		return true
@@ -188,7 +194,7 @@ type silentPrinter struct {
 	logger httpexpect.Logger
 }
 
-func newSilentPrinter(logger httpexpect.Logger) silentPrinter {
+func NewSilentPrinter(logger httpexpect.Logger) silentPrinter {
 	return silentPrinter{logger}
 }