feat: ApisixRoute v2alpha1 (#262)

diff --git a/cmd/ingress/ingress.go b/cmd/ingress/ingress.go
index d43f1cd..381024f 100644
--- a/cmd/ingress/ingress.go
+++ b/cmd/ingress/ingress.go
@@ -139,6 +139,7 @@
 	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.IngressClass, "ingress-class", config.IngressClass, "the class of an Ingress object is set using the field IngressClassName in Kubernetes clusters version v1.18.0 or higher or the annotation \"kubernetes.io/ingress.class\" (deprecated)")
 	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.ElectionID, "election-id", config.IngressAPISIXLeader, "election id used for campaign the controller leader")
 	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.IngressVersion, "ingress-version", config.IngressNetworkingV1, "the supported ingress api group version, can be \"networking/v1beta1\" or \"networking/v1\" (for Kubernetes version v1.19.0 or higher)")
+	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.ApisixRouteVersion, "apisix-route-version", config.ApisixRouteV2alpha1, "the supported apisixroute api group version, can be \"apisix.apache.org/v1\" or \"apisix.apache.org/v2alpha1\"")
 	cmd.PersistentFlags().StringVar(&cfg.APISIX.BaseURL, "apisix-base-url", "", "the base URL for APISIX admin api / manager api")
 	cmd.PersistentFlags().StringVar(&cfg.APISIX.AdminKey, "apisix-admin-key", "", "admin key used for the authorization of APISIX admin api / manager api")
 
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index b5d81da..73d1866 100644
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -51,6 +51,9 @@
                                        # or "networking/v1" (for Kubernetes version v1.19.0 or higher), default
                                        # is "networking/v1".
 
+  apisix_route_version: "apisix.apache.org/v2alpha1" # the supported apisixroute api group version, can be
+                                                     # "apisix.apache.org/v1" or "apisix.apache.org/v2alpha1",
+                                                     # default is "apisix.apache.org/v2alpha1".
 # APISIX related configurations.
 apisix:
   base_url: "http://127.0.0.1:9080/apisix/admin" # the APISIX admin api / manager api
diff --git a/go.mod b/go.mod
index bf3fd6a..aa872d2 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,10 @@
 go 1.13
 
 require (
+	github.com/gavv/httpexpect/v2 v2.2.0 // indirect
 	github.com/gin-gonic/gin v1.6.3
 	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
-	github.com/gruntwork-io/terratest v0.32.7 // indirect
+	github.com/gruntwork-io/terratest v0.32.8 // indirect
 	github.com/hashicorp/go-memdb v1.0.4
 	github.com/hashicorp/golang-lru v0.5.3 // indirect
 	github.com/imdario/mergo v0.3.11 // indirect
diff --git a/go.sum b/go.sum
index d538905..b8e07c6 100644
--- a/go.sum
+++ b/go.sum
@@ -71,10 +71,16 @@
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
+github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
+github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
+github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -156,13 +162,20 @@
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
 github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fasthttp/websocket v1.4.2/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y=
 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/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=
+github.com/gavv/httpexpect v1.1.3 h1:fPDU3PBu5fVcSORltSEcpvAoxmCtDB94re8UVL2tCro=
+github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
+github.com/gavv/httpexpect/v2 v2.2.0 h1:0VwaEBmQaNFHX9x591A8Up+8shCwdF/nF0qlRd/nI48=
+github.com/gavv/httpexpect/v2 v2.2.0/go.mod h1:lnd0TqJLrP+wkJk3SFwtrpSlOAZQ7HaaIFuOYbgqgUM=
 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/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -204,6 +217,7 @@
 github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -224,6 +238,7 @@
 github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 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=
@@ -251,6 +266,8 @@
 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/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3/go.mod h1:2wIuQute9+hhWqvL3vEI7YB0EKluF4WcPzI1eAliazk=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -277,7 +294,9 @@
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/mux v1.7.3/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/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=
 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=
@@ -287,8 +306,8 @@
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/gruntwork-io/gruntwork-cli v0.7.0 h1:YgSAmfCj9c61H+zuvHwKfYUwlMhu5arnQQLM4RH+CYs=
 github.com/gruntwork-io/gruntwork-cli v0.7.0/go.mod h1:jp6Z7NcLF2avpY8v71fBx6hds9eOFPELSuD/VPv7w00=
-github.com/gruntwork-io/terratest v0.32.7 h1:TJF4ZyOviWknlmzgre48JGGoXa20S8Ng0O/sfBfH+Bw=
-github.com/gruntwork-io/terratest v0.32.7/go.mod h1:0iy7d56CziX3R4ZUn570HMElr4WgBKoKNa8Hzex7bvo=
+github.com/gruntwork-io/terratest v0.32.8 h1:ccIRFH+e6zhSB5difg7baJec4FeOZNXpeIFlZZlKW2M=
+github.com/gruntwork-io/terratest v0.32.8/go.mod h1:FckR+7ks472IJfSKUPfPvnJfSxV1LKGWGMJ9m/LHegE=
 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
@@ -316,6 +335,7 @@
 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=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
@@ -328,6 +348,8 @@
 github.com/imdario/mergo v0.3.7/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=
+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/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
@@ -352,6 +374,10 @@
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
+github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+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=
@@ -363,6 +389,7 @@
 github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
 github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -372,6 +399,7 @@
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+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=
@@ -395,6 +423,7 @@
 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/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -480,8 +509,10 @@
 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/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY=
 github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 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=
@@ -503,6 +534,7 @@
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -530,10 +562,24 @@
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
 github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+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.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
+github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo=
+github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
 github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
+github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
 github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
 github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
@@ -541,6 +587,7 @@
 github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
 github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
@@ -565,6 +612,7 @@
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 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=
@@ -614,6 +662,7 @@
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -675,6 +724,7 @@
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -921,6 +971,8 @@
 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=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 46d413c..bb575d8 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -42,6 +42,10 @@
 	IngressNetworkingV1 = "networking/v1"
 	// IngressNetworkingV1beta1 represents ingress.networking/v1beta1
 	IngressNetworkingV1beta1 = "networking/v1beta1"
+	// ApisixRouteV1 represents apisixroute.apisix.apache.org/v1
+	ApisixRouteV1 = "apisix.apache.org/v1"
+	// ApisixRouteV2alpha1 represents apisixroute.apisix.apache.org/v2alpha1
+	ApisixRouteV2alpha1 = "apisix.apache.org/v2alpha1"
 
 	_minimalResyncInterval = 30 * time.Second
 )
@@ -59,12 +63,13 @@
 
 // KubernetesConfig contains all Kubernetes related config items.
 type KubernetesConfig struct {
-	Kubeconfig     string             `json:"kubeconfig" yaml:"kubeconfig"`
-	ResyncInterval types.TimeDuration `json:"resync_interval" yaml:"resync_interval"`
-	AppNamespaces  []string           `json:"app_namespaces" yaml:"app_namespaces"`
-	ElectionID     string             `json:"election_id" yaml:"election_id"`
-	IngressClass   string             `json:"ingress_class" yaml:"ingress_class"`
-	IngressVersion string             `json:"ingress_version" yaml:"ingress_version"`
+	Kubeconfig         string             `json:"kubeconfig" yaml:"kubeconfig"`
+	ResyncInterval     types.TimeDuration `json:"resync_interval" yaml:"resync_interval"`
+	AppNamespaces      []string           `json:"app_namespaces" yaml:"app_namespaces"`
+	ElectionID         string             `json:"election_id" yaml:"election_id"`
+	IngressClass       string             `json:"ingress_class" yaml:"ingress_class"`
+	IngressVersion     string             `json:"ingress_version" yaml:"ingress_version"`
+	ApisixRouteVersion string             `json:"apisix_route_version" yaml:"apisix_route_version"`
 }
 
 // APISIXConfig contains all APISIX related config items.
