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

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


The following commit(s) were added to refs/heads/master by this push:
     new 806f2f8  fix: ApisixRoute update operations is not effective (#319)
806f2f8 is described below

commit 806f2f8c2a54a87e6540620e88791c4409424382
Author: Alex Zhang <[email protected]>
AuthorDate: Wed Mar 31 19:19:19 2021 +0800

    fix: ApisixRoute update operations is not effective (#319)
    
    * fix: ApisixRoute update operations is not effective
    
    * fix
    
    * test: add two more e2e test cases about apisixroute
---
 go.mod                                  |   1 +
 go.sum                                  |  44 +-----
 pkg/ingress/controller/apisix_route.go  |  60 ++++++---
 pkg/ingress/controller/endpoint.go      |  24 ++--
 pkg/ingress/controller/ingress.go       |  43 ++++--
 pkg/ingress/controller/manifest.go      | 155 +++++++++++++++++++++
 pkg/ingress/controller/manifest_test.go | 190 ++++++++++++++++++++++++++
 pkg/kube/ingress.go                     |   1 +
 pkg/kube/translation/apisix_route.go    |   1 +
 pkg/seven/state/builder.go              | 231 --------------------------------
 pkg/seven/state/builder_test.go         |  51 -------
 pkg/seven/state/event.go                |  34 +----
 pkg/seven/state/route_worker.go         |  68 ----------
 pkg/seven/state/solver.go               | 184 -------------------------
 test/e2e/ingress/resourcepushing.go     | 173 +++++++++++++++++++++++-
 15 files changed, 608 insertions(+), 652 deletions(-)

diff --git a/go.mod b/go.mod
index 99be1b7..59dffa4 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
        github.com/gin-gonic/gin v1.6.3
        github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
        github.com/hashicorp/go-memdb v1.0.4
+       github.com/hashicorp/go-multierror v1.0.0
        github.com/hashicorp/golang-lru v0.5.3 // indirect
        github.com/imdario/mergo v0.3.11 // indirect
        github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // 
indirect
diff --git a/go.sum b/go.sum
index d0cf595..6b48ff8 100644
--- a/go.sum
+++ b/go.sum
@@ -8,7 +8,6 @@ cloud.google.com/go v0.46.3/go.mod 
h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg
 cloud.google.com/go v0.50.0/go.mod 
h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
 cloud.google.com/go v0.52.0/go.mod 
h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
 cloud.google.com/go v0.53.0/go.mod 
h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=
 cloud.google.com/go v0.54.0/go.mod 
h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
 cloud.google.com/go/bigquery v1.0.1/go.mod 
h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod 
h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
@@ -48,7 +47,6 @@ github.com/armon/go-metrics 
v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod 
h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod 
h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod 
h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 github.com/beorn7/perks v1.0.0/go.mod 
h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod 
h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -68,7 +66,6 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod 
h1:uF7uidLiAD3TWHmW31ZFd/JWoc
 github.com/coreos/go-semver v0.3.0/go.mod 
h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod 
h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod 
h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man/v2 v2.0.0 
h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod 
h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 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=
@@ -85,7 +82,6 @@ github.com/evanphx/json-patch v4.9.0+incompatible 
h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo
 github.com/evanphx/json-patch v4.9.0+incompatible/go.mod 
h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fatih/color v1.7.0/go.mod 
h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod 
h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-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/fsnotify/fsnotify v1.4.9 
h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod 
h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -127,7 +123,6 @@ github.com/gogo/protobuf v1.3.1 
h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
 github.com/gogo/protobuf v1.3.1/go.mod 
h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 
h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod 
h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef 
h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod 
h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod 
h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod 
h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -140,9 +135,7 @@ github.com/golang/mock v1.4.0/go.mod 
h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
 github.com/golang/mock v1.4.1/go.mod 
h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 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 
h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod 
h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3 
h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
 github.com/golang/protobuf v1.3.3/go.mod 
h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.3.4/go.mod 
h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod 
h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -157,14 +150,12 @@ github.com/golang/protobuf v1.4.3/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
 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=
-github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 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 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
 github.com/google/go-cmp v0.5.2/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 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=
@@ -183,7 +174,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod 
h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 github.com/googleapis/gnostic v0.4.1 
h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
 github.com/googleapis/gnostic v0.4.1/go.mod 
h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod 
h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/websocket v1.4.2 
h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod 
h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod 
h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod 
h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -200,6 +190,7 @@ github.com/hashicorp/go-immutable-radix v1.1.0/go.mod 
h1:0y9vanUI8NX6FsYoO3zeMjh
 github.com/hashicorp/go-memdb v1.0.4 
h1:sIdJHAEtV3//iXcUb4LumSQeorYos5V0ptvqvQvFgDA=
 github.com/hashicorp/go-memdb v1.0.4/go.mod 
h1:LWQ8R70vPrS4OEY9k28D2z8/Zzyu34NVzeRibGAzHO0=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod 
h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0 
h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
 github.com/hashicorp/go-multierror v1.0.0/go.mod 
h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 github.com/hashicorp/go-rootcerts v1.0.0/go.mod 
h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod 
h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
@@ -209,7 +200,6 @@ github.com/hashicorp/go-uuid v1.0.1 
h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1
 github.com/hashicorp/go-uuid v1.0.1/go.mod 
h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 github.com/hashicorp/go.net v0.0.1/go.mod 
h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod 
h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1 
h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod 
h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.3 
h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
 github.com/hashicorp/golang-lru v0.5.3/go.mod 
h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
@@ -221,7 +211,6 @@ github.com/hashicorp/serf v0.8.2/go.mod 
h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod 
h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod 
h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
 github.com/imdario/mergo v0.3.5/go.mod 
h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.11 
h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
 github.com/imdario/mergo v0.3.11/go.mod 
h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
@@ -229,7 +218,6 @@ github.com/inconshreveable/mousetrap v1.0.0 
h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
 github.com/inconshreveable/mousetrap v1.0.0/go.mod 
h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jonboulle/clockwork v0.1.0/go.mod 
h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/json-iterator/go v1.1.6/go.mod 
h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-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/json-iterator/go v1.1.10 
h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
 github.com/json-iterator/go v1.1.10/go.mod 
h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -244,7 +232,6 @@ github.com/kisielk/errcheck v1.2.0/go.mod 
h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
 github.com/kisielk/gotool v1.0.0/go.mod 
h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod 
h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod 
h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod 
h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod 
h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -264,14 +251,12 @@ github.com/mattn/go-isatty v0.0.3/go.mod 
h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
 github.com/mattn/go-isatty v0.0.8/go.mod 
h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.12 
h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod 
h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 
h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod 
h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions 
v1.0.2-0.20181231171920-c182affec369 
h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
 github.com/matttproud/golang_protobuf_extensions 
v1.0.2-0.20181231171920-c182affec369/go.mod 
h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/miekg/dns v1.0.14/go.mod 
h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/mitchellh/cli v1.0.0/go.mod 
h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/go-homedir v1.0.0/go.mod 
h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0 
h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod 
h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-testing-interface v1.0.0/go.mod 
h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
 github.com/mitchellh/gox v0.4.0/go.mod 
h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
@@ -300,7 +285,6 @@ github.com/pascaldekloe/goe 
v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
 github.com/pelletier/go-toml v1.2.0/go.mod 
h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod 
h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pkg/errors v0.8.0/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -308,26 +292,21 @@ github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod 
h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/prometheus/client_golang v0.9.1/go.mod 
h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3 
h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
 github.com/prometheus/client_golang v0.9.3/go.mod 
h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v1.0.0/go.mod 
h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.7.1 
h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
 github.com/prometheus/client_golang v1.7.1/go.mod 
h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod 
h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 
h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod 
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 
h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod 
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.2.0 
h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod 
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod 
h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0 
h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
 github.com/prometheus/common v0.4.0/go.mod 
h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod 
h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.10.0 
h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
 github.com/prometheus/common v0.10.0/go.mod 
h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod 
h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 
h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod 
h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2/go.mod 
h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.1.3/go.mod 
h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -336,13 +315,11 @@ github.com/prometheus/procfs v0.2.0/go.mod 
h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
 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/go-internal v1.3.0/go.mod 
h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/russross/blackfriday/v2 v2.0.1 
h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod 
h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod 
h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/sergi/go-diff v1.1.0/go.mod 
h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 
h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod 
h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod 
h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod 
h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -366,7 +343,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 v1.2.2/go.mod 
h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 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=
 github.com/stretchr/testify v1.6.1 
h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -404,11 +380,9 @@ golang.org/x/crypto 
v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod 
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 
h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 
h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 
h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -430,7 +404,6 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod 
h1:UVdnD1Gm6xHRNCYTk
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod 
h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod 
h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod 
h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de 
h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
 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=
@@ -442,7 +415,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod 
h1:mXi4GBBbnImb6dmsKG
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod 
h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod 
h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -476,7 +448,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b 
h1:iFwSg7t5GZmB/Q5TjiEAsdoLD
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod 
h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 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 
h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d 
h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
@@ -511,7 +482,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 
h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
 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=
@@ -528,14 +498,11 @@ golang.org/x/term 
v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod 
h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 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/time v0.0.0-20181108054448-85acf8d2951c/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 
h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e 
h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
@@ -561,7 +528,6 @@ golang.org/x/tools 
v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc 
h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
 golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -577,11 +543,9 @@ golang.org/x/tools 
v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb 
h1:iKlO7ROJc6SttHKlxzwGytRtBUqX4VARrNTgP2YLX5M=
 golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod 
h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
 golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 
h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
 golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod 
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-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/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -600,7 +564,6 @@ google.golang.org/api v0.20.0/go.mod 
h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
 google.golang.org/appengine v1.1.0/go.mod 
h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod 
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1 
h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
 google.golang.org/appengine v1.6.1/go.mod 
h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/appengine v1.6.5 
h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
 google.golang.org/appengine v1.6.5/go.mod 
h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -622,7 +585,6 @@ 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-20200526211855-cb27e3aa2013 
h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod 
h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod 
h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -630,7 +592,6 @@ google.golang.org/grpc v1.21.1/go.mod 
h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
 google.golang.org/grpc v1.23.0/go.mod 
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 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 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
 google.golang.org/grpc v1.27.1/go.mod 
h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 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=
@@ -662,7 +623,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod 
h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.5/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=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -672,7 +632,6 @@ honnef.co/go/tools 
v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod 
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod 
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod 
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 
h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod 
h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3 
h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod 
h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
@@ -695,7 +654,6 @@ rsc.io/quote/v3 v3.1.0/go.mod 
h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2 
h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod 
h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 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=
diff --git a/pkg/ingress/controller/apisix_route.go 
b/pkg/ingress/controller/apisix_route.go
index d8fcb99..36aded8 100644
--- a/pkg/ingress/controller/apisix_route.go
+++ b/pkg/ingress/controller/apisix_route.go
@@ -25,7 +25,6 @@ import (
 
        "github.com/apache/apisix-ingress-controller/pkg/kube"
        "github.com/apache/apisix-ingress-controller/pkg/log"
-       "github.com/apache/apisix-ingress-controller/pkg/seven/state"
        "github.com/apache/apisix-ingress-controller/pkg/types"
        apisixv1 
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
@@ -154,33 +153,48 @@ func (c *apisixRouteController) sync(ctx context.Context, 
ev *types.Event) error
                zap.Any("apisix_route", ar),
        )
 
+       m := &manifest{
+               routes:    routes,
+               upstreams: upstreams,
+       }
+
+       var (
+               added   *manifest
+               updated *manifest
+               deleted *manifest
+       )
+
        if ev.Type == types.EventDelete {
-               rc := &state.RouteCompare{OldRoutes: routes, NewRoutes: nil}
-               if err := rc.Sync(); err != nil {
-                       return err
-               } else {
-                       comb := state.ApisixCombination{Routes: nil, Upstreams: 
upstreams}
-                       if err := comb.Remove(); err != nil {
-                               return err
-                       }
-               }
-               return nil
+               deleted = m
        } else if ev.Type == types.EventAdd {
-               comb := state.ApisixCombination{Routes: routes, Upstreams: 
upstreams}
-               if _, err := comb.Solver(); err != nil {
-                       return err
-               }
+               added = m
        } else {
-               var oldRoutes []*apisixv1.Route
+               var (
+                       oldRoutes    []*apisixv1.Route
+                       oldUpstreams []*apisixv1.Upstream
+               )
                if obj.GroupVersion == kube.ApisixRouteV1 {
-                       oldRoutes, _, _ = 
c.controller.translator.TranslateRouteV1(ar.V1())
+                       oldRoutes, oldUpstreams, err = 
c.controller.translator.TranslateRouteV1(obj.OldObject.V1())
                } else {
-                       oldRoutes, _, _ = 
c.controller.translator.TranslateRouteV2alpha1(ar.V2alpha1())
+                       oldRoutes, oldUpstreams, err = 
c.controller.translator.TranslateRouteV2alpha1(obj.OldObject.V2alpha1())
                }
-               rc := &state.RouteCompare{OldRoutes: oldRoutes, NewRoutes: 
routes}
-               return rc.Sync()
+               if err != nil {
+                       log.Errorw("failed to translate old ApisixRoute 
v2alpha1",
+                               zap.String("event", "update"),
+                               zap.Error(err),
+                               zap.Any("ApisixRoute", ar),
+                       )
+                       return err
+               }
+
+               om := &manifest{
+                       routes:    oldRoutes,
+                       upstreams: oldUpstreams,
+               }
+               added, updated, deleted = m.diff(om)
        }
-       return nil
+
+       return c.controller.syncManifests(ctx, added, updated, deleted)
 }
 
 func (c *apisixRouteController) handleSyncErr(obj interface{}, err error) {
@@ -231,6 +245,10 @@ func (c *apisixRouteController) onUpdate(oldObj, newObj 
interface{}) {
        if !c.controller.namespaceWatching(key) {
                return
        }
+       log.Debugw("ApisixRoute update event arrived",
+               zap.Any("new object", curr),
+               zap.Any("old object", prev),
+       )
        c.workqueue.AddRateLimited(&types.Event{
                Type: types.EventUpdate,
                Object: kube.ApisixRouteEvent{
diff --git a/pkg/ingress/controller/endpoint.go 
b/pkg/ingress/controller/endpoint.go
index 62bed20..ad22a36 100644
--- a/pkg/ingress/controller/endpoint.go
+++ b/pkg/ingress/controller/endpoint.go
@@ -27,7 +27,6 @@ import (
        "github.com/apache/apisix-ingress-controller/pkg/apisix"
        apisixcache 
"github.com/apache/apisix-ingress-controller/pkg/apisix/cache"
        "github.com/apache/apisix-ingress-controller/pkg/log"
-       "github.com/apache/apisix-ingress-controller/pkg/seven/state"
        "github.com/apache/apisix-ingress-controller/pkg/types"
        apisixv1 
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
@@ -156,18 +155,10 @@ func (c *endpointsController) syncToCluster(ctx 
context.Context, cluster apisix.
                zap.String("cluster", cluster.String()),
        )
 
-       upstreams := []*apisixv1.Upstream{upstream}
-       comb := state.ApisixCombination{Routes: nil, Services: nil, Upstreams: 
upstreams}
-
-       if _, err = comb.Solver(); err != nil {
-               log.Errorw("failed to sync upstream",
-                       zap.String("upstream", upsName),
-                       zap.String("cluster", cluster.String()),
-                       zap.Error(err),
-               )
-               return err
+       updated := &manifest{
+               upstreams: []*apisixv1.Upstream{upstream},
        }
-       return nil
+       return c.controller.syncManifests(ctx, nil, updated, nil)
 }
 
 func (c *endpointsController) handleSyncErr(obj interface{}, err error) {
@@ -190,6 +181,8 @@ func (c *endpointsController) onAdd(obj interface{}) {
        if !c.controller.namespaceWatching(key) {
                return
        }
+       log.Debugw("endpoints add event arrived",
+               zap.Any("object", obj))
 
        c.workqueue.AddRateLimited(&types.Event{
                Type:   types.EventAdd,
@@ -212,6 +205,10 @@ func (c *endpointsController) onUpdate(prev, curr 
interface{}) {
        if !c.controller.namespaceWatching(key) {
                return
        }
+       log.Debugw("endpoints update event arrived",
+               zap.Any("new object", currEp),
+               zap.Any("old object", prevEp),
+       )
        c.workqueue.AddRateLimited(&types.Event{
                Type:   types.EventUpdate,
                Object: curr,
@@ -235,6 +232,9 @@ func (c *endpointsController) onDelete(obj interface{}) {
        if !c.controller.namespaceWatching(ep.Namespace + "/" + ep.Name) {
                return
        }
+       log.Debugw("endpoints delete event arrived",
+               zap.Any("final state", ep),
+       )
        c.workqueue.AddRateLimited(&types.Event{
                Type:   types.EventDelete,
                Object: ep,
diff --git a/pkg/ingress/controller/ingress.go 
b/pkg/ingress/controller/ingress.go
index f1ac7a9..badc105 100644
--- a/pkg/ingress/controller/ingress.go
+++ b/pkg/ingress/controller/ingress.go
@@ -137,16 +137,42 @@ func (c *ingressController) sync(ctx context.Context, ev 
*types.Event) error {
                zap.Any("upstreams", upstreams),
        )
 
-       if err := c.syncToCluster(ctx, "", routes, upstreams, ev.Type); err != 
nil {
-               log.Errorw("failed to sync ingress artifacts (routes, 
upstreams)",
-                       zap.Error(err),
-                       zap.Any("routes", routes),
-                       zap.Any("upstreams", upstreams),
-                       zap.Any("ingress", ing),
+       m := &manifest{
+               routes:    routes,
+               upstreams: upstreams,
+       }
+
+       var (
+               added   *manifest
+               updated *manifest
+               deleted *manifest
+       )
+
+       if ev.Type == types.EventDelete {
+               deleted = m
+       } else if ev.Type == types.EventAdd {
+               added = m
+       } else {
+               var (
+                       oldRoutes    []*apisixv1.Route
+                       oldUpstreams []*apisixv1.Upstream
                )
-               return err
+               oldRoutes, oldUpstreams, err := 
c.controller.translator.TranslateIngress(ingEv.OldObject)
+               if err != nil {
+                       log.Errorw("failed to translate ingress",
+                               zap.String("event", "update"),
+                               zap.Error(err),
+                               zap.Any("ingress", ingEv.OldObject),
+                       )
+                       return err
+               }
+               om := &manifest{
+                       routes:    oldRoutes,
+                       upstreams: oldUpstreams,
+               }
+               added, updated, deleted = m.diff(om)
        }
-       return nil
+       return c.controller.syncManifests(ctx, added, updated, deleted)
 }
 
 func (c *ingressController) syncToCluster(ctx context.Context, clusterName 
string, routes []*apisixv1.Route, upstreams []*apisixv1.Upstream, ev 
types.EventType) error {
@@ -272,6 +298,7 @@ func (c *ingressController) onUpdate(oldObj, newObj 
interface{}) {
                Object: kube.IngressEvent{
                        Key:          key,
                        GroupVersion: curr.GroupVersion(),
+                       OldObject:    prev,
                },
        })
 }
diff --git a/pkg/ingress/controller/manifest.go 
b/pkg/ingress/controller/manifest.go
new file mode 100644
index 0000000..709f986
--- /dev/null
+++ b/pkg/ingress/controller/manifest.go
@@ -0,0 +1,155 @@
+// 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 controller
+
+import (
+       "context"
+       "reflect"
+
+       "github.com/hashicorp/go-multierror"
+
+       apisixv1 
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+func diffRoutes(olds, news []*apisixv1.Route) (added, updated, deleted 
[]*apisixv1.Route) {
+       if olds == nil {
+               return news, nil, nil
+       }
+       if news == nil {
+               return nil, nil, olds
+       }
+
+       oldMap := make(map[string]*apisixv1.Route, len(olds))
+       newMap := make(map[string]*apisixv1.Route, len(news))
+       for _, r := range olds {
+               oldMap[r.ID] = r
+       }
+       for _, r := range news {
+               newMap[r.ID] = r
+       }
+
+       for _, r := range news {
+               if or, ok := oldMap[r.ID]; !ok {
+                       added = append(added, r)
+               } else if !reflect.DeepEqual(or, r) {
+                       updated = append(updated, r)
+               }
+       }
+       for _, r := range olds {
+               if _, ok := newMap[r.ID]; !ok {
+                       deleted = append(deleted, r)
+               }
+       }
+       return
+}
+
+func diffUpstreams(olds, news []*apisixv1.Upstream) (added, updated, deleted 
[]*apisixv1.Upstream) {
+       oldMap := make(map[string]*apisixv1.Upstream, len(olds))
+       newMap := make(map[string]*apisixv1.Upstream, len(news))
+       for _, u := range olds {
+               oldMap[u.ID] = u
+       }
+       for _, u := range news {
+               newMap[u.ID] = u
+       }
+
+       for _, u := range news {
+               if ou, ok := oldMap[u.ID]; !ok {
+                       added = append(added, u)
+               } else if !reflect.DeepEqual(ou, u) {
+                       updated = append(updated, u)
+               }
+       }
+       for _, u := range olds {
+               if _, ok := newMap[u.ID]; !ok {
+                       deleted = append(deleted, u)
+               }
+       }
+       return
+}
+
+type manifest struct {
+       routes    []*apisixv1.Route
+       upstreams []*apisixv1.Upstream
+}
+
+func (m *manifest) diff(om *manifest) (added, updated, deleted *manifest) {
+       ar, ur, dr := diffRoutes(om.routes, m.routes)
+       au, uu, du := diffUpstreams(om.upstreams, m.upstreams)
+       if ar != nil || au != nil {
+               added = &manifest{
+                       routes:    ar,
+                       upstreams: au,
+               }
+       }
+       if ur != nil || uu != nil {
+               updated = &manifest{
+                       routes:    ur,
+                       upstreams: uu,
+               }
+       }
+       if dr != nil || du != nil {
+               deleted = &manifest{
+                       routes:    dr,
+                       upstreams: du,
+               }
+       }
+       return
+}
+
+func (c *Controller) syncManifests(ctx context.Context, added, updated, 
deleted *manifest) error {
+       var merr *multierror.Error
+       if deleted != nil {
+               for _, r := range deleted.routes {
+                       if err := c.apisix.Cluster("").Route().Delete(ctx, r); 
err != nil {
+                               merr = multierror.Append(merr, err)
+                       }
+               }
+               for _, u := range deleted.upstreams {
+                       if err := c.apisix.Cluster("").Upstream().Delete(ctx, 
u); err != nil {
+                               merr = multierror.Append(merr, err)
+                       }
+               }
+       }
+       if added != nil {
+               // Should create upstreams firstly due to the dependencies.
+               for _, u := range added.upstreams {
+                       if _, err := 
c.apisix.Cluster("").Upstream().Create(ctx, u); err != nil {
+                               merr = multierror.Append(merr, err)
+                       }
+               }
+               for _, r := range added.routes {
+                       if _, err := c.apisix.Cluster("").Route().Create(ctx, 
r); err != nil {
+                               merr = multierror.Append(merr, err)
+                       }
+               }
+       }
+       if updated != nil {
+               for _, r := range updated.upstreams {
+                       if _, err := 
c.apisix.Cluster("").Upstream().Update(ctx, r); err != nil {
+                               merr = multierror.Append(merr, err)
+                       }
+               }
+               for _, r := range updated.routes {
+                       if _, err := c.apisix.Cluster("").Route().Update(ctx, 
r); err != nil {
+                               merr = multierror.Append(merr, err)
+                       }
+               }
+       }
+       if merr != nil {
+               return merr
+       }
+       return nil
+}
diff --git a/pkg/ingress/controller/manifest_test.go 
b/pkg/ingress/controller/manifest_test.go
new file mode 100644
index 0000000..811aefc
--- /dev/null
+++ b/pkg/ingress/controller/manifest_test.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 controller
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+
+       apisixv1 
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+func TestDiffRoutes(t *testing.T) {
+       news := []*apisixv1.Route{
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "1",
+                       },
+               },
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "3",
+                       },
+                       Methods: []string{"POST"},
+               },
+       }
+       added, updated, deleted := diffRoutes(nil, news)
+       assert.Nil(t, updated)
+       assert.Nil(t, deleted)
+       assert.Len(t, added, 2)
+       assert.Equal(t, added[0].ID, "1")
+       assert.Equal(t, added[1].ID, "3")
+       assert.Equal(t, added[1].Methods, []string{"POST"})
+
+       olds := []*apisixv1.Route{
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "2",
+                       },
+               },
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "3",
+                       },
+                       Methods: []string{"POST", "PUT"},
+               },
+       }
+       added, updated, deleted = diffRoutes(olds, nil)
+       assert.Nil(t, updated)
+       assert.Nil(t, added)
+       assert.Len(t, deleted, 2)
+       assert.Equal(t, deleted[0].ID, "2")
+       assert.Equal(t, deleted[1].ID, "3")
+       assert.Equal(t, deleted[1].Methods, []string{"POST", "PUT"})
+
+       added, updated, deleted = diffRoutes(olds, news)
+       assert.Len(t, added, 1)
+       assert.Equal(t, added[0].ID, "1")
+       assert.Len(t, updated, 1)
+       assert.Equal(t, updated[0].ID, "3")
+       assert.Equal(t, updated[0].Methods, []string{"POST"})
+       assert.Len(t, deleted, 1)
+       assert.Equal(t, deleted[0].ID, "2")
+}
+
+func TestDiffUpstreams(t *testing.T) {
+       news := []*apisixv1.Upstream{
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "1",
+                       },
+               },
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "3",
+                       },
+                       Retries: 3,
+               },
+       }
+       added, updated, deleted := diffUpstreams(nil, news)
+       assert.Nil(t, updated)
+       assert.Nil(t, deleted)
+       assert.Len(t, added, 2)
+       assert.Equal(t, added[0].ID, "1")
+       assert.Equal(t, added[1].ID, "3")
+       assert.Equal(t, added[1].Retries, 3)
+
+       olds := []*apisixv1.Upstream{
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "2",
+                       },
+               },
+               {
+                       Metadata: apisixv1.Metadata{
+                               ID: "3",
+                       },
+                       Retries: 5,
+                       Timeout: &apisixv1.UpstreamTimeout{
+                               Connect: 10,
+                       },
+               },
+       }
+       added, updated, deleted = diffUpstreams(olds, nil)
+       assert.Nil(t, updated)
+       assert.Nil(t, added)
+       assert.Len(t, deleted, 2)
+       assert.Equal(t, deleted[0].ID, "2")
+       assert.Equal(t, deleted[1].ID, "3")
+       assert.Equal(t, deleted[1].Retries, 5)
+       assert.Equal(t, deleted[1].Timeout.Connect, 10)
+
+       added, updated, deleted = diffUpstreams(olds, news)
+       assert.Len(t, added, 1)
+       assert.Equal(t, added[0].ID, "1")
+       assert.Len(t, updated, 1)
+       assert.Equal(t, updated[0].ID, "3")
+       assert.Nil(t, updated[0].Timeout)
+       assert.Equal(t, updated[0].Retries, 3)
+       assert.Len(t, deleted, 1)
+       assert.Equal(t, deleted[0].ID, "2")
+}
+
+func TestManifestDiff(t *testing.T) {
+       m := &manifest{
+               routes: []*apisixv1.Route{
+                       {
+                               Metadata: apisixv1.Metadata{
+                                       ID: "1",
+                               },
+                       },
+                       {
+                               Metadata: apisixv1.Metadata{
+                                       ID: "3",
+                               },
+                               Methods: []string{"GET"},
+                       },
+               },
+               upstreams: []*apisixv1.Upstream{
+                       {
+                               Metadata: apisixv1.Metadata{
+                                       ID: "4",
+                               },
+                               Retries: 2,
+                       },
+               },
+       }
+       om := &manifest{
+               routes: []*apisixv1.Route{
+                       {
+                               Metadata: apisixv1.Metadata{
+                                       ID: "2",
+                               },
+                       },
+                       {
+                               Metadata: apisixv1.Metadata{
+                                       ID: "3",
+                               },
+                               Methods: []string{"GET", "HEAD"},
+                       },
+               },
+       }
+
+       added, updated, deleted := m.diff(om)
+       assert.Len(t, added.routes, 1)
+       assert.Equal(t, added.routes[0].ID, "1")
+       assert.Len(t, added.upstreams, 1)
+       assert.Equal(t, added.upstreams[0].ID, "4")
+
+       assert.Len(t, updated.routes, 1)
+       assert.Equal(t, updated.routes[0].ID, "3")
+       assert.Equal(t, updated.routes[0].Methods, []string{"GET"})
+       assert.Nil(t, updated.upstreams)
+
+       assert.Len(t, deleted.routes, 1)
+       assert.Equal(t, deleted.routes[0].ID, "2")
+       assert.Nil(t, updated.upstreams)
+}
diff --git a/pkg/kube/ingress.go b/pkg/kube/ingress.go
index cd355da..ac39470 100644
--- a/pkg/kube/ingress.go
+++ b/pkg/kube/ingress.go
@@ -76,6 +76,7 @@ type Ingress interface {
 type IngressEvent struct {
        Key          string
        GroupVersion string
+       OldObject    Ingress
 }
 
 type ingress struct {
diff --git a/pkg/kube/translation/apisix_route.go 
b/pkg/kube/translation/apisix_route.go
index c3a8b8f..770a040 100644
--- a/pkg/kube/translation/apisix_route.go
+++ b/pkg/kube/translation/apisix_route.go
@@ -62,6 +62,7 @@ func (t *translator) TranslateRouteV1(ar 
*configv1.ApisixRoute) ([]*apisixv1.Rou
                        upsId := id.GenID(upstreamName)
                        route := &apisixv1.Route{
                                Metadata: apisixv1.Metadata{
+                                       ID:              id.GenID(routeName),
                                        FullName:        routeName,
                                        ResourceVersion: ar.ResourceVersion,
                                        Name:            routeName,
diff --git a/pkg/seven/state/builder.go b/pkg/seven/state/builder.go
deleted file mode 100644
index 9265cd9..0000000
--- a/pkg/seven/state/builder.go
+++ /dev/null
@@ -1,231 +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 state
-
-import (
-       "context"
-       "errors"
-       "sync"
-
-       "github.com/apache/apisix-ingress-controller/pkg/apisix/cache"
-       "github.com/apache/apisix-ingress-controller/pkg/id"
-       "github.com/apache/apisix-ingress-controller/pkg/log"
-       "github.com/apache/apisix-ingress-controller/pkg/seven/conf"
-       "github.com/apache/apisix-ingress-controller/pkg/seven/utils"
-       v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
-)
-
-const (
-       ApisixUpstream = "ApisixUpstream"
-       WatchFromKind  = "watch"
-)
-
-// paddingRoute fills route through currentRoute, it returns a boolean
-// value to indicate whether the route is a new created one.
-func paddingRoute(route *v1.Route, currentRoute *v1.Route) bool {
-       if currentRoute == nil {
-               route.ID = id.GenID(route.FullName)
-               return true
-       }
-       route.ID = currentRoute.ID
-       return false
-}
-
-// paddingService fills service through currentService, it returns a boolean
-// value to indicate whether the service is a new created one.
-func paddingService(service *v1.Service, currentService *v1.Service) bool {
-       if currentService == nil {
-               service.ID = id.GenID(service.FullName)
-               return true
-       }
-       service.ID = currentService.ID
-       return false
-}
-
-// paddingUpstream fills upstream through currentUpstream, it returns a boolean
-// value to indicate whether the upstream is a new created one.
-func paddingUpstream(upstream *v1.Upstream, currentUpstream *v1.Upstream) bool 
{
-       if currentUpstream == nil {
-               upstream.ID = id.GenID(upstream.FullName)
-               return true
-       }
-       upstream.ID = currentUpstream.ID
-       return false
-}
-
-// NewRouteWorkers make routeWorkers group by service per CRD
-// 1.make routes group by (1_2_3) it may be a map like map[1_2_3][]Route;
-// 2.route is listening Event from the ready of 1_2_3;
-func NewRouteWorkers(ctx context.Context,
-       routes []*v1.Route, wg *sync.WaitGroup, errorChan chan CRDStatus) 
RouteWorkerGroup {
-
-       rwg := make(RouteWorkerGroup)
-       for _, r := range routes {
-               rw := &routeWorker{Route: r, Ctx: ctx, Wg: wg, ErrorChan: 
errorChan}
-               rw.start()
-               rwg.Add(r.UpstreamName, rw)
-       }
-       return rwg
-}
-
-// 3.route get the Event and trigger a padding for object,then diff,sync;
-func (r *routeWorker) trigger() {
-       var (
-               op        string
-               errNotify error
-       )
-       defer func() {
-               if errNotify != nil {
-                       r.ErrorChan <- CRDStatus{Id: "", Status: "failure", 
Err: errNotify}
-               }
-               r.Wg.Done()
-       }()
-
-       // padding
-       var cluster string
-       if r.Route.Group != "" {
-               cluster = r.Route.Group
-       }
-       currentRoute, err := 
conf.Client.Cluster(cluster).Route().Get(context.TODO(), r.Route.FullName)
-       if err != nil && !errors.Is(err, cache.ErrNotFound) {
-               errNotify = err
-               return
-       }
-
-       if paddingRoute(r.Route, currentRoute) {
-               op = Create
-       } else {
-               op = Update
-       }
-
-       hasDiff, err := utils.HasDiff(r.Route, currentRoute)
-       if err != nil {
-               errNotify = err
-               return
-       }
-       if hasDiff {
-               err := r.sync(op)
-               if err != nil {
-                       errNotify = err
-                       return
-               }
-       }
-}
-
-// sync
-func (r *routeWorker) sync(op string) error {
-       var cluster string
-       if r.Group != "" {
-               cluster = r.Group
-       }
-       if op == Update {
-               if _, err := 
conf.Client.Cluster(cluster).Route().Update(context.TODO(), r.Route); err != 
nil {
-                       log.Errorf("failed to update route %s: %s, ", r.Name, 
err)
-                       return err
-               }
-               log.Infof("update route %s, %s", r.Name, r.ServiceId)
-       } else {
-               route, err := 
conf.Client.Cluster(cluster).Route().Create(context.TODO(), r.Route)
-               if err != nil {
-                       log.Errorf("failed to create route: %s", err.Error())
-                       return err
-               }
-               r.ID = route.ID
-       }
-       log.Infof("create route %s, %s", r.Name, r.ServiceId)
-       return nil
-}
-
-// upstream
-func SolverUpstream(upstreams []*v1.Upstream, rwg RouteWorkerGroup, wg 
*sync.WaitGroup, errorChan chan CRDStatus) {
-       for _, u := range upstreams {
-               go SolverSingleUpstream(u, rwg, wg, errorChan)
-       }
-}
-
-func SolverSingleUpstream(u *v1.Upstream, rwg RouteWorkerGroup, wg 
*sync.WaitGroup, errorChan chan CRDStatus) {
-       var (
-               op        string
-               errNotify error
-       )
-       defer func() {
-               if errNotify != nil {
-                       errorChan <- CRDStatus{Id: "", Status: "failure", Err: 
errNotify}
-               }
-               wg.Done()
-       }()
-       var cluster string
-       if u.Group != "" {
-               cluster = u.Group
-       }
-       if currentUpstream, err := 
conf.Client.Cluster(cluster).Upstream().Get(context.TODO(), u.FullName); err != 
nil && err != cache.ErrNotFound {
-               log.Errorf("failed to find upstream %s: %s", u.FullName, err)
-               errNotify = err
-               return
-       } else {
-               if paddingUpstream(u, currentUpstream) {
-                       op = Create
-               } else {
-                       op = Update
-               }
-
-               if op == Create {
-                       if u.FromKind == WatchFromKind {
-                               // We don't have a pre-defined upstream and the 
current upstream updating from
-                               // endpoints.
-                               return
-                       }
-                       if _, err := 
conf.Client.Cluster(cluster).Upstream().Create(context.TODO(), u); err != nil {
-                               log.Errorf("failed to create upstream %s: %s", 
u.FullName, err)
-                               return
-                       }
-               } else {
-                       // diff
-                       hasDiff, err := utils.HasDiff(u, currentUpstream)
-                       if err != nil {
-                               errNotify = err
-                               return
-                       }
-                       if hasDiff {
-                               op = Update
-                               // 0.field check
-                               needToUpdate := true
-                               if currentUpstream.FromKind == ApisixUpstream { 
// update from ApisixUpstream
-                                       if u.FromKind != ApisixUpstream {
-                                               // currentUpstream > u
-                                               // set lb && health check
-                                               needToUpdate = false
-                                       }
-                               }
-                               if needToUpdate || u.FromKind == WatchFromKind {
-                                       if u.FromKind == WatchFromKind {
-                                               currentUpstream.Nodes = u.Nodes
-                                       } else { // due to CRD update
-                                               currentUpstream = u
-                                       }
-                                       if _, err = 
conf.Client.Cluster(cluster).Upstream().Update(context.TODO(), 
currentUpstream); err != nil {
-                                               log.Errorf("failed to update 
upstream %s: %s", u.FullName, err)
-                                               return
-                                       }
-                               }
-                       }
-               }
-       }
-       log.Infof("solver upstream %s:%s", op, u.Name)
-       // anyway, broadcast to route
-       for _, sw := range rwg[u.Name] {
-               sw.Event <- Event{}
-       }
-}
diff --git a/pkg/seven/state/builder_test.go b/pkg/seven/state/builder_test.go
deleted file mode 100644
index a895fcf..0000000
--- a/pkg/seven/state/builder_test.go
+++ /dev/null
@@ -1,51 +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 state
-
-import (
-       "testing"
-
-       "github.com/apache/apisix-ingress-controller/pkg/seven/utils"
-)
-
-type school struct {
-       *province
-       Name    string `json:"name"`
-       Address string `json:"address"`
-}
-
-type province struct {
-       Location string `json:"location"`
-}
-
-func Test_diff(t *testing.T) {
-       //p1 := &province{Location: "jiangsu"}
-       p2 := &province{Location: "zh"}
-       s1 := &school{Name: "hello", Address: "this is a address"}
-       s2 := &school{Name: "hello", Address: "this is a address", province: p2}
-       t.Log(s1)
-       t.Log(s2)
-       if d, err := utils.Diff(s1, s2); err != nil {
-               t.Log(err.Error())
-       } else {
-               //t.Logf("s1 vs s2 hasDiff ? %v", d)
-               t.Log(d)
-               for _, delta := range d.Deltas() {
-                       t.Log(delta.Similarity())
-               }
-
-       }
-
-}
diff --git a/pkg/seven/state/event.go b/pkg/seven/state/event.go
index 3a750f9..8c354e0 100644
--- a/pkg/seven/state/event.go
+++ b/pkg/seven/state/event.go
@@ -14,36 +14,8 @@
 // limitations under the License.
 package state
 
-import (
-       v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
-)
-
-type ApisixCombination struct {
-       Routes    []*v1.Route
-       Services  []*v1.Service
-       Upstreams []*v1.Upstream
-}
-
-type RouteCompare struct {
-       OldRoutes []*v1.Route
-       NewRoutes []*v1.Route
-}
-
-type Quit struct {
-       Err error
-}
-
 const (
-       RouteKind    = "route"
-       ServiceKind  = "service"
-       UpstreamKind = "upstream"
-       Create       = "create"
-       Update       = "update"
-       Delete       = "delete"
+       Create = "create"
+       Update = "update"
+       Delete = "delete"
 )
-
-type Event struct {
-       Kind string      // route/service/upstream
-       Op   string      // create update delete
-       Obj  interface{} // the obj of kind
-}
diff --git a/pkg/seven/state/route_worker.go b/pkg/seven/state/route_worker.go
deleted file mode 100644
index ed28234..0000000
--- a/pkg/seven/state/route_worker.go
+++ /dev/null
@@ -1,68 +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 state
-
-import (
-       "context"
-       "sync"
-
-       v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
-)
-
-type routeWorker struct {
-       *v1.Route
-       Event     chan Event
-       Ctx       context.Context
-       Wg        *sync.WaitGroup
-       ErrorChan chan CRDStatus
-}
-
-// RouteWorkerGroup for broadcast from service to route
-type RouteWorkerGroup map[string][]*routeWorker
-
-// start start watch event
-func (w *routeWorker) start() {
-       w.Event = make(chan Event)
-       go func() {
-               for {
-                       select {
-                       case <-w.Event:
-                               w.trigger()
-                       case <-w.Ctx.Done():
-                               return
-                       }
-               }
-       }()
-}
-
-func (rg *RouteWorkerGroup) Add(key string, rw *routeWorker) {
-       routes := (*rg)[key]
-       if routes == nil {
-               routes = make([]*routeWorker, 0)
-       }
-       routes = append(routes, rw)
-       (*rg)[key] = routes
-}
-
-func (rg *RouteWorkerGroup) Delete(key string, route *routeWorker) {
-       routes := (*rg)[key]
-       result := make([]*routeWorker, 0)
-       for _, r := range routes {
-               if r.Name != route.Name {
-                       result = append(result, r)
-               }
-       }
-       (*rg)[key] = result
-}
diff --git a/pkg/seven/state/solver.go b/pkg/seven/state/solver.go
index 7d49746..d17cdeb 100644
--- a/pkg/seven/state/solver.go
+++ b/pkg/seven/state/solver.go
@@ -16,201 +16,17 @@ package state
 
 import (
        "context"
-       "errors"
-       "sync"
-       "time"
 
-       "go.uber.org/multierr"
-
-       "github.com/apache/apisix-ingress-controller/pkg/apisix/cache"
-       "github.com/apache/apisix-ingress-controller/pkg/log"
        "github.com/apache/apisix-ingress-controller/pkg/seven/conf"
        v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
 
-var UpstreamQueue chan UpstreamQueueObj
-
-func init() {
-       UpstreamQueue = make(chan UpstreamQueueObj, 500)
-       go WatchUpstream()
-}
-
-func WatchUpstream() {
-       for {
-               uqo := <-UpstreamQueue
-               SolverUpstream(uqo.Upstreams, uqo.RouteWorkerGroup, uqo.Wg, 
uqo.ErrorChan)
-       }
-}
-
-// Solver
-func (s *ApisixCombination) Solver() (string, error) {
-       // define the result notify
-       timeout := 10 * time.Second
-       resultChan := make(chan CRDStatus)
-       ctx := context.Background()
-       ctx, _ = context.WithTimeout(ctx, timeout)
-       go s.SyncWithGroup(ctx, "", resultChan)
-
-       return WaitWorkerGroup("", resultChan)
-}
-
-func (s *ApisixCombination) Remove() error {
-       // services
-       for _, svc := range s.Services {
-               var cluster string
-               if svc.Group != "" {
-                       cluster = svc.Group
-               }
-               svcInCache, err := 
conf.Client.Cluster(cluster).Service().Get(context.TODO(), svc.FullName)
-               if err != nil {
-                       if err == cache.ErrNotFound {
-                               log.Errorf("failed to remove service %s: %s", 
svc.FullName, err)
-                               continue
-                       } else {
-                               return err
-                       }
-               }
-               _ = paddingService(svc, svcInCache)
-               err = 
conf.Client.Cluster(cluster).Service().Delete(context.TODO(), svc)
-               if err != nil {
-                       if err == cache.ErrNotFound {
-                               log.Errorf("failed to remove service %s: %s", 
svc.FullName, err)
-                       } else if err == cache.ErrStillInUse {
-                               log.Warnf("failed to remove service %s: %s", 
svc.FullName, err)
-                       } else {
-                               return err
-                       }
-               }
-       }
-
-       // upstreams
-       for _, ups := range s.Upstreams {
-               var cluster string
-               if ups.Group != "" {
-                       cluster = ups.Group
-               }
-               upsInCache, err := 
conf.Client.Cluster(cluster).Upstream().Get(context.TODO(), ups.FullName)
-               if err != nil {
-                       if err == cache.ErrNotFound {
-                               log.Errorf("failed to remove service %s: %s", 
ups.FullName, err)
-                               continue
-                       } else {
-                               return err
-                       }
-               }
-               _ = paddingUpstream(ups, upsInCache)
-               err = 
conf.Client.Cluster(cluster).Upstream().Delete(context.TODO(), ups)
-               if err == cache.ErrNotFound {
-                       log.Errorf("failed to remove upstream %s: %s", 
ups.FullName, err)
-               } else if err == cache.ErrStillInUse {
-                       log.Warnf("failed to remove upstream %s: %s", 
ups.FullName, err)
-               } else {
-                       return err
-               }
-       }
-       return nil
-}
-
-func waitTimeout(ctx context.Context, wg *sync.WaitGroup, resultChan chan 
CRDStatus) {
-       c := make(chan struct{})
-       go func() {
-               defer close(c)
-               wg.Wait()
-       }()
-       select {
-       case <-c:
-               resultChan <- CRDStatus{Id: "", Status: "success", Err: nil}
-       case <-ctx.Done():
-               resultChan <- CRDStatus{Id: "", Status: "failure", Err: 
errors.New("timeout")}
-       }
-}
-
-func (s *ApisixCombination) SyncWithGroup(ctx context.Context, id string, 
resultChan chan CRDStatus) {
-       var wg sync.WaitGroup
-       count := len(s.Routes) + len(s.Services) + len(s.Upstreams)
-       wg.Add(count)
-       // goroutine for sync route/service/upstream
-       // route
-       rwg := NewRouteWorkers(ctx, s.Routes, &wg, resultChan)
-       // upstream
-       uqo := &UpstreamQueueObj{Upstreams: s.Upstreams, RouteWorkerGroup: rwg, 
Wg: &wg, ErrorChan: resultChan}
-       uqo.AddQueue()
-
-       waitTimeout(ctx, &wg, resultChan)
-}
-
-func WaitWorkerGroup(id string, resultChan chan CRDStatus) (string, error) {
-       r := <-resultChan
-       return id, r.Err
-}
-
-// UpstreamQueueObj for upstream queue
-type UpstreamQueueObj struct {
-       Upstreams        []*v1.Upstream
-       RouteWorkerGroup RouteWorkerGroup
-       Wg               *sync.WaitGroup
-       ErrorChan        chan CRDStatus
-}
-
 type CRDStatus struct {
        Id     string `json:"id"`
        Status string `json:"status"`
        Err    error  `json:"err"`
 }
 
-type ResourceStatus struct {
-       Kind string `json:"kind"`
-       Id   string `json:"id"`
-       Err  error  `json:"err"`
-}
-
-// AddQueue make upstreams in order
-// upstreams is group by CRD
-func (uqo *UpstreamQueueObj) AddQueue() {
-       UpstreamQueue <- *uqo
-}
-
-type ServiceQueueObj struct {
-       Services         []*v1.Service
-       RouteWorkerGroup RouteWorkerGroup
-}
-
-// Sync remove from apisix
-func (rc *RouteCompare) Sync() error {
-       var merr error
-       for _, old := range rc.OldRoutes {
-               needToDel := true
-               for _, nr := range rc.NewRoutes {
-                       if old.Name == nr.Name {
-                               needToDel = false
-                               break
-                       }
-               }
-               if needToDel {
-                       var cluster string
-                       if old.Group != "" {
-                               cluster = old.Group
-                       }
-
-                       // old should inject the ID.
-                       route, err := 
conf.Client.Cluster(cluster).Route().Get(context.TODO(), old.FullName)
-                       if err != nil {
-                               if err != cache.ErrNotFound {
-                                       merr = multierr.Append(merr, err)
-                               }
-                               continue
-                       }
-
-                       _ = paddingRoute(old, route)
-                       if err := 
conf.Client.Cluster(cluster).Route().Delete(context.TODO(), old); err != nil {
-                               log.Errorf("failed to delete route %s from 
APISIX: %s", old.Name, err)
-                               merr = multierr.Append(merr, err)
-                       }
-               }
-       }
-       return merr
-}
-
 func SyncSsl(ssl *v1.Ssl, method string) error {
        var cluster string
        if ssl.Group != "" {
diff --git a/test/e2e/ingress/resourcepushing.go 
b/test/e2e/ingress/resourcepushing.go
index 7e590e7..0e2db4e 100644
--- a/test/e2e/ingress/resourcepushing.go
+++ b/test/e2e/ingress/resourcepushing.go
@@ -26,7 +26,6 @@ import (
 )
 
 var _ = ginkgo.Describe("ApisixRoute Testing", func() {
-
        opts := &scaffold.Options{
                Name:                    "default",
                Kubeconfig:              scaffold.GetKubeconfig(),
@@ -73,7 +72,7 @@ spec:
                s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").Expect().Status(http.StatusOK).Body().Raw()
        })
 
-       ginkgo.It("create and then remove", func() {
+       ginkgo.It("create, update, then remove", func() {
                backendSvc, backendSvcPort := s.DefaultHTTPBackend()
                apisixRoute := fmt.Sprintf(`
 apiVersion: apisix.apache.org/v2alpha1
@@ -99,14 +98,182 @@ spec:
                err = s.EnsureNumApisixUpstreamsCreated(1)
                assert.Nil(ginkgo.GinkgoT(), err, "Checking number of 
upstreams")
 
+               s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").Expect().Status(http.StatusOK)
+
+               // update
+               apisixRoute = fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpbin-route
+spec:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.com
+      paths:
+      - /ip
+      exprs:
+      - subject:
+          scope: Header
+          name: X-Foo
+        op: Equal
+        value: "barbaz"
+    backend:
+      serviceName: %s
+      servicePort: %d
+`, backendSvc, backendSvcPort[0])
+
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(apisixRoute))
+               // TODO When ingress controller can feedback the lifecycle of 
CRDs to the
+               // status field, we can poll it rather than sleeping.
+               time.Sleep(10 * time.Second)
+
+               err = s.EnsureNumApisixRoutesCreated(1)
+               assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
+               err = s.EnsureNumApisixUpstreamsCreated(1)
+               assert.Nil(ginkgo.GinkgoT(), err, "Checking number of 
upstreams")
+
+               s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").Expect().Status(http.StatusNotFound)
+               s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").WithHeader("X-Foo", "barbaz").Expect().Status(http.StatusOK)
+
                // remove
                assert.Nil(ginkgo.GinkgoT(), 
s.RemoveResourceByString(apisixRoute))
-
                // TODO When ingress controller can feedback the lifecycle of 
CRDs to the
                // status field, we can poll it rather than sleeping.
                time.Sleep(10 * time.Second)
                ups, err := s.ListApisixUpstreams()
                assert.Nil(ginkgo.GinkgoT(), err, "list upstreams error")
                assert.Len(ginkgo.GinkgoT(), ups, 0, "upstreams nodes not 
expect")
+
+               body := s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").Expect().Status(http.StatusNotFound).Body().Raw()
+               assert.Contains(ginkgo.GinkgoT(), body, "404 Route Not Found")
+       })
+
+       ginkgo.It("change route rule name", func() {
+               backendSvc, backendSvcPort := s.DefaultHTTPBackend()
+               apisixRoute := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpbin-route
+spec:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.com
+      paths:
+      - /ip
+    backend:
+      serviceName: %s
+      servicePort: %d
+`, backendSvc, backendSvcPort[0])
+
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(apisixRoute), "creating ApisixRoute")
+               assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1))
+               assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixUpstreamsCreated(1))
+
+               routes, err := s.ListApisixRoutes()
+               assert.Nil(ginkgo.GinkgoT(), err, "listing routes in APISIX")
+               assert.Len(ginkgo.GinkgoT(), routes, 1)
+
+               upstreams, err := s.ListApisixUpstreams()
+               assert.Nil(ginkgo.GinkgoT(), err, "listing upstreams in APISIX")
+               assert.Len(ginkgo.GinkgoT(), upstreams, 1)
+
+               s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").Expect().Status(http.StatusOK)
+
+               apisixRoute = fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpbin-route
+spec:
+  http:
+  - name: rule1_1
+    match:
+      hosts:
+      - httpbin.com
+      paths:
+      - /headers
+    backend:
+      serviceName: %s
+      servicePort: %d
+`, backendSvc, backendSvcPort[0])
+
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(apisixRoute), "creating ApisixRoute")
+               assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1))
+               assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixUpstreamsCreated(1))
+
+               newRoutes, err := s.ListApisixRoutes()
+               assert.Nil(ginkgo.GinkgoT(), err, "listing routes in APISIX")
+               assert.Len(ginkgo.GinkgoT(), newRoutes, 1)
+               newUpstreams, err := s.ListApisixUpstreams()
+               assert.Nil(ginkgo.GinkgoT(), err, "listing upstreams in APISIX")
+               assert.Len(ginkgo.GinkgoT(), newUpstreams, 1)
+
+               // Upstream doesn't change.
+               assert.Equal(ginkgo.GinkgoT(), newUpstreams[0].ID, 
upstreams[0].ID)
+               assert.Equal(ginkgo.GinkgoT(), newUpstreams[0].FullName, 
upstreams[0].FullName)
+
+               s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").Expect().
+                       Status(http.StatusNotFound).
+                       Body().Contains("404 Route Not Found")
+
+               s.NewAPISIXClient().GET("/headers").WithHeader("Host", 
"httpbin.com").Expect().
+                       Status(http.StatusOK)
+       })
+
+       ginkgo.It("same route rule name between two ApisixRoute objects", 
func() {
+               backendSvc, backendSvcPort := s.DefaultHTTPBackend()
+               apisixRoute := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpbin-route
+spec:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.com
+      paths:
+      - /ip
+    backend:
+      serviceName: %s
+      servicePort: %d
+---
+apiVersion: apisix.apache.org/v2alpha1
+kind: ApisixRoute
+metadata:
+  name: httpbin-route-2
+spec:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.com
+      paths:
+      - /headers
+    backend:
+      serviceName: %s
+      servicePort: %d
+`, backendSvc, backendSvcPort[0], backendSvc, backendSvcPort[0])
+
+               assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(apisixRoute), "creating ApisixRoute")
+               assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(2))
+               assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixUpstreamsCreated(1))
+
+               s.NewAPISIXClient().GET("/ip").WithHeader("Host", 
"httpbin.com").Expect().
+                       Status(http.StatusOK).
+                       Body().
+                       Contains("origin")
+               s.NewAPISIXClient().GET("/headers").WithHeader("Host", 
"httpbin.com").Expect().
+                       Status(http.StatusOK).
+                       Body().
+                       Contains("headers").
+                       Contains("httpbin.com")
        })
 })

Reply via email to