@@ -83,12 +88,13 @@
 		HTTPListen:      ":8080",
 		EnableProfiling: true,
 		Kubernetes: KubernetesConfig{
-			Kubeconfig:     "", // Use in-cluster configurations.
-			ResyncInterval: types.TimeDuration{Duration: 6 * time.Hour},
-			AppNamespaces:  []string{v1.NamespaceAll},
-			ElectionID:     IngressAPISIXLeader,
-			IngressClass:   IngressClass,
-			IngressVersion: IngressNetworkingV1,
+			Kubeconfig:         "", // Use in-cluster configurations.
+			ResyncInterval:     types.TimeDuration{Duration: 6 * time.Hour},
+			AppNamespaces:      []string{v1.NamespaceAll},
+			ElectionID:         IngressAPISIXLeader,
+			IngressClass:       IngressClass,
+			IngressVersion:     IngressNetworkingV1,
+			ApisixRouteVersion: ApisixRouteV2alpha1,
 		},
 	}
 }
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 41fd6f4..ae7e868 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -33,12 +33,13 @@
 		HTTPListen:      ":9090",
 		EnableProfiling: true,
 		Kubernetes: KubernetesConfig{
-			ResyncInterval: types.TimeDuration{time.Hour},
-			Kubeconfig:     "/path/to/foo/baz",
-			AppNamespaces:  []string{""},
-			ElectionID:     "my-election-id",
-			IngressClass:   IngressClass,
-			IngressVersion: IngressNetworkingV1,
+			ResyncInterval:     types.TimeDuration{time.Hour},
+			Kubeconfig:         "/path/to/foo/baz",
+			AppNamespaces:      []string{""},
+			ElectionID:         "my-election-id",
+			IngressClass:       IngressClass,
+			IngressVersion:     IngressNetworkingV1,
+			ApisixRouteVersion: ApisixRouteV2alpha1,
 		},
 		APISIX: APISIXConfig{
 			BaseURL:  "http://127.0.0.1:8080/apisix",
diff --git a/pkg/ingress/controller/apisix_route.go b/pkg/ingress/controller/apisix_route.go
index f16ddbb..707cdd9 100644
--- a/pkg/ingress/controller/apisix_route.go
+++ b/pkg/ingress/controller/apisix_route.go
@@ -15,262 +15,147 @@
 package controller
 
 import (
-	"fmt"
-	"time"
+	"context"
+
+	"github.com/apache/apisix-ingress-controller/pkg/kube"
+
+	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 
 	"go.uber.org/zap"
-
-	"k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/util/runtime"
-	"k8s.io/apimachinery/pkg/util/wait"
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/kubernetes/scheme"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/util/workqueue"
 
-	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
-	clientset "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned"
-	apisixscheme "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/scheme"
-	apisixinformers "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions/config/v1"
-	listersv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v1"
 	"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"
 )
 
-type ApisixRouteController struct {
-	controller           *Controller
-	kubeclientset        kubernetes.Interface
-	apisixRouteClientset clientset.Interface
-	apisixRouteList      listersv1.ApisixRouteLister
-	apisixRouteSynced    cache.InformerSynced
-	workqueue            workqueue.RateLimitingInterface
+type apisixRouteController struct {
+	controller *Controller
+	workqueue  workqueue.RateLimitingInterface
+	workers    int
 }
 
-type RouteQueueObj struct {
-	Key    string                `json:"key"`
-	OldObj *configv1.ApisixRoute `json:"old_obj"`
-	Ope    string                `json:"ope"` // add / update / delete
-}
-
-func BuildApisixRouteController(
-	kubeclientset kubernetes.Interface,
-	api6RouteClientset clientset.Interface,
-	api6RouteInformer apisixinformers.ApisixRouteInformer,
-	root *Controller) *ApisixRouteController {
-
-	runtime.Must(apisixscheme.AddToScheme(scheme.Scheme))
-	controller := &ApisixRouteController{
-		controller:           root,
-		kubeclientset:        kubeclientset,
-		apisixRouteClientset: api6RouteClientset,
-		apisixRouteList:      api6RouteInformer.Lister(),
-		apisixRouteSynced:    api6RouteInformer.Informer().HasSynced,
-		workqueue:            workqueue.NewNamedRateLimitingQueue(workqueue.NewItemFastSlowRateLimiter(1*time.Second, 60*time.Second, 5), "ApisixRoutes"),
+func (c *Controller) newApisixRouteController() *apisixRouteController {
+	ctl := &apisixRouteController{
+		controller: c,
+		workqueue:  workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ApisixRoute"),
+		workers:    1,
 	}
-	api6RouteInformer.Informer().AddEventHandler(
+	c.apisixRouteInformer.AddEventHandler(
 		cache.ResourceEventHandlerFuncs{
-			AddFunc:    controller.addFunc,
-			UpdateFunc: controller.updateFunc,
-			DeleteFunc: controller.deleteFunc,
-		})
-	return controller
+			AddFunc:    ctl.onAdd,
+			UpdateFunc: ctl.onUpdate,
+			DeleteFunc: ctl.onDelete,
+		},
+	)
+	return ctl
 }
 
-func (c *ApisixRouteController) addFunc(obj interface{}) {
-	var key string
-	var err error
-	if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
-		runtime.HandleError(err)
-		return
-	}
-	if !c.controller.namespaceWatching(key) {
-		return
-	}
-	rqo := &RouteQueueObj{Key: key, OldObj: nil, Ope: ADD}
-	c.workqueue.AddRateLimited(rqo)
-}
-
-func (c *ApisixRouteController) updateFunc(oldObj, newObj interface{}) {
-	oldRoute := oldObj.(*configv1.ApisixRoute)
-	newRoute := newObj.(*configv1.ApisixRoute)
-	if oldRoute.ResourceVersion >= newRoute.ResourceVersion {
-		return
-	}
-	//c.addFunc(newObj)
-	var key string
-	var err error
-	if key, err = cache.MetaNamespaceKeyFunc(newObj); err != nil {
-		runtime.HandleError(err)
-		return
-	}
-	if !c.controller.namespaceWatching(key) {
-		return
-	}
-	rqo := &RouteQueueObj{Key: key, OldObj: oldRoute, Ope: UPDATE}
-	c.workqueue.AddRateLimited(rqo)
-}
-
-func (c *ApisixRouteController) deleteFunc(obj interface{}) {
-	oldRoute, ok := obj.(*configv1.ApisixRoute)
+func (c *apisixRouteController) run(ctx context.Context) {
+	log.Info("ApisixRoute controller started")
+	defer log.Info("ApisixRoute controller exited")
+	ok := cache.WaitForCacheSync(ctx.Done(), c.controller.apisixRouteInformer.HasSynced)
 	if !ok {
-		oldState, ok := obj.(cache.DeletedFinalStateUnknown)
-		if !ok {
-			return
-		}
-		oldRoute, ok = oldState.Obj.(*configv1.ApisixRoute)
-		if !ok {
-			return
-		}
-	}
-	var key string
-	var err error
-	key, err = cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
-	if err != nil {
-		runtime.HandleError(err)
+		log.Error("cache sync failed")
 		return
 	}
-	if !c.controller.namespaceWatching(key) {
-		return
+
+	for i := 0; i < c.workers; i++ {
+		go c.runWorker(ctx)
 	}
-	rqo := &RouteQueueObj{Key: key, OldObj: oldRoute, Ope: DELETE}
-	c.workqueue.AddRateLimited(rqo)
+	<-ctx.Done()
+	c.workqueue.ShutDown()
 }
 
-func (c *ApisixRouteController) Run(stop <-chan struct{}) error {
-	if ok := cache.WaitForCacheSync(stop); !ok {
-		log.Errorf("同步缓存失败")
-		return fmt.Errorf("failed to wait for caches to sync")
-	}
-	go wait.Until(c.runWorker, time.Second, stop)
-	return nil
-}
-
-func (c *ApisixRouteController) runWorker() {
-	for c.processNextWorkItem() {
-	}
-}
-
-func (c *ApisixRouteController) processNextWorkItem() bool {
-	defer recoverException()
-	obj, shutdown := c.workqueue.Get()
-	if shutdown {
-		return false
-	}
-	err := func(obj interface{}) error {
-		defer c.workqueue.Done(obj)
-		var ok bool
-		var rqo *RouteQueueObj
-		if rqo, ok = obj.(*RouteQueueObj); !ok {
-			c.workqueue.Forget(obj)
-			return fmt.Errorf("expected RouteQueueObj in workqueue but got %#v", obj)
+func (c *apisixRouteController) runWorker(ctx context.Context) {
+	for {
+		obj, quit := c.workqueue.Get()
+		if quit {
+			return
 		}
-		if err := c.syncHandler(rqo); err != nil {
-			c.workqueue.AddRateLimited(obj)
-			log.Errorf("sync route %s failed", rqo.Key)
-			return fmt.Errorf("error syncing '%s': %s", rqo.Key, err.Error())
-		}
+		err := c.sync(ctx, obj.(*types.Event))
+		c.workqueue.Done(obj)
+		c.handleSyncErr(obj, err)
+	}
+}
 
-		c.workqueue.Forget(obj)
-		return nil
-	}(obj)
+func (c *apisixRouteController) sync(ctx context.Context, ev *types.Event) error {
+	obj := ev.Object.(kube.ApisixRouteEvent)
+	namespace, name, err := cache.SplitMetaNamespaceKey(obj.Key)
 	if err != nil {
-		runtime.HandleError(err)
+		log.Errorf("invalid resource key: %s", obj.Key)
+		return err
 	}
-	return true
-}
+	var (
+		ar        kube.ApisixRoute
+		routes    []*apisixv1.Route
+		upstreams []*apisixv1.Upstream
+	)
+	if obj.GroupVersion == kube.ApisixRouteV1 {
+		ar, err = c.controller.apisixRouteLister.V1(namespace, name)
+	} else {
+		ar, err = c.controller.apisixRouteLister.V2alpha1(namespace, name)
+	}
 
-func (c *ApisixRouteController) syncHandler(rqo *RouteQueueObj) error {
-	key := rqo.Key
-	switch {
-	case rqo.Ope == ADD:
-		return c.add(key)
-	case rqo.Ope == UPDATE:
-		// 1.first add new route config
-		if err := c.add(key); err != nil {
-			// log error
+	if err != nil {
+		if !k8serrors.IsNotFound(err) {
+			log.Errorw("failed to get ApisixRoute",
+				zap.String("version", obj.GroupVersion),
+				zap.String("key", obj.Key),
+				zap.Error(err),
+			)
 			return err
-		} else {
-			// 2.then delete routes not exist
-			return c.sync(rqo)
 		}
-	case rqo.Ope == DELETE:
-		return c.sync(rqo)
-	default:
-		// log error
-		return fmt.Errorf("RouteQueueObj is not expected")
-	}
-}
 
-func (c *ApisixRouteController) add(key string) error {
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
-	if err != nil {
-		log.Errorf("invalid resource key: %s", key)
-		return fmt.Errorf("invalid resource key: %s", key)
-	}
-
-	apisixIngressRoute, err := c.apisixRouteList.ApisixRoutes(namespace).Get(name)
-	if err != nil {
-		if errors.IsNotFound(err) {
-			log.Infof("apisixRoute %s is removed", key)
+		if ev.Type != types.EventDelete {
+			log.Warnw("ApisixRoute was deleted before it can be delivered",
+				zap.String("key", obj.Key),
+				zap.String("version", obj.GroupVersion),
+			)
 			return nil
 		}
-		log.Errorf("failed to list ApisixRoute %s: %s", key, err.Error())
-		runtime.HandleError(fmt.Errorf("failed to list ApisixRoute %s: %s", key, err.Error()))
-		return err
 	}
-	routes, upstreams, err := c.controller.translator.TranslateRouteV1(apisixIngressRoute)
-	if err != nil {
-		log.Errorw("failed to translate ApisixRoute/v1",
-			zap.Any("route", apisixIngressRoute),
-			zap.Error(err),
-		)
-		return err
+	if ev.Type == types.EventDelete {
+		if ar != nil {
+			// We still find the resource while we are processing the DELETE event,
+			// that means object with same namespace and name was created, discarding
+			// this stale DELETE event.
+			log.Warnw("discard the stale ApisixRoute delete event since the resource still exists",
+				zap.String("key", obj.Key),
+			)
+			return nil
+		}
+		ar = ev.Tombstone.(kube.ApisixRoute)
 	}
-	comb := state.ApisixCombination{Routes: routes, Upstreams: upstreams}
-	_, err = comb.Solver()
-	if err != nil {
-		log.Errorw("failed to push routes and upstreams to APISIX",
-			zap.Any("routes", routes),
-			zap.Any("upstreams", upstreams),
-			zap.Error(err),
-		)
-	}
-	return err
-
-}
-
-// sync
-// 1.diff routes between old and new objects
-// 2.delete routes not exist
-func (c *ApisixRouteController) sync(rqo *RouteQueueObj) error {
-	key := rqo.Key
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
-	if err != nil {
-		log.Errorf("invalid resource key: %s", key)
-		return fmt.Errorf("invalid resource key: %s", key)
-	}
-	switch {
-	case rqo.Ope == UPDATE:
-		apisixIngressRoute, err := c.apisixRouteList.ApisixRoutes(namespace).Get(name)
+	if obj.GroupVersion == kube.ApisixRouteV1 {
+		routes, upstreams, err = c.controller.translator.TranslateRouteV1(ar.V1())
 		if err != nil {
-			if errors.IsNotFound(err) {
-				log.Errorf("apisixRoute %s is removed", key)
-				return nil
-			}
-			return err // if error occurred, return
+			log.Errorw("failed to translate ApisixRoute v1",
+				zap.Error(err),
+				zap.Any("object", ar),
+			)
+			return err
 		}
-		oldRoutes, _, _ := c.controller.translator.TranslateRouteV1(rqo.OldObj)
-		newRoutes, _, _ := c.controller.translator.TranslateRouteV1(apisixIngressRoute)
+	} else {
+		routes, upstreams, err = c.controller.translator.TranslateRouteV2alpha1(ar.V2alpha1())
+		if err != nil {
+			log.Errorw("failed to translate ApisixRoute v2alpha1",
+				zap.Error(err),
+				zap.Any("object", ar),
+			)
+			return err
+		}
+	}
 
-		rc := &state.RouteCompare{OldRoutes: oldRoutes, NewRoutes: newRoutes}
-		return rc.Sync()
-	case rqo.Ope == DELETE:
-		apisixIngressRoute, _ := c.apisixRouteList.ApisixRoutes(namespace).Get(name)
-		if apisixIngressRoute != nil && apisixIngressRoute.ResourceVersion > rqo.OldObj.ResourceVersion {
-			log.Warnf("Route %s has been covered when retry", rqo.Key)
-			return nil
-		}
-		routes, upstreams, _ := c.controller.translator.TranslateRouteV1(rqo.OldObj)
+	log.Debugw("translated ApisixRoute",
+		zap.Any("routes", routes),
+		zap.Any("upstreams", upstreams),
+		zap.Any("apisix_route", ar),
+	)
+
+	if ev.Type == types.EventDelete {
 		rc := &state.RouteCompare{OldRoutes: routes, NewRoutes: nil}
 		if err := rc.Sync(); err != nil {
 			return err
@@ -281,7 +166,112 @@
 			}
 		}
 		return nil
-	default:
-		return fmt.Errorf("not expected in (ApisixRouteController) sync")
+	} else if ev.Type == types.EventAdd {
+		comb := state.ApisixCombination{Routes: routes, Upstreams: upstreams}
+		if _, err := comb.Solver(); err != nil {
+			return err
+		}
+	} else {
+		var oldRoutes []*apisixv1.Route
+		if obj.GroupVersion == kube.ApisixRouteV1 {
+			oldRoutes, _, _ = c.controller.translator.TranslateRouteV1(ar.V1())
+		} else {
+			oldRoutes, _, _ = c.controller.translator.TranslateRouteV2alpha1(ar.V2alpha1())
+		}
+		rc := &state.RouteCompare{OldRoutes: oldRoutes, NewRoutes: routes}
+		return rc.Sync()
 	}
+	return nil
+}
+
+func (c *apisixRouteController) handleSyncErr(obj interface{}, err error) {
+	if err == nil {
+		c.workqueue.Forget(obj)
+		return
+	}
+	if c.workqueue.NumRequeues(obj) < _maxRetries {
+		log.Infow("sync ApisixRoute failed, will retry",
+			zap.Any("object", obj),
+		)
+		c.workqueue.AddRateLimited(obj)
+	} else {
+		c.workqueue.Forget(obj)
+		log.Warnf("drop ApisixRoute %+v out of the queue", obj)
+	}
+}
+
+func (c *apisixRouteController) onAdd(obj interface{}) {
+	key, err := cache.MetaNamespaceKeyFunc(obj)
+	if err != nil {
+		log.Errorf("found ApisixRoute resource with bad meta namespace key: %s", err)
+		return
+	}
+	if !c.controller.namespaceWatching(key) {
+		return
+	}
+	log.Debugw("ApisixRoute add event arrived",
+		zap.Any("object", obj))
+
+	ar := kube.MustNewApisixRoute(obj)
+	c.workqueue.AddRateLimited(&types.Event{
+		Type: types.EventAdd,
+		Object: kube.ApisixRouteEvent{
+			Key:          key,
+			GroupVersion: ar.GroupVersion(),
+		},
+	})
+}
+
+func (c *apisixRouteController) onUpdate(oldObj, newObj interface{}) {
+	prev := kube.MustNewApisixRoute(oldObj)
+	curr := kube.MustNewApisixRoute(newObj)
+	if prev.ResourceVersion() >= curr.ResourceVersion() {
+		return
+	}
+	key, err := cache.MetaNamespaceKeyFunc(newObj)
+	if err != nil {
+		log.Errorf("found ApisixRoute resource with bad meta namespace key: %s", err)
+		return
+	}
+	if !c.controller.namespaceWatching(key) {
+		return
+	}
+	c.workqueue.AddRateLimited(&types.Event{
+		Type: types.EventUpdate,
+		Object: kube.ApisixRouteEvent{
+			Key:          key,
+			GroupVersion: curr.GroupVersion(),
+			OldObject:    prev,
+		},
+	})
+}
+
+func (c *apisixRouteController) onDelete(obj interface{}) {
+	ar, err := kube.NewApisixRoute(obj)
+	if err != nil {
+		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
+		if !ok {
+			return
+		}
+		ar = kube.MustNewApisixRoute(tombstone)
+	}
+	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
+	if err != nil {
+		log.Errorf("found ApisixRoute resource with bad meta namesapce key: %s", err)
+		return
+	}
+	if !c.controller.namespaceWatching(key) {
+		return
+	}
+	log.Debugw("ApisixRoute delete event arrived",
+		zap.Any("final state", ar),
+	)
+	c.workqueue.AddRateLimited(&types.Event{
+		Type: types.EventDelete,
+		Object: kube.ApisixRouteEvent{
+			Key:          key,
+			GroupVersion: ar.GroupVersion(),
+		},
+		Tombstone: ar,
+	})
 }
diff --git a/pkg/ingress/controller/apisix_upstream.go b/pkg/ingress/controller/apisix_upstream.go
index ba116ad..c03ed9c 100644
--- a/pkg/ingress/controller/apisix_upstream.go
+++ b/pkg/ingress/controller/apisix_upstream.go
@@ -46,7 +46,7 @@
 		cache.ResourceEventHandlerFuncs{
 			AddFunc:    ctl.onAdd,
 			UpdateFunc: ctl.onUpdate,
-			DeleteFunc: ctl.OnDelete,
+			DeleteFunc: ctl.onDelete,
 		},
 	)
 	return ctl
@@ -56,7 +56,7 @@
 	log.Info("ApisixUpstream controller started")
 	defer log.Info("ApisixUpstream controller exited")
 	if ok := cache.WaitForCacheSync(ctx.Done(), c.controller.apisixUpstreamInformer.HasSynced, c.controller.svcInformer.HasSynced); !ok {
-		log.Errorf("cache sync failed")
+		log.Error("cache sync failed")
 		return
 	}
 	for i := 0; i < c.workers; i++ {
@@ -179,13 +179,13 @@
 		return
 	}
 	if c.workqueue.NumRequeues(obj) < _maxRetries {
-		log.Infow("sync endpoints failed, will retry",
+		log.Infow("sync ApisixUpstream failed, will retry",
 			zap.Any("object", obj),
 		)
 		c.workqueue.AddRateLimited(obj)
 	} else {
 		c.workqueue.Forget(obj)
-		log.Warnf("drop endpoints %+v out of the queue", obj)
+		log.Warnf("drop ApisixUpstream %+v out of the queue", obj)
 	}
 }
 
@@ -232,7 +232,7 @@
 	})
 }
 
-func (c *apisixUpstreamController) OnDelete(obj interface{}) {
+func (c *apisixUpstreamController) onDelete(obj interface{}) {
 	au, ok := obj.(*configv1.ApisixUpstream)
 	if !ok {
 		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
diff --git a/pkg/ingress/controller/controller.go b/pkg/ingress/controller/controller.go
index 2fb66ef..f12c528 100644
--- a/pkg/ingress/controller/controller.go
+++ b/pkg/ingress/controller/controller.go
@@ -77,11 +77,14 @@
 	ingressInformer        cache.SharedIndexInformer
 	apisixUpstreamInformer cache.SharedIndexInformer
 	apisixUpstreamLister   listersv1.ApisixUpstreamLister
+	apisixRouteLister      kube.ApisixRouteLister
+	apisixRouteInformer    cache.SharedIndexInformer
 
 	// resource controllers
 	endpointsController      *endpointsController
 	ingressController        *ingressController
 	apisixUpstreamController *apisixUpstreamController
+	apisixRouteController    *apisixRouteController
 }
 
 // NewController creates an ingress apisix controller object.
@@ -110,8 +113,9 @@
 	sharedInformerFactory := externalversions.NewSharedInformerFactory(crdClientset, cfg.Kubernetes.ResyncInterval.Duration)
 
 	var (
-		watchingNamespace map[string]struct{}
-		ingressInformer   cache.SharedIndexInformer
+		watchingNamespace   map[string]struct{}
+		ingressInformer     cache.SharedIndexInformer
+		apisixRouteInformer cache.SharedIndexInformer
 	)
 	if len(cfg.Kubernetes.AppNamespaces) > 1 || cfg.Kubernetes.AppNamespaces[0] != v1.NamespaceAll {
 		watchingNamespace = make(map[string]struct{}, len(cfg.Kubernetes.AppNamespaces))
@@ -123,12 +127,19 @@
 
 	ingressLister := kube.NewIngressLister(kube.CoreSharedInformerFactory.Networking().V1().Ingresses().Lister(),
 		kube.CoreSharedInformerFactory.Networking().V1beta1().Ingresses().Lister())
+	apisixRouteLister := kube.NewApisixRouteLister(sharedInformerFactory.Apisix().V1().ApisixRoutes().Lister(),
+		sharedInformerFactory.Apisix().V2alpha1().ApisixRoutes().Lister())
 
 	if cfg.Kubernetes.IngressVersion == config.IngressNetworkingV1 {
 		ingressInformer = kube.CoreSharedInformerFactory.Networking().V1().Ingresses().Informer()
 	} else {
 		ingressInformer = kube.CoreSharedInformerFactory.Extensions().V1beta1().Ingresses().Informer()
 	}
+	if cfg.Kubernetes.ApisixRouteVersion == config.ApisixRouteV2alpha1 {
+		apisixRouteInformer = sharedInformerFactory.Apisix().V2alpha1().ApisixRoutes().Informer()
+	} else {
+		apisixRouteInformer = sharedInformerFactory.Apisix().V1().ApisixRoutes().Informer()
+	}
 
 	c := &Controller{
 		name:               podName,
@@ -148,6 +159,8 @@
 		svcLister:              kube.CoreSharedInformerFactory.Core().V1().Services().Lister(),
 		ingressLister:          ingressLister,
 		ingressInformer:        ingressInformer,
+		apisixRouteInformer:    apisixRouteInformer,
+		apisixRouteLister:      apisixRouteLister,
 		apisixUpstreamInformer: sharedInformerFactory.Apisix().V1().ApisixUpstreams().Informer(),
 		apisixUpstreamLister:   sharedInformerFactory.Apisix().V1().ApisixUpstreams().Lister(),
 	}
@@ -159,6 +172,7 @@
 
 	c.endpointsController = c.newEndpointsController()
 	c.apisixUpstreamController = c.newApisixUpstreamController()
+	c.apisixRouteController = c.newApisixRouteController()
 	c.ingressController = c.newIngressController()
 
 	return c, nil
@@ -290,6 +304,9 @@
 	c.goAttach(func() {
 		c.ingressController.run(ctx)
 	})
+	c.goAttach(func() {
+		c.apisixRouteController.run(ctx)
+	})
 
 	ac := &Api6Controller{
 		KubeClientSet:             c.clientset,
@@ -299,8 +316,6 @@
 		Stop:                      ctx.Done(),
 	}
 
-	// ApisixRoute
-	ac.ApisixRoute(c)
 	// ApisixTLS
 	ac.ApisixTLS(c)
 
@@ -338,17 +353,6 @@
 	Stop                      <-chan struct{}
 }
 
-func (api6 *Api6Controller) ApisixRoute(controller *Controller) {
-	arc := BuildApisixRouteController(
-		api6.KubeClientSet,
-		api6.Api6ClientSet,
-		api6.SharedInformerFactory.Apisix().V1().ApisixRoutes(),
-		controller)
-	if err := arc.Run(api6.Stop); err != nil {
-		log.Errorf("failed to run ApisixRouteController: %s", err)
-	}
-}
-
 func (api6 *Api6Controller) ApisixTLS(controller *Controller) {
 	atc := BuildApisixTlsController(
 		api6.KubeClientSet,
diff --git a/pkg/ingress/controller/ingress.go b/pkg/ingress/controller/ingress.go
index fe0f108..5a042b5 100644
--- a/pkg/ingress/controller/ingress.go
+++ b/pkg/ingress/controller/ingress.go
@@ -87,6 +87,7 @@
 	namespace, name, err := cache.SplitMetaNamespaceKey(ingEv.Key)
 	if err != nil {
 		log.Errorf("found ingress resource with invalid meta namespace key %s: %s", ingEv.Key, err)
+		return err
 	}
 
 	var ing kube.Ingress
diff --git a/pkg/kube/apisix/apis/config/v2alpha1/doc.go b/pkg/kube/apisix/apis/config/v2alpha1/doc.go
new file mode 100644
index 0000000..fd42d17
--- /dev/null
+++ b/pkg/kube/apisix/apis/config/v2alpha1/doc.go
@@ -0,0 +1,18 @@
+// 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.
+// +k8s:deepcopy-gen=package
+
+// +groupName=apisix.apache.org
+package v2alpha1
diff --git a/pkg/kube/apisix/apis/config/v2alpha1/register.go b/pkg/kube/apisix/apis/config/v2alpha1/register.go
new file mode 100644
index 0000000..5af6a1b
--- /dev/null
+++ b/pkg/kube/apisix/apis/config/v2alpha1/register.go
@@ -0,0 +1,50 @@
+// 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 v2alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+const (
+	_groupName = "apisix.apache.org"
+	_version   = "v2alpha1"
+)
+
+var (
+	SchemeGroupVersion = schema.GroupVersion{
+		Group:   _groupName,
+		Version: _version,
+	}
+	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
+	AddToScheme   = SchemeBuilder.AddToScheme
+)
+
+func Resource(resource string) schema.GroupResource {
+	return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(SchemeGroupVersion,
+		&ApisixRoute{},
+		&ApisixRouteList{},
+	)
+
+	// register the type in the scheme
+	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
+	return nil
+}
diff --git a/pkg/kube/apisix/apis/config/v2alpha1/types.go b/pkg/kube/apisix/apis/config/v2alpha1/types.go
new file mode 100644
index 0000000..4e5ab94
--- /dev/null
+++ b/pkg/kube/apisix/apis/config/v2alpha1/types.go
@@ -0,0 +1,163 @@
+// 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 v2alpha1
+
+import (
+	"encoding/json"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+)
+
+// +genclient
+// +genclient:noStatus
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// ApisixRoute is used to define the route rules and upstreams for Apache APISIX.
+type ApisixRoute struct {
+	metav1.TypeMeta   `json:",inline" yaml:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
+	Spec              *ApisixRouteSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
+}
+
+// ApisixRouteSpec is the spec definition for ApisixRouteSpec.
+type ApisixRouteSpec struct {
+	HTTP []*ApisixRouteHTTP `json:"http,omitempty"`
+}
+
+// ApisixRouteHTTP represents a single route in for HTTP traffic.
+type ApisixRouteHTTP struct {
+	// The rule name, cannot be empty.
+	Name string `json:"name"`
+	// Route priority, when multiple routes contains
+	// same URI path (for path matching), route with
+	// higher priority will take effect.
+	Priority int                      `json:"priority,omitempty"`
+	Match    *ApisixRouteHTTPMatch    `json:"match,omitempty"`
+	Backend  *ApisixRouteHTTPBackend  `json:"backend"`
+	Plugins  []*ApisixRouteHTTPPlugin `json:"plugins,omitempty"`
+}
+
+// ApisixRouteHTTPMatch represents the match condition for hitting this route.
+type ApisixRouteHTTPMatch struct {
+	// URI path predicates, at least one path should be
+	// configured, path could be exact or prefix, for prefix path,
+	// append "*" after it, for instance, "/foo*".
+	Paths []string `json:"paths"`
+	// HTTP request method predicates.
+	Methods []string `json:"methods,omitempty"`
+	// HTTP Host predicates, host can be a wildcard domain or
+	// an exact domain. For wildcard domain, only one generic
+	// level is allowed, for instance, "*.foo.com" is valid but
+	// "*.*.foo.com" is not.
+	Hosts []string `json:"hosts,omitempty"`
+	// Remote address predicates, items can be valid IPv4 address
+	// or IPv6 address or CIDR.
+	RemoteAddrs []string `json:"remoteAddrs,omitempty"`
+	// NginxVars represents generic match predicates,
+	// it uses Nginx variable systems, so any predicate
+	// like headers, querystring and etc can be leveraged
+	// here to match the route.
+	// For instance, it can be:
+	// nginxVars:
+	//   - subject: "$remote_addr"
+	//     op: in
+	//     value:
+	//       - "127.0.0.1"
+	//       - "10.0.5.11"
+	NginxVars []ApisixRouteHTTPMatchNginxVar `json:"nginxVars,omitempty"`
+}
+
+// ApisixRouteHTTPMatchNginxVar represents a binary expression for the Nginx vars.
+type ApisixRouteHTTPMatchNginxVar struct {
+	// Subject is the expression subject, it can
+	// be any string composed by literals and nginx
+	// vars.
+	Subject string `json:"subject"`
+	// Op is the operator, can be:
+	// - "==": equal
+	// - "~=": not equal
+	// - ">": greater than
+	// - ">=": greater than or equal to
+	// - "<": less then
+	// - "<=": less then or equal to
+	// - "~~": regex match
+	// - "!~~": regex not match
+	// - "~*": case insensitive regex match
+	// - "!~*": case insensitive regex not match
+	// - "in": set match, Value should be a string array.
+	// - "not_in": set match, Value should be a string array.
+	// - "contain": subject contains the Value (inclusion relation).
+	// - "not_contain": subject doesn't contain the Value (inclusion relation).
+	Op string `json:"op"`
+	// Array is an array type object of the expression.
+	// It should be used when the Op is "in" or "not_in";
+	Array []string `json:"array"`
+	// Value is the normal type object for the expression,
+	// it should be used when the Op is not "in" and "not_in".
+	// Array and Value are exclusive so only of them can be set
+	// in the same time.
+	Value *string `json:"value"`
+}
+
+// ApisixRouteHTTPBackend represents a HTTP backend (a Kuberentes Service).
+type ApisixRouteHTTPBackend struct {
+	// The name (short) of the service, note cross namespace is forbidden,
+	// so be sure the ApisixRoute and Service are in the same namespace.
+	ServiceName string `json:"serviceName"`
+	// The service port, could be the name or the port number.
+	ServicePort intstr.IntOrString `json:"servicePort"`
+	// The resolve granularity, can be "endpoints" or "service",
+	// when set to "endpoints", the pod ips will be used; other
+	// wise, the service ClusterIP or ExternalIP will be used,
+	// default is endpoints.
+	ResolveGranularity string `json:"resolveGranularity"`
+}
+
+// ApisixRouteHTTPPlugin represents an APISIX plugin.
+type ApisixRouteHTTPPlugin struct {
+	// The plugin name.
+	Name string `json:"name"`
+	// Whether this plugin is in use, default is true.
+	Enable bool `json:"enable"`
+	// Plugin configuration.
+	// TODO we may use protobuf to define it.
+	Config ApisixRouteHTTPPluginConfig
+}
+
+// ApisixRouteHTTPPluginConfig is the configuration for
+// any plugins.
+type ApisixRouteHTTPPluginConfig map[string]interface{}
+
+func (p ApisixRouteHTTPPluginConfig) DeepCopyInto(out *ApisixRouteHTTPPluginConfig) {
+	b, _ := json.Marshal(&p)
+	_ = json.Unmarshal(b, out)
+}
+
+func (p *ApisixRouteHTTPPluginConfig) DeepCopy() *ApisixRouteHTTPPluginConfig {
+	if p == nil {
+		return nil
+	}
+	out := new(ApisixRouteHTTPPluginConfig)
+	p.DeepCopyInto(out)
+	return out
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type ApisixRouteList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata"`
+	Items           []ApisixRoute `json:"items,omitempty"`
+}
diff --git a/pkg/kube/apisix/apis/config/v2alpha1/zz_generated.deepcopy.go b/pkg/kube/apisix/apis/config/v2alpha1/zz_generated.deepcopy.go
new file mode 100644
index 0000000..5fd71bf
--- /dev/null
+++ b/pkg/kube/apisix/apis/config/v2alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,256 @@
+// +build !ignore_autogenerated
+
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by deepcopy-gen. DO NOT EDIT.
+
+package v2alpha1
+
+import (
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRoute) DeepCopyInto(out *ApisixRoute) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	if in.Spec != nil {
+		in, out := &in.Spec, &out.Spec
+		*out = new(ApisixRouteSpec)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRoute.
+func (in *ApisixRoute) DeepCopy() *ApisixRoute {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRoute)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ApisixRoute) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRouteHTTP) DeepCopyInto(out *ApisixRouteHTTP) {
+	*out = *in
+	if in.Match != nil {
+		in, out := &in.Match, &out.Match
+		*out = new(ApisixRouteHTTPMatch)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Backend != nil {
+		in, out := &in.Backend, &out.Backend
+		*out = new(ApisixRouteHTTPBackend)
+		**out = **in
+	}
+	if in.Plugins != nil {
+		in, out := &in.Plugins, &out.Plugins
+		*out = make([]*ApisixRouteHTTPPlugin, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(ApisixRouteHTTPPlugin)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRouteHTTP.
+func (in *ApisixRouteHTTP) DeepCopy() *ApisixRouteHTTP {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRouteHTTP)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRouteHTTPBackend) DeepCopyInto(out *ApisixRouteHTTPBackend) {
+	*out = *in
+	out.ServicePort = in.ServicePort
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRouteHTTPBackend.
+func (in *ApisixRouteHTTPBackend) DeepCopy() *ApisixRouteHTTPBackend {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRouteHTTPBackend)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRouteHTTPMatch) DeepCopyInto(out *ApisixRouteHTTPMatch) {
+	*out = *in
+	if in.Paths != nil {
+		in, out := &in.Paths, &out.Paths
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Methods != nil {
+		in, out := &in.Methods, &out.Methods
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Hosts != nil {
+		in, out := &in.Hosts, &out.Hosts
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.RemoteAddrs != nil {
+		in, out := &in.RemoteAddrs, &out.RemoteAddrs
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.NginxVars != nil {
+		in, out := &in.NginxVars, &out.NginxVars
+		*out = make([]ApisixRouteHTTPMatchNginxVar, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRouteHTTPMatch.
+func (in *ApisixRouteHTTPMatch) DeepCopy() *ApisixRouteHTTPMatch {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRouteHTTPMatch)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRouteHTTPMatchNginxVar) DeepCopyInto(out *ApisixRouteHTTPMatchNginxVar) {
+	*out = *in
+	if in.Array != nil {
+		in, out := &in.Array, &out.Array
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Value != nil {
+		in, out := &in.Value, &out.Value
+		*out = new(string)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRouteHTTPMatchNginxVar.
+func (in *ApisixRouteHTTPMatchNginxVar) DeepCopy() *ApisixRouteHTTPMatchNginxVar {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRouteHTTPMatchNginxVar)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRouteHTTPPlugin) DeepCopyInto(out *ApisixRouteHTTPPlugin) {
+	*out = *in
+	in.Config.DeepCopyInto(&out.Config)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRouteHTTPPlugin.
+func (in *ApisixRouteHTTPPlugin) DeepCopy() *ApisixRouteHTTPPlugin {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRouteHTTPPlugin)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRouteList) DeepCopyInto(out *ApisixRouteList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ApisixRoute, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRouteList.
+func (in *ApisixRouteList) DeepCopy() *ApisixRouteList {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRouteList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ApisixRouteList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ApisixRouteSpec) DeepCopyInto(out *ApisixRouteSpec) {
+	*out = *in
+	if in.HTTP != nil {
+		in, out := &in.HTTP, &out.HTTP
+		*out = make([]*ApisixRouteHTTP, len(*in))
+		for i := range *in {
+			if (*in)[i] != nil {
+				in, out := &(*in)[i], &(*out)[i]
+				*out = new(ApisixRouteHTTP)
+				(*in).DeepCopyInto(*out)
+			}
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRouteSpec.
+func (in *ApisixRouteSpec) DeepCopy() *ApisixRouteSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(ApisixRouteSpec)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/kube/apisix/client/clientset/versioned/clientset.go b/pkg/kube/apisix/client/clientset/versioned/clientset.go
index f85e3fd..1e82ba4 100644
--- a/pkg/kube/apisix/client/clientset/versioned/clientset.go
+++ b/pkg/kube/apisix/client/clientset/versioned/clientset.go
@@ -22,6 +22,7 @@
 	"fmt"
 
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/typed/config/v1"
+	apisixv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1"
 	discovery "k8s.io/client-go/discovery"
 	rest "k8s.io/client-go/rest"
 	flowcontrol "k8s.io/client-go/util/flowcontrol"
@@ -30,13 +31,15 @@
 type Interface interface {
 	Discovery() discovery.DiscoveryInterface
 	ApisixV1() apisixv1.ApisixV1Interface
+	ApisixV2alpha1() apisixv2alpha1.ApisixV2alpha1Interface
 }
 
 // Clientset contains the clients for groups. Each group has exactly one
 // version included in a Clientset.
 type Clientset struct {
 	*discovery.DiscoveryClient
-	apisixV1 *apisixv1.ApisixV1Client
+	apisixV1       *apisixv1.ApisixV1Client
+	apisixV2alpha1 *apisixv2alpha1.ApisixV2alpha1Client
 }
 
 // ApisixV1 retrieves the ApisixV1Client
@@ -44,6 +47,11 @@
 	return c.apisixV1
 }
 
+// ApisixV2alpha1 retrieves the ApisixV2alpha1Client
+func (c *Clientset) ApisixV2alpha1() apisixv2alpha1.ApisixV2alpha1Interface {
+	return c.apisixV2alpha1
+}
+
 // Discovery retrieves the DiscoveryClient
 func (c *Clientset) Discovery() discovery.DiscoveryInterface {
 	if c == nil {
@@ -69,6 +77,10 @@
 	if err != nil {
 		return nil, err
 	}
+	cs.apisixV2alpha1, err = apisixv2alpha1.NewForConfig(&configShallowCopy)
+	if err != nil {
+		return nil, err
+	}
 
 	cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
 	if err != nil {
@@ -82,6 +94,7 @@
 func NewForConfigOrDie(c *rest.Config) *Clientset {
 	var cs Clientset
 	cs.apisixV1 = apisixv1.NewForConfigOrDie(c)
+	cs.apisixV2alpha1 = apisixv2alpha1.NewForConfigOrDie(c)
 
 	cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
 	return &cs
@@ -91,6 +104,7 @@
 func New(c rest.Interface) *Clientset {
 	var cs Clientset
 	cs.apisixV1 = apisixv1.New(c)
+	cs.apisixV2alpha1 = apisixv2alpha1.New(c)
 
 	cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
 	return &cs
diff --git a/pkg/kube/apisix/client/clientset/versioned/fake/clientset_generated.go b/pkg/kube/apisix/client/clientset/versioned/fake/clientset_generated.go
index 7e27814..61caf81 100644
--- a/pkg/kube/apisix/client/clientset/versioned/fake/clientset_generated.go
+++ b/pkg/kube/apisix/client/clientset/versioned/fake/clientset_generated.go
@@ -22,6 +22,8 @@
 	clientset "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned"
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/typed/config/v1"
 	fakeapisixv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/typed/config/v1/fake"
+	apisixv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1"
+	fakeapisixv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/watch"
 	"k8s.io/client-go/discovery"
@@ -80,3 +82,8 @@
 func (c *Clientset) ApisixV1() apisixv1.ApisixV1Interface {
 	return &fakeapisixv1.FakeApisixV1{Fake: &c.Fake}
 }
+
+// ApisixV2alpha1 retrieves the ApisixV2alpha1Client
+func (c *Clientset) ApisixV2alpha1() apisixv2alpha1.ApisixV2alpha1Interface {
+	return &fakeapisixv2alpha1.FakeApisixV2alpha1{Fake: &c.Fake}
+}
diff --git a/pkg/kube/apisix/client/clientset/versioned/fake/register.go b/pkg/kube/apisix/client/clientset/versioned/fake/register.go
index 153181f..c013698 100644
--- a/pkg/kube/apisix/client/clientset/versioned/fake/register.go
+++ b/pkg/kube/apisix/client/clientset/versioned/fake/register.go
@@ -20,6 +20,7 @@
 
 import (
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	apisixv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	runtime "k8s.io/apimachinery/pkg/runtime"
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
@@ -32,6 +33,7 @@
 
 var localSchemeBuilder = runtime.SchemeBuilder{
 	apisixv1.AddToScheme,
+	apisixv2alpha1.AddToScheme,
 }
 
 // AddToScheme adds all types of this clientset into the given scheme. This allows composition
diff --git a/pkg/kube/apisix/client/clientset/versioned/scheme/register.go b/pkg/kube/apisix/client/clientset/versioned/scheme/register.go
index f91acfa..00b3917 100644
--- a/pkg/kube/apisix/client/clientset/versioned/scheme/register.go
+++ b/pkg/kube/apisix/client/clientset/versioned/scheme/register.go
@@ -20,6 +20,7 @@
 
 import (
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	apisixv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	runtime "k8s.io/apimachinery/pkg/runtime"
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
@@ -32,6 +33,7 @@
 var ParameterCodec = runtime.NewParameterCodec(Scheme)
 var localSchemeBuilder = runtime.SchemeBuilder{
 	apisixv1.AddToScheme,
+	apisixv2alpha1.AddToScheme,
 }
 
 // AddToScheme adds all types of this clientset into the given scheme. This allows composition
diff --git a/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/apisixroute.go b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/apisixroute.go
new file mode 100644
index 0000000..91a26c4
--- /dev/null
+++ b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/apisixroute.go
@@ -0,0 +1,178 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v2alpha1
+
+import (
+	"context"
+	"time"
+
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
+	scheme "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/scheme"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	rest "k8s.io/client-go/rest"
+)
+
+// ApisixRoutesGetter has a method to return a ApisixRouteInterface.
+// A group's client should implement this interface.
+type ApisixRoutesGetter interface {
+	ApisixRoutes(namespace string) ApisixRouteInterface
+}
+
+// ApisixRouteInterface has methods to work with ApisixRoute resources.
+type ApisixRouteInterface interface {
+	Create(ctx context.Context, apisixRoute *v2alpha1.ApisixRoute, opts v1.CreateOptions) (*v2alpha1.ApisixRoute, error)
+	Update(ctx context.Context, apisixRoute *v2alpha1.ApisixRoute, opts v1.UpdateOptions) (*v2alpha1.ApisixRoute, error)
+	Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+	DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+	Get(ctx context.Context, name string, opts v1.GetOptions) (*v2alpha1.ApisixRoute, error)
+	List(ctx context.Context, opts v1.ListOptions) (*v2alpha1.ApisixRouteList, error)
+	Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.ApisixRoute, err error)
+	ApisixRouteExpansion
+}
+
+// apisixRoutes implements ApisixRouteInterface
+type apisixRoutes struct {
+	client rest.Interface
+	ns     string
+}
+
+// newApisixRoutes returns a ApisixRoutes
+func newApisixRoutes(c *ApisixV2alpha1Client, namespace string) *apisixRoutes {
+	return &apisixRoutes{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the apisixRoute, and returns the corresponding apisixRoute object, and an error if there is any.
+func (c *apisixRoutes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.ApisixRoute, err error) {
+	result = &v2alpha1.ApisixRoute{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of ApisixRoutes that match those selectors.
+func (c *apisixRoutes) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.ApisixRouteList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v2alpha1.ApisixRouteList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested apisixRoutes.
+func (c *apisixRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	opts.Watch = true
+	return c.client.Get().
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch(ctx)
+}
+
+// Create takes the representation of a apisixRoute and creates it.  Returns the server's representation of the apisixRoute, and an error, if there is any.
+func (c *apisixRoutes) Create(ctx context.Context, apisixRoute *v2alpha1.ApisixRoute, opts v1.CreateOptions) (result *v2alpha1.ApisixRoute, err error) {
+	result = &v2alpha1.ApisixRoute{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(apisixRoute).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Update takes the representation of a apisixRoute and updates it. Returns the server's representation of the apisixRoute, and an error, if there is any.
+func (c *apisixRoutes) Update(ctx context.Context, apisixRoute *v2alpha1.ApisixRoute, opts v1.UpdateOptions) (result *v2alpha1.ApisixRoute, err error) {
+	result = &v2alpha1.ApisixRoute{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		Name(apisixRoute.Name).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(apisixRoute).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Delete takes name of the apisixRoute and deletes it. Returns an error if one occurs.
+func (c *apisixRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		Name(name).
+		Body(&opts).
+		Do(ctx).
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *apisixRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+	var timeout time.Duration
+	if listOpts.TimeoutSeconds != nil {
+		timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+	}
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		VersionedParams(&listOpts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(&opts).
+		Do(ctx).
+		Error()
+}
+
+// Patch applies the patch and returns the patched apisixRoute.
+func (c *apisixRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.ApisixRoute, err error) {
+	result = &v2alpha1.ApisixRoute{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("apisixroutes").
+		Name(name).
+		SubResource(subresources...).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(data).
+		Do(ctx).
+		Into(result)
+	return
+}
diff --git a/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/config_client.go b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/config_client.go
new file mode 100644
index 0000000..ade8d12
--- /dev/null
+++ b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/config_client.go
@@ -0,0 +1,89 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v2alpha1
+
+import (
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
+	"github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/scheme"
+	rest "k8s.io/client-go/rest"
+)
+
+type ApisixV2alpha1Interface interface {
+	RESTClient() rest.Interface
+	ApisixRoutesGetter
+}
+
+// ApisixV2alpha1Client is used to interact with features provided by the apisix.apache.org group.
+type ApisixV2alpha1Client struct {
+	restClient rest.Interface
+}
+
+func (c *ApisixV2alpha1Client) ApisixRoutes(namespace string) ApisixRouteInterface {
+	return newApisixRoutes(c, namespace)
+}
+
+// NewForConfig creates a new ApisixV2alpha1Client for the given config.
+func NewForConfig(c *rest.Config) (*ApisixV2alpha1Client, error) {
+	config := *c
+	if err := setConfigDefaults(&config); err != nil {
+		return nil, err
+	}
+	client, err := rest.RESTClientFor(&config)
+	if err != nil {
+		return nil, err
+	}
+	return &ApisixV2alpha1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new ApisixV2alpha1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *ApisixV2alpha1Client {
+	client, err := NewForConfig(c)
+	if err != nil {
+		panic(err)
+	}
+	return client
+}
+
+// New creates a new ApisixV2alpha1Client for the given RESTClient.
+func New(c rest.Interface) *ApisixV2alpha1Client {
+	return &ApisixV2alpha1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) error {
+	gv := v2alpha1.SchemeGroupVersion
+	config.GroupVersion = &gv
+	config.APIPath = "/apis"
+	config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
+
+	if config.UserAgent == "" {
+		config.UserAgent = rest.DefaultKubernetesUserAgent()
+	}
+
+	return nil
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *ApisixV2alpha1Client) RESTClient() rest.Interface {
+	if c == nil {
+		return nil
+	}
+	return c.restClient
+}
diff --git a/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/doc.go b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/doc.go
new file mode 100644
index 0000000..3efe0d2
--- /dev/null
+++ b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated typed clients.
+package v2alpha1
diff --git a/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/doc.go b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/doc.go
new file mode 100644
index 0000000..16f4439
--- /dev/null
+++ b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// Package fake has the automatically generated clients.
+package fake
diff --git a/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/fake_apisixroute.go b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/fake_apisixroute.go
new file mode 100644
index 0000000..a57673b
--- /dev/null
+++ b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/fake_apisixroute.go
@@ -0,0 +1,130 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	"context"
+
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeApisixRoutes implements ApisixRouteInterface
+type FakeApisixRoutes struct {
+	Fake *FakeApisixV2alpha1
+	ns   string
+}
+
+var apisixroutesResource = schema.GroupVersionResource{Group: "apisix.apache.org", Version: "v2alpha1", Resource: "apisixroutes"}
+
+var apisixroutesKind = schema.GroupVersionKind{Group: "apisix.apache.org", Version: "v2alpha1", Kind: "ApisixRoute"}
+
+// Get takes name of the apisixRoute, and returns the corresponding apisixRoute object, and an error if there is any.
+func (c *FakeApisixRoutes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.ApisixRoute, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(apisixroutesResource, c.ns, name), &v2alpha1.ApisixRoute{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v2alpha1.ApisixRoute), err
+}
+
+// List takes label and field selectors, and returns the list of ApisixRoutes that match those selectors.
+func (c *FakeApisixRoutes) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.ApisixRouteList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(apisixroutesResource, apisixroutesKind, c.ns, opts), &v2alpha1.ApisixRouteList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v2alpha1.ApisixRouteList{ListMeta: obj.(*v2alpha1.ApisixRouteList).ListMeta}
+	for _, item := range obj.(*v2alpha1.ApisixRouteList).Items {
+		if label.Matches(labels.Set(item.Labels)) {
+			list.Items = append(list.Items, item)
+		}
+	}
+	return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested apisixRoutes.
+func (c *FakeApisixRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(apisixroutesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a apisixRoute and creates it.  Returns the server's representation of the apisixRoute, and an error, if there is any.
+func (c *FakeApisixRoutes) Create(ctx context.Context, apisixRoute *v2alpha1.ApisixRoute, opts v1.CreateOptions) (result *v2alpha1.ApisixRoute, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(apisixroutesResource, c.ns, apisixRoute), &v2alpha1.ApisixRoute{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v2alpha1.ApisixRoute), err
+}
+
+// Update takes the representation of a apisixRoute and updates it. Returns the server's representation of the apisixRoute, and an error, if there is any.
+func (c *FakeApisixRoutes) Update(ctx context.Context, apisixRoute *v2alpha1.ApisixRoute, opts v1.UpdateOptions) (result *v2alpha1.ApisixRoute, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(apisixroutesResource, c.ns, apisixRoute), &v2alpha1.ApisixRoute{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v2alpha1.ApisixRoute), err
+}
+
+// Delete takes name of the apisixRoute and deletes it. Returns an error if one occurs.
+func (c *FakeApisixRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(apisixroutesResource, c.ns, name), &v2alpha1.ApisixRoute{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeApisixRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(apisixroutesResource, c.ns, listOpts)
+
+	_, err := c.Fake.Invokes(action, &v2alpha1.ApisixRouteList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched apisixRoute.
+func (c *FakeApisixRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.ApisixRoute, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(apisixroutesResource, c.ns, name, pt, data, subresources...), &v2alpha1.ApisixRoute{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v2alpha1.ApisixRoute), err
+}
diff --git a/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/fake_config_client.go b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/fake_config_client.go
new file mode 100644
index 0000000..448bafe
--- /dev/null
+++ b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/fake/fake_config_client.go
@@ -0,0 +1,40 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1"
+	rest "k8s.io/client-go/rest"
+	testing "k8s.io/client-go/testing"
+)
+
+type FakeApisixV2alpha1 struct {
+	*testing.Fake
+}
+
+func (c *FakeApisixV2alpha1) ApisixRoutes(namespace string) v2alpha1.ApisixRouteInterface {
+	return &FakeApisixRoutes{c, namespace}
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *FakeApisixV2alpha1) RESTClient() rest.Interface {
+	var ret *rest.RESTClient
+	return ret
+}
diff --git a/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/generated_expansion.go b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/generated_expansion.go
new file mode 100644
index 0000000..980dad5
--- /dev/null
+++ b/pkg/kube/apisix/client/clientset/versioned/typed/config/v2alpha1/generated_expansion.go
@@ -0,0 +1,21 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v2alpha1
+
+type ApisixRouteExpansion interface{}
diff --git a/pkg/kube/apisix/client/informers/externalversions/config/interface.go b/pkg/kube/apisix/client/informers/externalversions/config/interface.go
index 8dc3dca..c881109 100644
--- a/pkg/kube/apisix/client/informers/externalversions/config/interface.go
+++ b/pkg/kube/apisix/client/informers/externalversions/config/interface.go
@@ -20,6 +20,7 @@
 
 import (
 	v1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions/config/v1"
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions/config/v2alpha1"
 	internalinterfaces "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions/internalinterfaces"
 )
 
@@ -27,6 +28,8 @@
 type Interface interface {
 	// V1 provides access to shared informers for resources in V1.
 	V1() v1.Interface
+	// V2alpha1 provides access to shared informers for resources in V2alpha1.
+	V2alpha1() v2alpha1.Interface
 }
 
 type group struct {
@@ -44,3 +47,8 @@
 func (g *group) V1() v1.Interface {
 	return v1.New(g.factory, g.namespace, g.tweakListOptions)
 }
+
+// V2alpha1 returns a new v2alpha1.Interface.
+func (g *group) V2alpha1() v2alpha1.Interface {
+	return v2alpha1.New(g.factory, g.namespace, g.tweakListOptions)
+}
diff --git a/pkg/kube/apisix/client/informers/externalversions/config/v2alpha1/apisixroute.go b/pkg/kube/apisix/client/informers/externalversions/config/v2alpha1/apisixroute.go
new file mode 100644
index 0000000..b793bbf
--- /dev/null
+++ b/pkg/kube/apisix/client/informers/externalversions/config/v2alpha1/apisixroute.go
@@ -0,0 +1,90 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v2alpha1
+
+import (
+	"context"
+	time "time"
+
+	configv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
+	versioned "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned"
+	internalinterfaces "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions/internalinterfaces"
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v2alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	watch "k8s.io/apimachinery/pkg/watch"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// ApisixRouteInformer provides access to a shared informer and lister for
+// ApisixRoutes.
+type ApisixRouteInformer interface {
+	Informer() cache.SharedIndexInformer
+	Lister() v2alpha1.ApisixRouteLister
+}
+
+type apisixRouteInformer struct {
+	factory          internalinterfaces.SharedInformerFactory
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+	namespace        string
+}
+
+// NewApisixRouteInformer constructs a new informer for ApisixRoute type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewApisixRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+	return NewFilteredApisixRouteInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredApisixRouteInformer constructs a new informer for ApisixRoute type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredApisixRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+	return cache.NewSharedIndexInformer(
+		&cache.ListWatch{
+			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.ApisixV2alpha1().ApisixRoutes(namespace).List(context.TODO(), options)
+			},
+			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.ApisixV2alpha1().ApisixRoutes(namespace).Watch(context.TODO(), options)
+			},
+		},
+		&configv2alpha1.ApisixRoute{},
+		resyncPeriod,
+		indexers,
+	)
+}
+
+func (f *apisixRouteInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+	return NewFilteredApisixRouteInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *apisixRouteInformer) Informer() cache.SharedIndexInformer {
+	return f.factory.InformerFor(&configv2alpha1.ApisixRoute{}, f.defaultInformer)
+}
+
+func (f *apisixRouteInformer) Lister() v2alpha1.ApisixRouteLister {
+	return v2alpha1.NewApisixRouteLister(f.Informer().GetIndexer())
+}
diff --git a/pkg/kube/apisix/client/informers/externalversions/config/v2alpha1/interface.go b/pkg/kube/apisix/client/informers/externalversions/config/v2alpha1/interface.go
new file mode 100644
index 0000000..620edb7
--- /dev/null
+++ b/pkg/kube/apisix/client/informers/externalversions/config/v2alpha1/interface.go
@@ -0,0 +1,45 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v2alpha1
+
+import (
+	internalinterfaces "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to all the informers in this group version.
+type Interface interface {
+	// ApisixRoutes returns a ApisixRouteInformer.
+	ApisixRoutes() ApisixRouteInformer
+}
+
+type version struct {
+	factory          internalinterfaces.SharedInformerFactory
+	namespace        string
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+	return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// ApisixRoutes returns a ApisixRouteInformer.
+func (v *version) ApisixRoutes() ApisixRouteInformer {
+	return &apisixRouteInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
diff --git a/pkg/kube/apisix/client/informers/externalversions/generic.go b/pkg/kube/apisix/client/informers/externalversions/generic.go
index ffd2686..8c2e73e 100644
--- a/pkg/kube/apisix/client/informers/externalversions/generic.go
+++ b/pkg/kube/apisix/client/informers/externalversions/generic.go
@@ -22,6 +22,7 @@
 	"fmt"
 
 	v1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
 	cache "k8s.io/client-go/tools/cache"
 )
@@ -60,6 +61,10 @@
 	case v1.SchemeGroupVersion.WithResource("apisixupstreams"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Apisix().V1().ApisixUpstreams().Informer()}, nil
 
+		// Group=apisix.apache.org, Version=v2alpha1
+	case v2alpha1.SchemeGroupVersion.WithResource("apisixroutes"):
+		return &genericInformer{resource: resource.GroupResource(), informer: f.Apisix().V2alpha1().ApisixRoutes().Informer()}, nil
+
 	}
 
 	return nil, fmt.Errorf("no informer found for %v", resource)
diff --git a/pkg/kube/apisix/client/listers/config/v2alpha1/apisixroute.go b/pkg/kube/apisix/client/listers/config/v2alpha1/apisixroute.go
new file mode 100644
index 0000000..687e227
--- /dev/null
+++ b/pkg/kube/apisix/client/listers/config/v2alpha1/apisixroute.go
@@ -0,0 +1,99 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v2alpha1
+
+import (
+	v2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/client-go/tools/cache"
+)
+
+// ApisixRouteLister helps list ApisixRoutes.
+// All objects returned here must be treated as read-only.
+type ApisixRouteLister interface {
+	// List lists all ApisixRoutes in the indexer.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*v2alpha1.ApisixRoute, err error)
+	// ApisixRoutes returns an object that can list and get ApisixRoutes.
+	ApisixRoutes(namespace string) ApisixRouteNamespaceLister
+	ApisixRouteListerExpansion
+}
+
+// apisixRouteLister implements the ApisixRouteLister interface.
+type apisixRouteLister struct {
+	indexer cache.Indexer
+}
+
+// NewApisixRouteLister returns a new ApisixRouteLister.
+func NewApisixRouteLister(indexer cache.Indexer) ApisixRouteLister {
+	return &apisixRouteLister{indexer: indexer}
+}
+
+// List lists all ApisixRoutes in the indexer.
+func (s *apisixRouteLister) List(selector labels.Selector) (ret []*v2alpha1.ApisixRoute, err error) {
+	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+		ret = append(ret, m.(*v2alpha1.ApisixRoute))
+	})
+	return ret, err
+}
+
+// ApisixRoutes returns an object that can list and get ApisixRoutes.
+func (s *apisixRouteLister) ApisixRoutes(namespace string) ApisixRouteNamespaceLister {
+	return apisixRouteNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// ApisixRouteNamespaceLister helps list and get ApisixRoutes.
+// All objects returned here must be treated as read-only.
+type ApisixRouteNamespaceLister interface {
+	// List lists all ApisixRoutes in the indexer for a given namespace.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*v2alpha1.ApisixRoute, err error)
+	// Get retrieves the ApisixRoute from the indexer for a given namespace and name.
+	// Objects returned here must be treated as read-only.
+	Get(name string) (*v2alpha1.ApisixRoute, error)
+	ApisixRouteNamespaceListerExpansion
+}
+
+// apisixRouteNamespaceLister implements the ApisixRouteNamespaceLister
+// interface.
+type apisixRouteNamespaceLister struct {
+	indexer   cache.Indexer
+	namespace string
+}
+
+// List lists all ApisixRoutes in the indexer for a given namespace.
+func (s apisixRouteNamespaceLister) List(selector labels.Selector) (ret []*v2alpha1.ApisixRoute, err error) {
+	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+		ret = append(ret, m.(*v2alpha1.ApisixRoute))
+	})
+	return ret, err
+}
+
+// Get retrieves the ApisixRoute from the indexer for a given namespace and name.
+func (s apisixRouteNamespaceLister) Get(name string) (*v2alpha1.ApisixRoute, error) {
+	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+	if err != nil {
+		return nil, err
+	}
+	if !exists {
+		return nil, errors.NewNotFound(v2alpha1.Resource("apisixroute"), name)
+	}
+	return obj.(*v2alpha1.ApisixRoute), nil
+}
diff --git a/pkg/kube/apisix/client/listers/config/v2alpha1/expansion_generated.go b/pkg/kube/apisix/client/listers/config/v2alpha1/expansion_generated.go
new file mode 100644
index 0000000..e26b85d
--- /dev/null
+++ b/pkg/kube/apisix/client/listers/config/v2alpha1/expansion_generated.go
@@ -0,0 +1,27 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed 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.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v2alpha1
+
+// ApisixRouteListerExpansion allows custom methods to be added to
+// ApisixRouteLister.
+type ApisixRouteListerExpansion interface{}
+
+// ApisixRouteNamespaceListerExpansion allows custom methods to be added to
+// ApisixRouteNamespaceLister.
+type ApisixRouteNamespaceListerExpansion interface{}
diff --git a/pkg/kube/apisix_route.go b/pkg/kube/apisix_route.go
new file mode 100644
index 0000000..0cd6bc5
--- /dev/null
+++ b/pkg/kube/apisix_route.go
@@ -0,0 +1,175 @@
+// 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 kube
+
+import (
+	"errors"
+
+	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	configv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
+	listersv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v1"
+	listersv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v2alpha1"
+)
+
+const (
+	// ApisixRouteV1 represents the ApisixRoute in apisix.apache.org/v1 group version.
+	ApisixRouteV1 = "apisix.apache.org/v1"
+	// ApisixRouteV2alpha1 represents the APisixRoute in apisix.apache.org/v2alpha1 group version
+	ApisixRouteV2alpha1 = "apisix.apache.org/v2alpha1"
+)
+
+// ApisixRouteLister is an encapsulation for the lister of ApisixRoute,
+// it aims at to be compatible with different ApisixRoute versions.
+type ApisixRouteLister interface {
+	// V1 gets the ApisixRoute in apisix.apache.org/v1.
+	V1(string, string) (ApisixRoute, error)
+	// V2alpha1 gets the ApisixRoute in apisix.apache.org/v2alpha1.
+	V2alpha1(string, string) (ApisixRoute, error)
+}
+
+// ApisixRouteInformer is an encapsulation for the informer of ApisixRoute,
+// it aims at to be compatible with different ApisixRoute versions.
+type ApisixRouteInformer interface {
+	Run(chan struct{})
+}
+
+// ApisixRoute is an encapsulation for ApisixRoute resource with different
+// versions, for now, they are apisix.apache.org/v1 and apisix.apache.org/v2alpha1
+type ApisixRoute interface {
+	// GroupVersion returns the api group version of the
+	// real ApisixRoute.
+	GroupVersion() string
+	// V1 returns the ApisixRoute in apisix.apache.org/v1, the real
+	// ApisixRoute must be in this group version, or V1() will panic.
+	V1() *configv1.ApisixRoute
+	// V2alpha1 returns the ApisixRoute in apisix.apache.org/v1alpha1, the real
+	// ApisixRoute must be in this group version, or V2alpha1() will panic.
+	V2alpha1() *configv2alpha1.ApisixRoute
+	// ResourceVersion returns the the resource version field inside
+	// the real ApisixRoute.
+	ResourceVersion() string
+}
+
+// ApisixRouteEvent contains the ApisixRoute key (namespace/name)
+// and the group version message.
+type ApisixRouteEvent struct {
+	Key          string
+	OldObject    ApisixRoute
+	GroupVersion string
+}
+
+type apisixRoute struct {
+	groupVersion string
+	v1           *configv1.ApisixRoute
+	v2alpha1     *configv2alpha1.ApisixRoute
+}
+
+func (ar *apisixRoute) V1() *configv1.ApisixRoute {
+	if ar.groupVersion != ApisixRouteV1 {
+		panic("not a apisix.apache.org/v1 ingress")
+	}
+	return ar.v1
+}
+
+func (ar *apisixRoute) V2alpha1() *configv2alpha1.ApisixRoute {
+	if ar.groupVersion != ApisixRouteV2alpha1 {
+		panic("not a apisix.apache.org/v2alpha1 ingress")
+	}
+	return ar.v2alpha1
+}
+
+func (ar *apisixRoute) GroupVersion() string {
+	return ar.groupVersion
+}
+
+func (ar *apisixRoute) ResourceVersion() string {
+	if ar.groupVersion == ApisixRouteV1 {
+		return ar.V1().ResourceVersion
+	}
+	return ar.V2alpha1().ResourceVersion
+}
+
+type apisixRouteLister struct {
+	v1Lister       listersv1.ApisixRouteLister
+	v2alpha1Lister listersv2alpha1.ApisixRouteLister
+}
+
+func (l *apisixRouteLister) V1(namespace, name string) (ApisixRoute, error) {
+	ar, err := l.v1Lister.ApisixRoutes(namespace).Get(name)
+	if err != nil {
+		return nil, err
+	}
+	return &apisixRoute{
+		groupVersion: ApisixRouteV1,
+		v1:           ar,
+	}, nil
+}
+
+func (l *apisixRouteLister) V2alpha1(namespace, name string) (ApisixRoute, error) {
+	ar, err := l.v2alpha1Lister.ApisixRoutes(namespace).Get(name)
+	if err != nil {
+		return nil, err
+	}
+	return &apisixRoute{
+		groupVersion: ApisixRouteV2alpha1,
+		v2alpha1:     ar,
+	}, nil
+}
+
+// MustNewApisixRoute creates a kube.ApisixRoute object according to the
+// type of obj.
+func MustNewApisixRoute(obj interface{}) ApisixRoute {
+	switch ar := obj.(type) {
+	case *configv1.ApisixRoute:
+		return &apisixRoute{
+			groupVersion: ApisixRouteV1,
+			v1:           ar,
+		}
+	case *configv2alpha1.ApisixRoute:
+		return &apisixRoute{
+			groupVersion: ApisixRouteV2alpha1,
+			v2alpha1:     ar,
+		}
+	default:
+		panic("invalid ApisixRoute type")
+	}
+}
+
+// NewApisixRoute creates a kube.ApisixRoute object according to the
+// type of obj. It returns nil and the error reason when the
+// type assertion fails.
+func NewApisixRoute(obj interface{}) (ApisixRoute, error) {
+	switch ar := obj.(type) {
+	case *configv1.ApisixRoute:
+		return &apisixRoute{
+			groupVersion: ApisixRouteV1,
+			v1:           ar,
+		}, nil
+	case *configv2alpha1.ApisixRoute:
+		return &apisixRoute{
+			groupVersion: ApisixRouteV2alpha1,
+			v2alpha1:     ar,
+		}, nil
+	default:
+		return nil, errors.New("invalid ApisixRoute type")
+	}
+}
+
+func NewApisixRouteLister(v1 listersv1.ApisixRouteLister, v2alpha1 listersv2alpha1.ApisixRouteLister) ApisixRouteLister {
+	return &apisixRouteLister{
+		v1Lister:       v1,
+		v2alpha1Lister: v2alpha1,
+	}
+}
diff --git a/pkg/kube/translation/apisix_route.go b/pkg/kube/translation/apisix_route.go
index a7909e1..ab61048 100644
--- a/pkg/kube/translation/apisix_route.go
+++ b/pkg/kube/translation/apisix_route.go
@@ -15,8 +15,15 @@
 package translation
 
 import (
+	"errors"
+
+	"go.uber.org/zap"
+	"k8s.io/apimachinery/pkg/util/intstr"
+
 	"github.com/apache/apisix-ingress-controller/pkg/id"
 	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	configv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
+	"github.com/apache/apisix-ingress-controller/pkg/log"
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
 
@@ -84,3 +91,113 @@
 	}
 	return routes, upstreams, nil
 }
+
+func (t *translator) TranslateRouteV2alpha1(ar *configv2alpha1.ApisixRoute) ([]*apisixv1.Route, []*apisixv1.Upstream, error) {
+	var (
+		routes    []*apisixv1.Route
+		upstreams []*apisixv1.Upstream
+	)
+
+	upstreamMap := make(map[string]*apisixv1.Upstream)
+
+	for _, part := range ar.Spec.HTTP {
+		if part.Match == nil {
+			return nil, nil, errors.New("empty route match section")
+		}
+		if len(part.Match.Paths) < 1 {
+			return nil, nil, errors.New("empty route paths match")
+		}
+		svc, err := t.ServiceLister.Services(ar.Namespace).Get(part.Backend.ServiceName)
+		if err != nil {
+			return nil, nil, err
+		}
+		svcPort := int32(-1)
+	loop:
+		for _, port := range svc.Spec.Ports {
+			switch part.Backend.ServicePort.Type {
+			case intstr.Int:
+				if part.Backend.ServicePort.IntVal == port.Port {
+					svcPort = port.Port
+					break loop
+				}
+			case intstr.String:
+				if part.Backend.ServicePort.StrVal == port.Name {
+					svcPort = port.Port
+					break loop
+				}
+			}
+		}
+		if svcPort == -1 {
+			log.Errorw("ApisixRoute refers to non-existent Service port",
+				zap.Any("ApisixRoute", ar),
+				zap.String("port", part.Backend.ServicePort.String()),
+			)
+			return nil, nil, err
+		}
+
+		if part.Backend.ResolveGranularity == "service" && svc.Spec.ClusterIP == "" {
+			log.Errorw("ApisixRoute refers to a headless service but want to use the service level resolve granualrity",
+				zap.Any("ApisixRoute", ar),
+				zap.Any("service", svc),
+			)
+			return nil, nil, errors.New("conflict headless service and backend resolve granularity")
+		}
+
+		pluginMap := make(apisixv1.Plugins)
+		// 2.add route plugins
+		for _, plugin := range part.Plugins {
+			if !plugin.Enable {
+				continue
+			}
+			if plugin.Config != nil {
+				pluginMap[plugin.Name] = plugin.Config
+			} else {
+				pluginMap[plugin.Name] = make(map[string]interface{})
+			}
+		}
+
+		routeName := apisixv1.ComposeRouteName(ar.Namespace, ar.Name, part.Name)
+		upstreamName := apisixv1.ComposeUpstreamName(ar.Namespace, part.Backend.ServiceName, svcPort)
+		route := &apisixv1.Route{
+			Metadata: apisixv1.Metadata{
+				FullName:        routeName,
+				Name:            routeName,
+				ID:              id.GenID(routeName),
+				ResourceVersion: ar.ResourceVersion,
+			},
+			Hosts:        part.Match.Hosts,
+			Uris:         part.Match.Paths,
+			Methods:      part.Match.Methods,
+			UpstreamName: upstreamName,
+			UpstreamId:   id.GenID(upstreamName),
+			Plugins:      pluginMap,
+		}
+
+		routes = append(routes, route)
+
+		if _, ok := upstreamMap[upstreamName]; !ok {
+			ups, err := t.TranslateUpstream(ar.Namespace, part.Backend.ServiceName, svcPort)
+			if err != nil {
+				return nil, nil, err
+			}
+			if part.Backend.ResolveGranularity == "service" {
+				ups.Nodes = []apisixv1.UpstreamNode{
+					{
+						IP:     svc.Spec.ClusterIP,
+						Port:   int(svcPort),
+						Weight: _defaultWeight,
+					},
+				}
+			}
+			ups.FullName = upstreamName
+			ups.ResourceVersion = ar.ResourceVersion
+			ups.Name = upstreamName
+			upstreamMap[ups.FullName] = ups
+		}
+	}
+
+	for _, ups := range upstreamMap {
+		upstreams = append(upstreams, ups)
+	}
+	return routes, upstreams, nil
+}
diff --git a/pkg/kube/translation/translator.go b/pkg/kube/translation/translator.go
index da31d20..f7843b6 100644
--- a/pkg/kube/translation/translator.go
+++ b/pkg/kube/translation/translator.go
@@ -23,6 +23,7 @@
 
 	"github.com/apache/apisix-ingress-controller/pkg/kube"
 	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	configv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
 	listersv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v1"
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
@@ -60,6 +61,9 @@
 	// TranslateRouteV1 translates the configv1.ApisixRoute object into several Route
 	// and Upstream resources.
 	TranslateRouteV1(*configv1.ApisixRoute) ([]*apisixv1.Route, []*apisixv1.Upstream, error)
+	// TranslateRouteV2alpha1 translates the configv2alph1.ApisixRoute object into several Route
+	// and Upstream resources.
+	TranslateRouteV2alpha1(*configv2alpha1.ApisixRoute) ([]*apisixv1.Route, []*apisixv1.Upstream, error)
 }
 
 // TranslatorOptions contains options to help Translator
diff --git a/pkg/types/apisix/v1/types.go b/pkg/types/apisix/v1/types.go
index 638858c..57eaf4c 100644
--- a/pkg/types/apisix/v1/types.go
+++ b/pkg/types/apisix/v1/types.go
@@ -82,7 +82,9 @@
 	Metadata `json:",inline" yaml:",inline"`
 
 	Host         string   `json:"host,omitempty" yaml:"host,omitempty"`
+	Hosts        []string `json:"hosts,omitempty" yaml:"hosts,omitempty"`
 	Path         string   `json:"path,omitempty" yaml:"path,omitempty"`
+	Priority     int      `json:"priority,omitempty" yaml:"priority,omitempty"`
 	Uris         []string `json:"uris,omitempty" yaml:"uris,omitempty"`
 	Methods      []string `json:"methods,omitempty" yaml:"methods,omitempty"`
 	ServiceId    string   `json:"service_id,omitempty" yaml:"service_id,omitempty"`
@@ -264,3 +266,20 @@
 
 	return buf.String()
 }
+
+// ComposeRouteName uses namespace, name and rule name to compose
+// the route name.
+func ComposeRouteName(namespace, name string, rule string) string {
+	// FIXME Use sync.Pool to reuse this buffer if the upstream
+	// name composing code path is hot.
+	p := make([]byte, 0, len(namespace)+len(name)+len(rule)+2)
+	buf := bytes.NewBuffer(p)
+
+	buf.WriteString(namespace)
+	buf.WriteByte('_')
+	buf.WriteString(name)
+	buf.WriteByte('_')
+	buf.WriteString(rule)
+
+	return buf.String()
+}
diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go b/pkg/types/apisix/v1/zz_generated.deepcopy.go
index 51869da..d914f5e 100644
--- a/pkg/types/apisix/v1/zz_generated.deepcopy.go
+++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go
@@ -24,6 +24,16 @@
 func (in *Route) DeepCopyInto(out *Route) {
 	*out = *in
 	out.Metadata = in.Metadata
+	if in.Hosts != nil {
+		in, out := &in.Hosts, &out.Hosts
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Uris != nil {
+		in, out := &in.Uris, &out.Uris
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
 	if in.Methods != nil {
 		in, out := &in.Methods, &out.Methods
 		*out = make([]string, len(*in))
diff --git a/samples/deploy/crd/v1beta1/ApisixRoute.yaml b/samples/deploy/crd/v1beta1/ApisixRoute.yaml
index a4e495d..af70902 100644
--- a/samples/deploy/crd/v1beta1/ApisixRoute.yaml
+++ b/samples/deploy/crd/v1beta1/ApisixRoute.yaml
@@ -24,6 +24,9 @@
   versions:
     - name: v1
       served: true
+      storage: false
+    - name: v2alpha1
+      served: true
       storage: true
   scope: Namespaced
   names:
diff --git a/test/e2e/endpoints/endpoints.go b/test/e2e/endpoints/endpoints.go
index 86a1ff2..8cc2c4f 100644
--- a/test/e2e/endpoints/endpoints.go
+++ b/test/e2e/endpoints/endpoints.go
@@ -105,7 +105,7 @@
        path: /ip
 `, backendSvc, backendSvcPort[0])
 		assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(apisixRoute))
-		time.Sleep(3 * time.Second)
+		time.Sleep(5 * time.Second)
 		ups, err := s.ListApisixUpstreams()
 		assert.Nil(ginkgo.GinkgoT(), err, "listing APISIX upstreams")
 		assert.Len(ginkgo.GinkgoT(), ups, 1)
diff --git a/test/e2e/go.mod b/test/e2e/go.mod
index 65d7d26..3fc8e00 100644
--- a/test/e2e/go.mod
+++ b/test/e2e/go.mod
@@ -4,8 +4,8 @@
 
 require (
 	github.com/apache/apisix-ingress-controller v0.0.0-20210105024109-72e53386de5a
-	github.com/gavv/httpexpect/v2 v2.1.0
-	github.com/gruntwork-io/terratest v0.32.7
+	github.com/gavv/httpexpect/v2 v2.2.0
+	github.com/gruntwork-io/terratest v0.32.8
 	github.com/onsi/ginkgo v1.14.2
 	github.com/stretchr/testify v1.6.1
 	k8s.io/api v0.20.2
diff --git a/test/e2e/go.sum b/test/e2e/go.sum
index 1bb3ed4..a32c64a 100644
--- a/test/e2e/go.sum
+++ b/test/e2e/go.sum
@@ -72,12 +72,16 @@
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
 github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
 github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
+github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
+github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -168,6 +172,8 @@
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 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 v2.2.0 h1:0VwaEBmQaNFHX9x591A8Up+8shCwdF/nF0qlRd/nI48=
+github.com/gavv/httpexpect/v2 v2.2.0/go.mod h1:lnd0TqJLrP+wkJk3SFwtrpSlOAZQ7HaaIFuOYbgqgUM=
 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/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
@@ -203,6 +209,7 @@
 github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -221,6 +228,7 @@
 github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 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=
@@ -297,6 +305,8 @@
 github.com/gruntwork-io/terratest v0.32.3/go.mod h1:PlvB/MeCTUE9gX2Eabx/GAFjfuFL8kR8gfwxxn2B15Q=
 github.com/gruntwork-io/terratest v0.32.7 h1:TJF4ZyOviWknlmzgre48JGGoXa20S8Ng0O/sfBfH+Bw=
 github.com/gruntwork-io/terratest v0.32.7/go.mod h1:0iy7d56CziX3R4ZUn570HMElr4WgBKoKNa8Hzex7bvo=
+github.com/gruntwork-io/terratest v0.32.8 h1:ccIRFH+e6zhSB5difg7baJec4FeOZNXpeIFlZZlKW2M=
+github.com/gruntwork-io/terratest v0.32.8/go.mod h1:FckR+7ks472IJfSKUPfPvnJfSxV1LKGWGMJ9m/LHegE=
 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
@@ -323,6 +333,7 @@
 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=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
@@ -374,6 +385,7 @@
 github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -405,6 +417,7 @@
 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/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -513,6 +526,7 @@
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -543,6 +557,7 @@
 github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo=
+github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
 github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -561,6 +576,7 @@
 github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
 github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
@@ -585,6 +601,7 @@
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 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=
@@ -633,6 +650,7 @@
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -697,6 +715,7 @@
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/test/e2e/ingress/resourcepushing.go b/test/e2e/ingress/resourcepushing.go
index a936c45..7e590e7 100644
--- a/test/e2e/ingress/resourcepushing.go
+++ b/test/e2e/ingress/resourcepushing.go
@@ -26,23 +26,35 @@
 )
 
 var _ = ginkgo.Describe("ApisixRoute Testing", func() {
-	s := scaffold.NewDefaultScaffold()
+
+	opts := &scaffold.Options{
+		Name:                    "default",
+		Kubeconfig:              scaffold.GetKubeconfig(),
+		APISIXConfigPath:        "testdata/apisix-gw-config.yaml",
+		APISIXDefaultConfigPath: "testdata/apisix-gw-config-default.yaml",
+		IngressAPISIXReplicas:   1,
+		HTTPBinServicePort:      80,
+		APISIXRouteVersion:      "apisix.apache.org/v2alpha1",
+	}
+	s := scaffold.NewScaffold(opts)
 	ginkgo.It("create and then scale upstream pods to 2 ", func() {
 		backendSvc, backendSvcPort := s.DefaultHTTPBackend()
 		apisixRoute := fmt.Sprintf(`
-apiVersion: apisix.apache.org/v1
+apiVersion: apisix.apache.org/v2alpha1
 kind: ApisixRoute
 metadata:
   name: httpbin-route
 spec:
-  rules:
-  - host: httpbin.com
-    http:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.com
       paths:
-      - backend:
-          serviceName: %s
-          servicePort: %d
-        path: /ip
+      - /ip
+    backend:
+      serviceName: %s
+      servicePort: %d
 `, backendSvc, backendSvcPort[0])
 		assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(apisixRoute))
 
@@ -64,19 +76,21 @@
 	ginkgo.It("create and then remove", func() {
 		backendSvc, backendSvcPort := s.DefaultHTTPBackend()
 		apisixRoute := fmt.Sprintf(`
-apiVersion: apisix.apache.org/v1
+apiVersion: apisix.apache.org/v2alpha1
 kind: ApisixRoute
 metadata:
   name: httpbin-route
 spec:
-  rules:
-  - host: httpbin.com
-    http:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.com
       paths:
-      - backend:
-          serviceName: %s
-          servicePort: %d
-        path: /ip
+      - /ip
+    backend:
+      serviceName: %s
+      servicePort: %d
 `, backendSvc, backendSvcPort[0])
 
 		assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(apisixRoute), "creating ApisixRoute")
diff --git a/test/e2e/scaffold/ingress.go b/test/e2e/scaffold/ingress.go
index eb7cc91..b3cb70f 100644
--- a/test/e2e/scaffold/ingress.go
+++ b/test/e2e/scaffold/ingress.go
@@ -247,12 +247,14 @@
             - http://apisix-service-e2e-test:9180/apisix/admin
             - --app-namespace
             - %s
+            - --apisix-route-version
+            - %s
       serviceAccount: ingress-apisix-e2e-test-service-account
 `
 )
 
 func (s *Scaffold) newIngressAPISIXController() error {
-	ingressAPISIXDeployment := fmt.Sprintf(_ingressAPISIXDeploymentTemplate, s.opts.IngressAPISIXReplicas, s.namespace)
+	ingressAPISIXDeployment := fmt.Sprintf(_ingressAPISIXDeploymentTemplate, s.opts.IngressAPISIXReplicas, s.namespace, s.opts.APISIXRouteVersion)
 	if err := k8s.CreateServiceAccountE(s.t, s.kubectlOptions, _serviceAccount); err != nil {
 		return err
 	}
diff --git a/test/e2e/scaffold/scaffold.go b/test/e2e/scaffold/scaffold.go
index 5ab61c8..ed2979a 100644
--- a/test/e2e/scaffold/scaffold.go
+++ b/test/e2e/scaffold/scaffold.go
@@ -28,6 +28,7 @@
 	"text/template"
 	"time"
 
+	"github.com/apache/apisix-ingress-controller/pkg/kube"
 	"github.com/gavv/httpexpect/v2"
 	"github.com/gruntwork-io/terratest/modules/k8s"
 	"github.com/gruntwork-io/terratest/modules/testing"
@@ -46,6 +47,7 @@
 	APISIXDefaultConfigPath string
 	IngressAPISIXReplicas   int
 	HTTPBinServicePort      int
+	APISIXRouteVersion      string
 }
 
 type Scaffold struct {
@@ -86,6 +88,9 @@
 
 // NewScaffold creates an e2e test scaffold.
 func NewScaffold(o *Options) *Scaffold {
+	if o.APISIXRouteVersion == "" {
+		o.APISIXRouteVersion = kube.ApisixRouteV1
+	}
 	defer ginkgo.GinkgoRecover()
 
 	s := &Scaffold{
@@ -108,6 +113,7 @@
 		APISIXDefaultConfigPath: "testdata/apisix-gw-config-default.yaml",
 		IngressAPISIXReplicas:   1,
 		HTTPBinServicePort:      80,
+		APISIXRouteVersion:      kube.ApisixRouteV1,
 	}
 	return NewScaffold(opts)
 }