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

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


The following commit(s) were added to refs/heads/master by this push:
     new cf99f1f  chore: integrated api7/seven (#128)
cf99f1f is described below

commit cf99f1f57a4072fc743ce361053171ba9ad95c67
Author: Alex Zhang <[email protected]>
AuthorDate: Fri Dec 25 15:16:37 2020 +0800

    chore: integrated api7/seven (#128)
    
    This commit integrated github.com/api7/seven module, previously the
    release it not so convenient.
    
    What's more, we have the follow changes:
    
    * Apache License V2 header is added in each of file;
    * Apply gofmt;
    * Change the reference from github.com/api7/seven to 
github.com/api7/ingress-controller/pkg/seven
---
 cmd/ingress/ingress.go                             |   2 +-
 cmd/ingress/ingress_test.go                        |   2 +-
 go.mod                                             |  11 +-
 go.sum                                             |   2 -
 pkg/ingress/apisix/annotation.go                   |   6 +-
 pkg/ingress/apisix/plugin.go                       |   3 +-
 pkg/ingress/apisix/route.go                        |   8 +-
 pkg/ingress/apisix/service.go                      |   8 +-
 pkg/ingress/apisix/tls.go                          |   5 +-
 pkg/ingress/apisix/upstream.go                     |   8 +-
 pkg/ingress/controller/apisix_route.go             |   4 +-
 pkg/ingress/controller/apisix_service.go           |   2 +-
 pkg/ingress/controller/apisix_tls.go               |   7 +-
 pkg/ingress/controller/apisix_upstream.go          |   2 +-
 pkg/ingress/controller/endpoint.go                 |   6 +-
 pkg/ingress/controller/watch.go                    |  10 +-
 .../apisix/plugin.go => seven/apisix/client.go}    |  49 ++--
 pkg/seven/apisix/error.go                          |  22 ++
 pkg/seven/apisix/event.go                          |  23 ++
 pkg/seven/apisix/plugins.go                        | 115 +++++++++
 pkg/seven/apisix/route.go                          | 246 +++++++++++++++++++
 pkg/seven/apisix/route_test.go                     | 104 ++++++++
 pkg/seven/apisix/service.go                        | 215 ++++++++++++++++
 pkg/seven/apisix/service_test.go                   |  80 ++++++
 pkg/seven/apisix/ssl.go                            | 139 +++++++++++
 pkg/seven/apisix/ssl_test.go                       |  80 ++++++
 pkg/seven/apisix/upstream.go                       | 269 +++++++++++++++++++++
 pkg/seven/apisix/upstream_test.go                  |  84 +++++++
 pkg/seven/conf/conf.go                             |  36 +++
 pkg/seven/conf/conf_test.go                        |  24 ++
 pkg/seven/db/route.go                              | 110 +++++++++
 pkg/seven/db/service.go                            |  97 ++++++++
 pkg/seven/db/store.go                              |  43 ++++
 pkg/seven/db/upstream.go                           | 103 ++++++++
 pkg/seven/state/builder.go                         | 244 +++++++++++++++++++
 .../plugin.go => seven/state/builder_test.go}      |  52 ++--
 pkg/seven/state/diff.go                            |  15 ++
 .../apisix/plugin.go => seven/state/event.go}      |  48 ++--
 pkg/seven/state/route_worker.go                    |  61 +++++
 pkg/seven/state/service_worker.go                  | 195 +++++++++++++++
 pkg/seven/state/solver.go                          | 129 ++++++++++
 pkg/seven/state/sync.go                            |  15 ++
 pkg/seven/state/upstream_worker.go                 |  23 ++
 .../apisix/plugin.go => seven/utils/diff.go}       |  61 ++---
 pkg/seven/utils/http.go                            |  88 +++++++
 pkg/seven/utils/types.go                           |  15 ++
 46 files changed, 2731 insertions(+), 140 deletions(-)

diff --git a/cmd/ingress/ingress.go b/cmd/ingress/ingress.go
index 5cae8a5..fb41553 100644
--- a/cmd/ingress/ingress.go
+++ b/cmd/ingress/ingress.go
@@ -24,7 +24,6 @@ import (
        "time"
 
        api6Informers 
"github.com/gxthrj/apisix-ingress-types/pkg/client/informers/externalversions"
-       "github.com/gxthrj/seven/conf"
        "github.com/spf13/cobra"
 
        "github.com/api7/ingress-controller/pkg/api"
@@ -32,6 +31,7 @@ import (
        "github.com/api7/ingress-controller/pkg/ingress/controller"
        "github.com/api7/ingress-controller/pkg/kube"
        "github.com/api7/ingress-controller/pkg/log"
+       "github.com/api7/ingress-controller/pkg/seven/conf"
 )
 
 func dief(template string, args ...interface{}) {
diff --git a/cmd/ingress/ingress_test.go b/cmd/ingress/ingress_test.go
index 144f753..108fee1 100644
--- a/cmd/ingress/ingress_test.go
+++ b/cmd/ingress/ingress_test.go
@@ -25,11 +25,11 @@ import (
        "testing"
        "time"
 
-       "github.com/gxthrj/seven/conf"
        "github.com/stretchr/testify/assert"
 
        "github.com/api7/ingress-controller/pkg/config"
        "github.com/api7/ingress-controller/pkg/log"
+       "github.com/api7/ingress-controller/pkg/seven/conf"
        "github.com/api7/ingress-controller/pkg/types"
 )
 
diff --git a/go.mod b/go.mod
index 261896d..f4195b0 100644
--- a/go.mod
+++ b/go.mod
@@ -8,11 +8,20 @@ require (
        github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
        github.com/gxthrj/apisix-ingress-types v0.1.3
        github.com/gxthrj/apisix-types v0.1.3
-       github.com/gxthrj/seven v0.2.7
+       github.com/hashicorp/go-memdb v1.0.4
+       github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // 
indirect
+       github.com/mattn/go-colorable v0.1.4 // indirect
+       github.com/onsi/ginkgo v1.11.0 // indirect
+       github.com/onsi/gomega v1.8.1 // indirect
        github.com/prometheus/client_golang v0.9.3
+       github.com/sergi/go-diff v1.1.0 // indirect
        github.com/spf13/cobra v1.1.1
        github.com/stretchr/testify v1.4.0
+       github.com/yudai/gojsondiff v1.0.0
+       github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
+       github.com/yudai/pp v2.0.1+incompatible // indirect
        go.uber.org/zap v1.13.0
+       gopkg.in/resty.v1 v1.12.0
        gopkg.in/yaml.v2 v2.2.8
        k8s.io/api v0.0.0-20190819141258-3544db3b9e44
        k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d
diff --git a/go.sum b/go.sum
index 32e6a93..096a026 100644
--- a/go.sum
+++ b/go.sum
@@ -112,8 +112,6 @@ github.com/gregjones/httpcache 
v0.0.0-20170728041850-787624de3eb7/go.mod h1:Fecb
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod 
h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod 
h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod 
h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/gxthrj/seven v0.2.7 h1:DNRi3HGXiTEC2O87jq9MqEMHjwf7eHvYQXhJxv1Qa5E=
-github.com/gxthrj/seven v0.2.7/go.mod 
h1:SYs/veqEMdwRF5BL3nf/nxfypoDMO2E6Odgp17m+J9U=
 github.com/hashicorp/consul/api v1.1.0/go.mod 
h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod 
h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/errwrap v1.0.0/go.mod 
h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
diff --git a/pkg/ingress/apisix/annotation.go b/pkg/ingress/apisix/annotation.go
index cead443..0bf0459 100644
--- a/pkg/ingress/apisix/annotation.go
+++ b/pkg/ingress/apisix/annotation.go
@@ -15,9 +15,11 @@
 package apisix
 
 import (
-       apisix "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       seven "github.com/gxthrj/seven/apisix"
        "strconv"
+
+       apisix "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       seven "github.com/api7/ingress-controller/pkg/seven/apisix"
 )
 
 // BuildAnnotation return plugins and group
diff --git a/pkg/ingress/apisix/plugin.go b/pkg/ingress/apisix/plugin.go
index 4feb097..4518324 100644
--- a/pkg/ingress/apisix/plugin.go
+++ b/pkg/ingress/apisix/plugin.go
@@ -15,8 +15,9 @@
 package apisix
 
 import (
-       "github.com/gxthrj/seven/apisix"
        "strconv"
+
+       "github.com/api7/ingress-controller/pkg/seven/apisix"
 )
 
 type CorsYaml struct {
diff --git a/pkg/ingress/apisix/route.go b/pkg/ingress/apisix/route.go
index fe4d0aa..d40734a 100644
--- a/pkg/ingress/apisix/route.go
+++ b/pkg/ingress/apisix/route.go
@@ -15,11 +15,13 @@
 package apisix
 
 import (
-       "github.com/api7/ingress-controller/pkg/ingress/endpoint"
+       "strconv"
+
        ingress "github.com/gxthrj/apisix-ingress-types/pkg/apis/config/v1"
        apisix "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       "github.com/gxthrj/seven/conf"
-       "strconv"
+
+       "github.com/api7/ingress-controller/pkg/ingress/endpoint"
+       "github.com/api7/ingress-controller/pkg/seven/conf"
 )
 
 const (
diff --git a/pkg/ingress/apisix/service.go b/pkg/ingress/apisix/service.go
index b3e00b9..62d11e4 100644
--- a/pkg/ingress/apisix/service.go
+++ b/pkg/ingress/apisix/service.go
@@ -15,11 +15,13 @@
 package apisix
 
 import (
-       "github.com/api7/ingress-controller/pkg/ingress/endpoint"
+       "strconv"
+
        ingress "github.com/gxthrj/apisix-ingress-types/pkg/apis/config/v1"
        apisix "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       "github.com/gxthrj/seven/conf"
-       "strconv"
+
+       "github.com/api7/ingress-controller/pkg/ingress/endpoint"
+       "github.com/api7/ingress-controller/pkg/seven/conf"
 )
 
 const (
diff --git a/pkg/ingress/apisix/tls.go b/pkg/ingress/apisix/tls.go
index ec3888c..10c3201 100644
--- a/pkg/ingress/apisix/tls.go
+++ b/pkg/ingress/apisix/tls.go
@@ -15,12 +15,13 @@
 package apisix
 
 import (
-       ingressConf "github.com/api7/ingress-controller/pkg/kube"
        ingress "github.com/gxthrj/apisix-ingress-types/pkg/apis/config/v1"
        apisix "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       "github.com/gxthrj/seven/conf"
        "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+       ingressConf "github.com/api7/ingress-controller/pkg/kube"
+       "github.com/api7/ingress-controller/pkg/seven/conf"
 )
 
 const (
diff --git a/pkg/ingress/apisix/upstream.go b/pkg/ingress/apisix/upstream.go
index c5c08e6..358ff81 100644
--- a/pkg/ingress/apisix/upstream.go
+++ b/pkg/ingress/apisix/upstream.go
@@ -15,11 +15,13 @@
 package apisix
 
 import (
-       "github.com/api7/ingress-controller/pkg/ingress/endpoint"
+       "strconv"
+
        ingress "github.com/gxthrj/apisix-ingress-types/pkg/apis/config/v1"
        apisix "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       "github.com/gxthrj/seven/conf"
-       "strconv"
+
+       "github.com/api7/ingress-controller/pkg/ingress/endpoint"
+       "github.com/api7/ingress-controller/pkg/seven/conf"
 )
 
 const (
diff --git a/pkg/ingress/controller/apisix_route.go 
b/pkg/ingress/controller/apisix_route.go
index 73d34bf..0759f20 100644
--- a/pkg/ingress/controller/apisix_route.go
+++ b/pkg/ingress/controller/apisix_route.go
@@ -16,14 +16,12 @@ package controller
 
 import (
        "fmt"
-       "github.com/api7/ingress-controller/pkg/ingress/apisix"
        api6V1 "github.com/gxthrj/apisix-ingress-types/pkg/apis/config/v1"
        clientSet 
"github.com/gxthrj/apisix-ingress-types/pkg/client/clientset/versioned"
        api6Scheme 
"github.com/gxthrj/apisix-ingress-types/pkg/client/clientset/versioned/scheme"
        api6Informers 
"github.com/gxthrj/apisix-ingress-types/pkg/client/informers/externalversions/config/v1"
        "github.com/gxthrj/apisix-ingress-types/pkg/client/listers/config/v1"
        apisixV1 "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       "github.com/gxthrj/seven/state"
        "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/util/runtime"
        "k8s.io/apimachinery/pkg/util/wait"
@@ -33,7 +31,9 @@ import (
        "k8s.io/client-go/util/workqueue"
        "time"
 
+       "github.com/api7/ingress-controller/pkg/ingress/apisix"
        "github.com/api7/ingress-controller/pkg/log"
+       "github.com/api7/ingress-controller/pkg/seven/state"
 )
 
 type ApisixRouteController struct {
diff --git a/pkg/ingress/controller/apisix_service.go 
b/pkg/ingress/controller/apisix_service.go
index a6b2005..474f5de 100644
--- a/pkg/ingress/controller/apisix_service.go
+++ b/pkg/ingress/controller/apisix_service.go
@@ -23,7 +23,6 @@ import (
        apisixScheme 
"github.com/gxthrj/apisix-ingress-types/pkg/client/clientset/versioned/scheme"
        informers 
"github.com/gxthrj/apisix-ingress-types/pkg/client/informers/externalversions/config/v1"
        "github.com/gxthrj/apisix-ingress-types/pkg/client/listers/config/v1"
-       "github.com/gxthrj/seven/state"
        "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/util/runtime"
        "k8s.io/apimachinery/pkg/util/wait"
@@ -34,6 +33,7 @@ import (
 
        "github.com/api7/ingress-controller/pkg/ingress/apisix"
        "github.com/api7/ingress-controller/pkg/log"
+       "github.com/api7/ingress-controller/pkg/seven/state"
 )
 
 type ApisixServiceController struct {
diff --git a/pkg/ingress/controller/apisix_tls.go 
b/pkg/ingress/controller/apisix_tls.go
index 4efeb05..0510d14 100644
--- a/pkg/ingress/controller/apisix_tls.go
+++ b/pkg/ingress/controller/apisix_tls.go
@@ -18,14 +18,11 @@ import (
        "fmt"
        "time"
 
-       "github.com/api7/ingress-controller/pkg/ingress/apisix"
-       "github.com/api7/ingress-controller/pkg/log"
        apisixV1 "github.com/gxthrj/apisix-ingress-types/pkg/apis/config/v1"
        clientSet 
"github.com/gxthrj/apisix-ingress-types/pkg/client/clientset/versioned"
        apisixScheme 
"github.com/gxthrj/apisix-ingress-types/pkg/client/clientset/versioned/scheme"
        informers 
"github.com/gxthrj/apisix-ingress-types/pkg/client/informers/externalversions/config/v1"
        "github.com/gxthrj/apisix-ingress-types/pkg/client/listers/config/v1"
-       "github.com/gxthrj/seven/state"
        "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/util/runtime"
        "k8s.io/apimachinery/pkg/util/wait"
@@ -33,6 +30,10 @@ import (
        "k8s.io/client-go/kubernetes/scheme"
        "k8s.io/client-go/tools/cache"
        "k8s.io/client-go/util/workqueue"
+
+       "github.com/api7/ingress-controller/pkg/ingress/apisix"
+       "github.com/api7/ingress-controller/pkg/log"
+       "github.com/api7/ingress-controller/pkg/seven/state"
 )
 
 type ApisixTlsController struct {
diff --git a/pkg/ingress/controller/apisix_upstream.go 
b/pkg/ingress/controller/apisix_upstream.go
index 092f63e..bc08e82 100644
--- a/pkg/ingress/controller/apisix_upstream.go
+++ b/pkg/ingress/controller/apisix_upstream.go
@@ -23,7 +23,6 @@ import (
        apisixScheme 
"github.com/gxthrj/apisix-ingress-types/pkg/client/clientset/versioned/scheme"
        informers 
"github.com/gxthrj/apisix-ingress-types/pkg/client/informers/externalversions/config/v1"
        "github.com/gxthrj/apisix-ingress-types/pkg/client/listers/config/v1"
-       "github.com/gxthrj/seven/state"
        "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/util/runtime"
        "k8s.io/apimachinery/pkg/util/wait"
@@ -35,6 +34,7 @@ import (
        "github.com/api7/ingress-controller/pkg/ingress/apisix"
        "github.com/api7/ingress-controller/pkg/ingress/endpoint"
        "github.com/api7/ingress-controller/pkg/log"
+       "github.com/api7/ingress-controller/pkg/seven/state"
 )
 
 type ApisixUpstreamController struct {
diff --git a/pkg/ingress/controller/endpoint.go 
b/pkg/ingress/controller/endpoint.go
index 7c42a22..b21cfbf 100644
--- a/pkg/ingress/controller/endpoint.go
+++ b/pkg/ingress/controller/endpoint.go
@@ -18,9 +18,6 @@ import (
        "fmt"
        "github.com/golang/glog"
        apisixType "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       "github.com/gxthrj/seven/apisix"
-       sevenConf "github.com/gxthrj/seven/conf"
-       "github.com/gxthrj/seven/state"
        CoreV1 "k8s.io/api/core/v1"
        "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/util/runtime"
@@ -34,6 +31,9 @@ import (
 
        "github.com/api7/ingress-controller/pkg/kube"
        "github.com/api7/ingress-controller/pkg/log"
+       "github.com/api7/ingress-controller/pkg/seven/apisix"
+       sevenConf "github.com/api7/ingress-controller/pkg/seven/conf"
+       "github.com/api7/ingress-controller/pkg/seven/state"
 )
 
 type EndpointController struct {
diff --git a/pkg/ingress/controller/watch.go b/pkg/ingress/controller/watch.go
index ce4a2ca..6738e19 100644
--- a/pkg/ingress/controller/watch.go
+++ b/pkg/ingress/controller/watch.go
@@ -15,14 +15,16 @@
 package controller
 
 import (
+       "strconv"
+
        "github.com/api7/ingress-controller/pkg/kube"
        "github.com/golang/glog"
        apisixType "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
-       "github.com/gxthrj/seven/apisix"
-       sevenConf "github.com/gxthrj/seven/conf"
-       "github.com/gxthrj/seven/state"
        "k8s.io/api/core/v1"
-       "strconv"
+
+       "github.com/api7/ingress-controller/pkg/seven/apisix"
+       sevenConf "github.com/api7/ingress-controller/pkg/seven/conf"
+       "github.com/api7/ingress-controller/pkg/seven/state"
 )
 
 const (
diff --git a/pkg/ingress/apisix/plugin.go b/pkg/seven/apisix/client.go
similarity index 50%
copy from pkg/ingress/apisix/plugin.go
copy to pkg/seven/apisix/client.go
index 4feb097..fcb2466 100644
--- a/pkg/ingress/apisix/plugin.go
+++ b/pkg/seven/apisix/client.go
@@ -15,37 +15,28 @@
 package apisix
 
 import (
-       "github.com/gxthrj/seven/apisix"
-       "strconv"
+       "fmt"
+       "net/http"
+       "time"
+
+       "gopkg.in/resty.v1"
 )
 
-type CorsYaml struct {
-       Enable       bool   `json:"enable,omitempty"`
-       AllowOrigin  string `json:"allow_origin,omitempty"`
-       AllowHeaders string `json:"allow_headers,omitempty"`
-       AllowMethods string `json:"allow_methods,omitempty"`
-}
+const (
+       timeout = 3000
+)
 
-func (c *CorsYaml) SetEnable(enable string) {
-       if b, err := strconv.ParseBool(enable); err != nil {
-               c.Enable = false
-       } else {
-               c.Enable = b
+func Get(url string) ([]byte, error) {
+       r := resty.New().
+               SetTimeout(time.Duration(timeout)*time.Millisecond).
+               R().
+               SetHeader("content-type", "application/json")
+       resp, err := r.Get(url)
+       if err != nil {
+               return nil, err
        }
-}
-
-func (c *CorsYaml) SetOrigin(origin string) {
-       c.AllowOrigin = origin
-}
-
-func (c *CorsYaml) SetHeaders(headers string) {
-       c.AllowHeaders = headers
-}
-func (c *CorsYaml) SetMethods(methods string) {
-       c.AllowMethods = methods
-}
-
-func (c *CorsYaml) Build() *apisix.Cors {
-       maxAge := int64(3600)
-       return apisix.BuildCors(c.Enable, &c.AllowOrigin, &c.AllowHeaders, 
&c.AllowMethods, &maxAge)
+       if resp.StatusCode() != http.StatusOK {
+               return nil, fmt.Errorf("status: %d, body: %s", 
resp.StatusCode(), resp.Body())
+       }
+       return resp.Body(), nil
 }
diff --git a/pkg/seven/apisix/error.go b/pkg/seven/apisix/error.go
new file mode 100644
index 0000000..d832854
--- /dev/null
+++ b/pkg/seven/apisix/error.go
@@ -0,0 +1,22 @@
+// 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 apisix
+
+type ErrorResponse struct {
+       Cause     string `json:"cause"`
+       Index     int64  `json:"index"`
+       ErrorCode int64  `json:"errorCode"`
+       Message   string `json:"message"`
+}
diff --git a/pkg/seven/apisix/event.go b/pkg/seven/apisix/event.go
new file mode 100644
index 0000000..64bd3d9
--- /dev/null
+++ b/pkg/seven/apisix/event.go
@@ -0,0 +1,23 @@
+// 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 apisix
+
+// define event for workflow
+
+type Event struct {
+       Method string               // ADD UPDATE DELETE
+       Kind   string               // route service upstream
+       Func   func(...interface{}) // callback
+}
diff --git a/pkg/seven/apisix/plugins.go b/pkg/seven/apisix/plugins.go
new file mode 100644
index 0000000..f8eefc8
--- /dev/null
+++ b/pkg/seven/apisix/plugins.go
@@ -0,0 +1,115 @@
+// 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 apisix
+
+import "strings"
+
+// ip-restrictio
+type IpRestriction struct {
+       Whitelist []string `json:"whitelist,omitempty"`
+       Blacklist []string `json:"blacklist,omitempty"`
+}
+
+// Convert2IpRestriction build IpRestriction
+func BuildIpRestriction(whites, blacks *string) *IpRestriction {
+       result := &IpRestriction{}
+       if whites != nil {
+               whiteIps := strings.Split(*whites, ",")
+               result.Whitelist = whiteIps
+       }
+       if blacks != nil {
+               blackIps := strings.Split(*blacks, ",")
+               result.Blacklist = blackIps
+       }
+       return result
+}
+
+// Cors
+type Cors struct {
+       Origins []string `json:"origins,omitempty"`
+       Headers []string `json:"headers,omitempty"`
+       Methods []string `json:"methods,omitempty"`
+       MaxAge  int64    `json:"max_age,omitempty"`
+}
+
+// BuildCors
+func BuildCors(enable bool, originStr, headerStr, methodStr *string, maxAge 
*int64) *Cors {
+       result := &Cors{}
+       if enable {
+               if originStr != nil {
+                       origins := strings.Split(*originStr, ",")
+                       result.Origins = origins
+               }
+               if headerStr != nil {
+                       headers := strings.Split(*headerStr, ",")
+                       result.Headers = headers
+               }
+               if methodStr != nil {
+                       methods := strings.Split(*methodStr, ",")
+                       result.Methods = methods
+               }
+               if maxAge != nil {
+                       result.MaxAge = *maxAge
+               }
+               return result
+       } else {
+               return nil
+       }
+}
+
+// routex
+type Routex struct {
+       Rules []Rule `json:"rules,inline"`
+}
+
+type Rule struct {
+       Priority int64   `json:"priority,omitempty"`
+       Upstream string  `json:"upstream"`
+       Desc     string  `json:"desc"`
+       Matches  []Match `json:"matchs,omitempty"`
+}
+
+type Match struct {
+       Host   string   `json:"host,omitempty"`
+       Uri    string   `json:"uri,omitempty"`
+       Use    string   `json:"use"`
+       Key    string   `json:"key"`
+       Values []string `json:"values,omitempty"`
+}
+
+// BuildRoutex
+func BuildRoutex(enable bool, rules []Rule) *Routex {
+       if enable {
+               result := &Routex{Rules: rules}
+               return result
+       } else {
+               return nil
+       }
+}
+
+// token
+type Token struct {
+       IgnoreUri []string `json:"ignore_uri,omitempty"`
+}
+
+// BuildToken
+func BuildToken(enable bool, ignoreUris []string) *Token {
+       if enable {
+               result := &Token{IgnoreUri: ignoreUris}
+               return result
+       } else {
+               return nil
+       }
+}
diff --git a/pkg/seven/apisix/route.go b/pkg/seven/apisix/route.go
new file mode 100644
index 0000000..3ad709f
--- /dev/null
+++ b/pkg/seven/apisix/route.go
@@ -0,0 +1,246 @@
+// 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 apisix
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "strings"
+
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       "github.com/api7/ingress-controller/pkg/seven/conf"
+       sevendb "github.com/api7/ingress-controller/pkg/seven/db"
+       "github.com/api7/ingress-controller/pkg/seven/utils"
+)
+
+// FindCurrentRoute find current route in memDB
+func FindCurrentRoute(route *v1.Route) (*v1.Route, error) {
+       db := &sevendb.RouteRequest{Group: *route.Group, Name: *route.Name, 
FullName: *route.FullName}
+       currentRoute, _ := db.FindByName()
+       if currentRoute != nil {
+               return currentRoute, nil
+       } else {
+               // find from apisix
+               if routes, err := ListRoute(*route.Group); err != nil {
+                       return nil, fmt.Errorf("list routes from etcd failed, 
err: %+v", err)
+               } else {
+                       for _, r := range routes {
+                               if r.Name != nil && *r.Name == *route.Name {
+                                       // insert to memDB
+                                       db := &sevendb.RouteDB{Routes: 
[]*v1.Route{r}}
+                                       db.Insert()
+                                       // return
+                                       return r, nil
+                               }
+                       }
+               }
+
+       }
+       return nil, fmt.Errorf("NOT FOUND")
+}
+
+// ListRoute list route from etcd , convert to v1.Route
+func ListRoute(group string) ([]*v1.Route, error) {
+       baseUrl := conf.FindUrl(group)
+       url := baseUrl + "/routes"
+       ret, err := Get(url)
+       if err != nil {
+               return nil, fmt.Errorf("http get failed, url: %s, err: %+v", 
url, err)
+       }
+       var routesResponse RoutesResponse
+       if err := json.Unmarshal(ret, &routesResponse); err != nil {
+               return nil, fmt.Errorf("json unmarshal failed, err: %+v", err)
+       } else {
+               routes := make([]*v1.Route, 0)
+               for _, u := range routesResponse.Routes.Routes {
+                       if n, err := u.convert(group); err == nil {
+                               routes = append(routes, n)
+                       } else {
+                               return nil, fmt.Errorf("upstream: %s 转换失败, %s", 
*u.Value.Desc, err.Error())
+                       }
+               }
+               return routes, nil
+       }
+}
+
+func AddRoute(route *v1.Route) (*RouteResponse, error) {
+       baseUrl := conf.FindUrl(*route.Group)
+       url := fmt.Sprintf("%s/routes", baseUrl)
+       rr := convert2RouteRequest(route)
+       if b, err := json.Marshal(rr); err != nil {
+               return nil, err
+       } else {
+               if res, err := utils.Post(url, b); err != nil {
+                       return nil, err
+               } else {
+                       var routeResp RouteResponse
+                       if err = json.Unmarshal(res, &routeResp); err != nil {
+                               return nil, err
+                       } else {
+                               if routeResp.Route.Key != nil {
+                                       return &routeResp, nil
+                               } else {
+                                       return nil, fmt.Errorf("apisix route 
not expected response")
+                               }
+
+                       }
+               }
+       }
+}
+
+func UpdateRoute(route *v1.Route) error {
+       baseUrl := conf.FindUrl(*route.Group)
+       url := fmt.Sprintf("%s/routes/%s", baseUrl, *route.ID)
+       rr := convert2RouteRequest(route)
+       if b, err := json.Marshal(rr); err != nil {
+               return err
+       } else {
+               if _, err := utils.Patch(url, b); err != nil {
+                       return err
+               } else {
+                       return nil
+               }
+       }
+}
+
+func DeleteRoute(route *v1.Route) error {
+       baseUrl := conf.FindUrl(*route.Group)
+       url := fmt.Sprintf("%s/routes/%s", baseUrl, *route.ID)
+       if _, err := utils.Delete(url); err != nil {
+               return err
+       } else {
+               return nil
+       }
+}
+
+type Redirect struct {
+       RetCode int64  `json:"ret_code"`
+       Uri     string `json:"uri"`
+}
+
+func convert2RouteRequest(route *v1.Route) *RouteRequest {
+       return &RouteRequest{
+               Desc:      *route.Name,
+               Host:      *route.Host,
+               Uri:       *route.Path,
+               ServiceId: *route.ServiceId,
+               Plugins:   route.Plugins,
+       }
+}
+
+// convert apisix RouteResponse -> apisix-types v1.Route
+func (r *Route) convert(group string) (*v1.Route, error) {
+       // id
+       key := r.Key
+       ks := strings.Split(*key, "/")
+       id := ks[len(ks)-1]
+       // name
+       name := r.Value.Desc
+       // host
+       host := r.Value.Host
+       // path
+       path := r.Value.Uri
+       // method
+       methods := r.Value.Methods
+       // upstreamId
+       upstreamId := r.Value.UpstreamId
+       // serviceId
+       serviceId := r.Value.ServiceId
+       // plugins
+       var plugins v1.Plugins
+       plugins = r.Value.Plugins
+
+       // fullName
+       fullName := "unknown"
+       if name != nil {
+               fullName = *name
+       }
+       if group != "" {
+               fullName = group + "_" + fullName
+       }
+
+       return &v1.Route{
+               ID:         &id,
+               Group:      &group,
+               FullName:   &fullName,
+               Name:       name,
+               Host:       host,
+               Path:       path,
+               Methods:    methods,
+               UpstreamId: upstreamId,
+               ServiceId:  serviceId,
+               Plugins:    &plugins,
+       }, nil
+}
+
+type RoutesResponse struct {
+       Routes Routes `json:"node"`
+}
+
+type Routes struct {
+       Key    string   `json:"key"`
+       Routes RouteSet `json:"nodes"`
+}
+
+type RouteSet []Route
+
+// RouteSet.UnmarshalJSON implements json.Unmarshaler interface.
+// lua-cjson doesn't distinguish empty array and table,
+// and by default empty array will be encoded as '{}'.
+// We have to maintain the compatibility.
+func (set *RouteSet) UnmarshalJSON(p []byte) error {
+       if p[0] == '{' {
+               if len(p) != 2 {
+                       return errors.New("unexpected non-empty object")
+               }
+               return nil
+       }
+       var route []Route
+       if err := json.Unmarshal(p, &route); err != nil {
+               return err
+       }
+       *set = route
+       return nil
+}
+
+type RouteResponse struct {
+       Action string `json:"action"`
+       Route  Route  `json:"node"`
+}
+
+type Route struct {
+       Key   *string `json:"key"`   // route key
+       Value Value   `json:"value"` // route content
+}
+
+type Value struct {
+       UpstreamId *string                `json:"upstream_id"`
+       ServiceId  *string                `json:"service_id"`
+       Plugins    map[string]interface{} `json:"plugins"`
+       Host       *string                `json:"host,omitempty"`
+       Uri        *string                `json:"uri"`
+       Desc       *string                `json:"desc"`
+       Methods    []*string              `json:"methods,omitempty"`
+}
+
+type RouteRequest struct {
+       Desc      string      `json:"desc,omitempty"`
+       Uri       string      `json:"uri,omitempty"`
+       Host      string      `json:"host,omitempty"`
+       ServiceId string      `json:"service_id,omitempty"`
+       Plugins   *v1.Plugins `json:"plugins,omitempty"`
+}
diff --git a/pkg/seven/apisix/route_test.go b/pkg/seven/apisix/route_test.go
new file mode 100644
index 0000000..9fba34e
--- /dev/null
+++ b/pkg/seven/apisix/route_test.go
@@ -0,0 +1,104 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package apisix
+
+import (
+       "encoding/json"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestRouteUnmarshalJSON(t *testing.T) {
+       var route Routes
+       emptyData := `
+{
+       "key": "test",
+       "nodes": {}
+}
+`
+       err := json.Unmarshal([]byte(emptyData), &route)
+       assert.Nil(t, err)
+
+       emptyData = `
+{
+       "key": "test",
+       "nodes": {"a": "b", "c": "d"}
+}
+`
+       err = json.Unmarshal([]byte(emptyData), &route)
+       assert.Equal(t, err.Error(), "unexpected non-empty object")
+
+       emptyArray := `
+{
+       "key": "test",
+       "nodes": []
+}
+`
+       err = json.Unmarshal([]byte(emptyArray), &route)
+       assert.Nil(t, err)
+
+       normalData := `
+{
+       "key": "test",
+       "nodes": [
+               {
+                       "key": "route 1",
+                       "value": {
+                               "desc": "test route 1",
+                               "upstream_id": "123",
+                               "service_id": "12345",
+                               "host": "foo.com",
+                               "uri": "/bar/baz",
+                               "methods": ["GET", "POST"]
+                       }
+               }
+       ]
+}
+`
+       err = json.Unmarshal([]byte(normalData), &route)
+       assert.Nil(t, err)
+       assert.Equal(t, route.Key, "test")
+       assert.Equal(t, len(route.Routes), 1)
+
+       key := *route.Routes[0].Key
+       assert.Equal(t, key, "route 1")
+       desc := *route.Routes[0].Value.Desc
+       assert.Equal(t, desc, "test route 1")
+       upstreamId := *route.Routes[0].Value.UpstreamId
+       assert.Equal(t, upstreamId, "123")
+       svcId := *route.Routes[0].Value.ServiceId
+       assert.Equal(t, svcId, "12345")
+       assert.Equal(t, *route.Routes[0].Value.Host, "foo.com")
+       assert.Equal(t, *route.Routes[0].Value.Uri, "/bar/baz")
+       assert.Equal(t, *route.Routes[0].Value.Methods[0], "GET")
+       assert.Equal(t, *route.Routes[0].Value.Methods[1], "POST")
+}
+
+func TestRouteConvertWithoutDesc(t *testing.T) {
+       upsId := "1"
+       svcId := "2"
+       key := "foo/bar"
+       r := &Route{
+               Key: &key,
+               Value: Value{
+                       UpstreamId: &upsId,
+                       ServiceId:  &svcId,
+                       Host:       nil,
+               },
+       }
+       _, err := r.convert("mygroup")
+       assert.Nil(t, err)
+}
diff --git a/pkg/seven/apisix/service.go b/pkg/seven/apisix/service.go
new file mode 100644
index 0000000..a036e8e
--- /dev/null
+++ b/pkg/seven/apisix/service.go
@@ -0,0 +1,215 @@
+// 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 apisix
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "strings"
+
+       "github.com/golang/glog"
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       "github.com/api7/ingress-controller/pkg/seven/conf"
+       sevendb "github.com/api7/ingress-controller/pkg/seven/db"
+       "github.com/api7/ingress-controller/pkg/seven/utils"
+)
+
+// FindCurrentService find service from memDB,
+// if Not Found, find service from apisix
+func FindCurrentService(group, name, fullName string) (*v1.Service, error) {
+       db := sevendb.ServiceRequest{Group: group, Name: name, FullName: 
fullName}
+       currentService, _ := db.FindByName()
+       if currentService != nil {
+               return currentService, nil
+       } else {
+               // find service from apisix
+               if services, err := ListService(group); err != nil {
+                       glog.Errorf("list services in etcd failed, group: %s, 
err: %+v", group, err)
+                       return nil, fmt.Errorf("list services failed, err: 
%+v", err)
+               } else {
+                       for _, s := range services {
+                               if s.Name != nil && *(s.Name) == name {
+                                       // and save to memDB
+                                       db := &sevendb.ServiceDB{Services: 
[]*v1.Service{s}}
+                                       db.Insert()
+                                       // return
+                                       return s, nil
+                               }
+                       }
+               }
+       }
+       return nil, nil
+}
+
+// ListUpstream list upstream from etcd , convert to v1.Upstream
+func ListService(group string) ([]*v1.Service, error) {
+       baseUrl := conf.FindUrl(group)
+       url := baseUrl + "/services"
+       ret, err := Get(url)
+       if err != nil {
+               return nil, fmt.Errorf("http get failed, url: %s, err: %+v", 
url, err)
+       }
+       var servicesResponse ServicesResponse
+       if err := json.Unmarshal(ret, &servicesResponse); err != nil {
+               return nil, fmt.Errorf("json unmarshal failed, err: %+v", err)
+       } else {
+               result := make([]*v1.Service, 0)
+               for _, u := range servicesResponse.Services.Services {
+                       if n, err := u.convert(group); err == nil {
+                               result = append(result, n)
+                       } else {
+                               return nil, fmt.Errorf("service : %+v 转换失败, 
%s", u.ServiceValue, err.Error())
+                       }
+               }
+               return result, nil
+       }
+}
+
+// convert convert Service from etcd to v1.Service
+func (u *Service) convert(group string) (*v1.Service, error) {
+       // id
+       keys := strings.Split(*u.Key, "/")
+       id := keys[len(keys)-1]
+       // Name
+       name := u.ServiceValue.Desc
+       // upstreamId
+       upstreamId := u.ServiceValue.UpstreamId
+       // plugins
+       plugins := &v1.Plugins{}
+       for k, v := range u.ServiceValue.Plugins {
+               (*plugins)[k] = v
+       }
+       fullName := *name
+       if group != "" {
+               fullName = group + "_" + *name
+       }
+       return &v1.Service{ID: &id, FullName: &fullName, Group: &group, Name: 
name, UpstreamId: upstreamId, Plugins: plugins}, nil
+}
+
+func AddService(service *v1.Service) (*ServiceResponse, error) {
+       baseUrl := conf.FindUrl(*service.Group)
+       url := fmt.Sprintf("%s/services", baseUrl)
+       ur := convert2ServiceRequest(service)
+       if b, err := json.Marshal(ur); err != nil {
+               return nil, err
+       } else {
+               if res, err := utils.Post(url, b); err != nil {
+                       return nil, fmt.Errorf("http post failed, err: %+v", 
err)
+               } else {
+                       var uRes ServiceResponse
+                       if err = json.Unmarshal(res, &uRes); err != nil {
+                               return nil, err
+                       } else {
+                               if uRes.Service.Key != nil {
+                                       return &uRes, nil
+                               } else {
+                                       return nil, fmt.Errorf("apisix service 
not expected response")
+                               }
+
+                       }
+               }
+       }
+}
+
+func UpdateService(service *v1.Service) (*ServiceResponse, error) {
+       baseUrl := conf.FindUrl(*service.Group)
+       url := fmt.Sprintf("%s/services/%s", baseUrl, *service.ID)
+       ur := convert2ServiceRequest(service)
+       if b, err := json.Marshal(ur); err != nil {
+               return nil, err
+       } else {
+               if res, err := utils.Patch(url, b); err != nil {
+                       return nil, err
+               } else {
+                       var uRes ServiceResponse
+                       if err = json.Unmarshal(res, &uRes); err != nil {
+                               return nil, err
+                       } else {
+                               if uRes.Service.Key != nil {
+                                       return &uRes, nil
+                               } else {
+                                       var errResp ErrorResponse
+                                       json.Unmarshal(res, &errResp)
+                                       glog.Error(errResp.Message)
+                                       return nil, fmt.Errorf("apisix service 
not expected response %s", errResp.Message)
+                               }
+                       }
+               }
+       }
+}
+
+func convert2ServiceRequest(service *v1.Service) *ServiceRequest {
+       request := &ServiceRequest{
+               Desc:       service.Name,
+               UpstreamId: service.UpstreamId,
+               Plugins:    service.Plugins,
+       }
+       glog.V(2).Info(*request.Desc)
+       return request
+}
+
+type ServiceRequest struct {
+       Desc       *string     `json:"desc,omitempty"`
+       UpstreamId *string     `json:"upstream_id"`
+       Plugins    *v1.Plugins `json:"plugins,omitempty"`
+}
+
+type ServicesResponse struct {
+       Services Services `json:"node"`
+}
+
+type Services struct {
+       Key      string     `json:"key"` // 用来定位upstreams 列表
+       Services ServiceSet `json:"nodes"`
+}
+
+type ServiceSet []Service
+
+// UpstreamSet.UnmarshalJSON implements json.Unmarshaler interface.
+// lua-cjson doesn't distinguish empty array and table,
+// and by default empty array will be encoded as '{}'.
+// We have to maintain the compatibility.
+func (set *ServiceSet) UnmarshalJSON(p []byte) error {
+       if p[0] == '{' {
+               if len(p) != 2 {
+                       return errors.New("unexpected non-empty object")
+               }
+               return nil
+       }
+       var svcs []Service
+       if err := json.Unmarshal(p, &svcs); err != nil {
+               return err
+       }
+       *set = svcs
+       return nil
+}
+
+type ServiceResponse struct {
+       Action  string  `json:"action"`
+       Service Service `json:"node"`
+}
+
+type Service struct {
+       Key          *string      `json:"key"` // service key
+       ServiceValue ServiceValue `json:"value,omitempty"`
+}
+
+type ServiceValue struct {
+       UpstreamId *string                `json:"upstream_id,omitempty"`
+       Plugins    map[string]interface{} `json:"plugins"`
+       Desc       *string                `json:"desc,omitempty"`
+}
diff --git a/pkg/seven/apisix/service_test.go b/pkg/seven/apisix/service_test.go
new file mode 100644
index 0000000..4309e4d
--- /dev/null
+++ b/pkg/seven/apisix/service_test.go
@@ -0,0 +1,80 @@
+// 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 apisix
+
+import (
+       "encoding/json"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestServiceUnmarshalJSON(t *testing.T) {
+       var svc Services
+       emptyData := `
+{
+       "key": "test",
+       "nodes": {}
+}
+`
+       err := json.Unmarshal([]byte(emptyData), &svc)
+       assert.Nil(t, err)
+
+       emptyData = `
+{
+       "key": "test",
+       "nodes": {"a": "b", "c": "d"}
+}
+`
+       err = json.Unmarshal([]byte(emptyData), &svc)
+       assert.Equal(t, err.Error(), "unexpected non-empty object")
+
+       emptyArray := `
+{
+       "key": "test",
+       "nodes": []
+}
+`
+       err = json.Unmarshal([]byte(emptyArray), &svc)
+       assert.Nil(t, err)
+
+       normalData := `
+{
+       "key": "test",
+       "nodes": [
+               {
+                       "key": "svc1",
+                       "value": {
+                               "desc": "test service 1",
+                               "upstream_id": "123",
+                               "plugins": {}
+                       }
+               }
+       ]
+}
+`
+       err = json.Unmarshal([]byte(normalData), &svc)
+       assert.Nil(t, err)
+       assert.Equal(t, svc.Key, "test")
+       assert.Equal(t, len(svc.Services), 1)
+
+       key := *svc.Services[0].Key
+       assert.Equal(t, key, "svc1")
+       desc := *svc.Services[0].ServiceValue.Desc
+       assert.Equal(t, desc, "test service 1")
+
+       upstreamId := *svc.Services[0].ServiceValue.UpstreamId
+       assert.Equal(t, upstreamId, "123")
+}
diff --git a/pkg/seven/apisix/ssl.go b/pkg/seven/apisix/ssl.go
new file mode 100644
index 0000000..4933f80
--- /dev/null
+++ b/pkg/seven/apisix/ssl.go
@@ -0,0 +1,139 @@
+// 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 apisix
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "strings"
+
+       "github.com/golang/glog"
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       "github.com/api7/ingress-controller/pkg/seven/conf"
+       "github.com/api7/ingress-controller/pkg/seven/utils"
+)
+
+// ListSsl list ssl from etcd , convert to v1.Upstream
+func ListSsl(group string) ([]*v1.Ssl, error) {
+       baseUrl := conf.FindUrl(group)
+       url := baseUrl + "/ssl"
+       ret, err := Get(url)
+       if err != nil {
+               return nil, fmt.Errorf("http get failed, url: %s, err: %+v", 
url, err)
+       }
+       var sslsResponse SslsResponse
+       if err := json.Unmarshal(ret, &sslsResponse); err != nil {
+               return nil, fmt.Errorf("json transform error")
+       } else {
+               ssls := make([]*v1.Ssl, 0)
+               for _, s := range sslsResponse.SslList.SslNodes {
+                       id := strings.ReplaceAll(*s.Key, "/apisix/ssl/", "")
+                       ssl := &v1.Ssl{
+                               ID:     &id,
+                               Snis:   s.Ssl.Snis,
+                               Cert:   s.Ssl.Cert,
+                               Key:    s.Ssl.Key,
+                               Status: s.Ssl.Status,
+                               Group:  &group,
+                       }
+                       ssls = append(ssls, ssl)
+               }
+               return ssls, nil
+       }
+}
+
+func AddOrUpdateSsl(ssl *v1.Ssl) (*SslResponse, error) {
+       baseUrl := conf.FindUrl(*ssl.Group)
+       url := fmt.Sprintf("%s/ssl/%s", baseUrl, *ssl.ID)
+       glog.V(2).Info(url)
+       ur := &v1.Ssl{
+               Snis:   ssl.Snis,
+               Cert:   ssl.Cert,
+               Key:    ssl.Key,
+               Status: ssl.Status,
+       }
+       if b, err := json.Marshal(ur); err != nil {
+               return nil, err
+       } else {
+               if res, err := utils.Put(url, b); err != nil {
+                       return nil, fmt.Errorf("http put failed, url: %s, err: 
%+v", url, err)
+               } else {
+                       var uRes SslResponse
+                       if err = json.Unmarshal(res, &uRes); err != nil {
+                               glog.Errorf("json Unmarshal error: %s", 
err.Error())
+                               return nil, err
+                       } else {
+                               glog.V(2).Info(uRes)
+                               if uRes.Ssl.Key != nil {
+                                       return &uRes, nil
+                               } else {
+                                       return nil, fmt.Errorf("apisix ssl not 
expected response")
+                               }
+                       }
+               }
+       }
+}
+
+func DeleteSsl(ssl *v1.Ssl) error {
+       baseUrl := conf.FindUrl(*ssl.Group)
+       url := fmt.Sprintf("%s/ssl/%s", baseUrl, *ssl.ID)
+       if _, err := utils.Delete(url); err != nil {
+               return fmt.Errorf("http delete failed, url: %s, err: %+v", url, 
err)
+       } else {
+               return nil
+       }
+}
+
+type SslResponse struct {
+       Action string  `json:"action"`
+       Ssl    SslNode `json:"node"`
+}
+
+type SslsResponse struct {
+       Action  string  `json:"action"`
+       SslList SslList `json:"node"`
+}
+
+type SslList struct {
+       SslNodes SslSet `json:"nodes"`
+}
+
+type SslNode struct {
+       Key *string `json:"key"`
+       Ssl *v1.Ssl `json:"value"`
+}
+
+type SslSet []SslNode
+
+// SslSet.UnmarshalJSON implements json.Unmarshaler interface.
+// lua-cjson doesn't distinguish empty array and table,
+// and by default empty array will be encoded as '{}'.
+// We have to maintain the compatibility.
+func (set *SslSet) UnmarshalJSON(p []byte) error {
+       if p[0] == '{' {
+               if len(p) != 2 {
+                       return errors.New("unexpected non-empty object")
+               }
+               return nil
+       }
+       var ssls []SslNode
+       if err := json.Unmarshal(p, &ssls); err != nil {
+               return err
+       }
+       *set = ssls
+       return nil
+}
diff --git a/pkg/seven/apisix/ssl_test.go b/pkg/seven/apisix/ssl_test.go
new file mode 100644
index 0000000..dbb8d30
--- /dev/null
+++ b/pkg/seven/apisix/ssl_test.go
@@ -0,0 +1,80 @@
+// 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 apisix
+
+import (
+       "encoding/json"
+       "github.com/stretchr/testify/assert"
+       "testing"
+)
+
+func TestSslUnmarshalJSON(t *testing.T) {
+       var sslList SslList
+       emptyData := `
+{
+       "key": "test",
+       "nodes": {}
+}
+`
+       err := json.Unmarshal([]byte(emptyData), &sslList)
+       assert.Nil(t, err)
+
+       notEmptyObject := `
+{
+       "key": "test",
+       "nodes": {"a": "b", "c": "d"}
+}
+`
+       err = json.Unmarshal([]byte(notEmptyObject), &sslList)
+       assert.Equal(t, err.Error(), "unexpected non-empty object")
+
+       emptyArray := `
+{
+       "key": "test",
+       "nodes": []
+}
+`
+       err = json.Unmarshal([]byte(emptyArray), &sslList)
+       assert.Nil(t, err)
+
+       normalData := `
+{
+       "key": "test",
+       "nodes": [
+               {
+                       "key": "ssl id",
+                       "value": {
+                               "snis": ["test.apisix.org"],
+                               "cert": "root",
+                               "key": "123456",
+                               "status": 1
+                       }
+               }
+       ]
+}
+`
+       err = json.Unmarshal([]byte(normalData), &sslList)
+       assert.Nil(t, err)
+       assert.Equal(t, len(sslList.SslNodes), 1)
+
+       key := *sslList.SslNodes[0].Key
+       assert.Equal(t, key, "ssl id")
+       cert := *sslList.SslNodes[0].Ssl.Cert
+       assert.Equal(t, cert, "root")
+       sslKey := *sslList.SslNodes[0].Ssl.Key
+       assert.Equal(t, sslKey, "123456")
+       sni := *sslList.SslNodes[0].Ssl.Snis[0]
+       assert.Equal(t, sni, "test.apisix.org")
+}
diff --git a/pkg/seven/apisix/upstream.go b/pkg/seven/apisix/upstream.go
new file mode 100644
index 0000000..5d9b372
--- /dev/null
+++ b/pkg/seven/apisix/upstream.go
@@ -0,0 +1,269 @@
+// 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 apisix
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "strconv"
+       "strings"
+
+       "github.com/golang/glog"
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       "github.com/api7/ingress-controller/pkg/seven/conf"
+       "github.com/api7/ingress-controller/pkg/seven/db"
+       "github.com/api7/ingress-controller/pkg/seven/utils"
+)
+
+// FindCurrentUpstream find upstream from memDB,
+// if Not Found, find upstream from apisix
+func FindCurrentUpstream(group, name, fullName string) (*v1.Upstream, error) {
+       ur := &db.UpstreamRequest{Group: group, Name: name, FullName: fullName}
+       currentUpstream, _ := ur.FindByName()
+       if currentUpstream != nil {
+               return currentUpstream, nil
+       } else {
+               // find upstream from apisix
+               if upstreams, err := ListUpstream(group); err != nil {
+                       glog.Errorf("list upstreams in etcd failed, group: %s, 
err: %+v", group, err)
+                       return nil, fmt.Errorf("list upstreams failed, err: 
%+v", err)
+               } else {
+                       for _, upstream := range upstreams {
+                               if upstream.Name != nil && *(upstream.Name) == 
name {
+                                       // and save to memDB
+                                       upstreamDB := &db.UpstreamDB{Upstreams: 
[]*v1.Upstream{upstream}}
+                                       upstreamDB.InsertUpstreams()
+                                       
//InsertUpstreams([]*v1.Upstream{upstream})
+                                       // return
+                                       return upstream, nil
+                               }
+                       }
+               }
+
+       }
+       return nil, nil
+}
+
+// ListUpstream list upstream from etcd , convert to v1.Upstream
+func ListUpstream(group string) ([]*v1.Upstream, error) {
+       baseUrl := conf.FindUrl(group)
+       url := baseUrl + "/upstreams"
+       ret, err := Get(url)
+       if err != nil {
+               return nil, fmt.Errorf("http get failed, url: %s, err: %+v", 
url, err)
+       }
+       var upstreamsResponse UpstreamsResponse
+       if err := json.Unmarshal(ret, &upstreamsResponse); err != nil {
+               return nil, fmt.Errorf("json转换失败")
+       } else {
+               upstreams := make([]*v1.Upstream, 0)
+               for _, u := range upstreamsResponse.Upstreams.Upstreams {
+                       if n, err := u.convert(group); err == nil {
+                               upstreams = append(upstreams, n)
+                       } else {
+                               return nil, fmt.Errorf("upstream: %s 转换失败, %s", 
*u.UpstreamNodes.Desc, err.Error())
+                       }
+               }
+               return upstreams, nil
+       }
+}
+
+//func IsExist(name string) (bool, error) {
+//     if upstreams, err := ListUpstream(); err != nil {
+//             return false, err
+//     } else {
+//             for _, upstream := range upstreams {
+//                     if *upstream.Name == name {
+//                             return true, nil
+//                     }
+//             }
+//             return false, nil
+//     }
+//}
+
+func AddUpstream(upstream *v1.Upstream) (*UpstreamResponse, error) {
+       baseUrl := conf.FindUrl(*upstream.Group)
+       url := fmt.Sprintf("%s/upstreams", baseUrl)
+       glog.V(2).Info(url)
+       ur := convert2UpstreamRequest(upstream)
+       if b, err := json.Marshal(ur); err != nil {
+               return nil, err
+       } else {
+               if res, err := utils.Post(url, b); err != nil {
+                       return nil, fmt.Errorf("http post failed, url: %s, err: 
%+v", url, err)
+               } else {
+                       var uRes UpstreamResponse
+                       if err = json.Unmarshal(res, &uRes); err != nil {
+                               glog.Errorf("json Unmarshal error: %s", 
err.Error())
+                               return nil, err
+                       } else {
+                               glog.V(2).Info(uRes)
+                               if uRes.Upstream.Key != nil {
+                                       return &uRes, nil
+                               } else {
+                                       return nil, fmt.Errorf("apisix upstream 
not expected response")
+                               }
+                       }
+               }
+       }
+}
+
+func UpdateUpstream(upstream *v1.Upstream) error {
+       baseUrl := conf.FindUrl(*upstream.Group)
+       url := fmt.Sprintf("%s/upstreams/%s", baseUrl, *upstream.ID)
+       ur := convert2UpstreamRequest(upstream)
+       if b, err := json.Marshal(ur); err != nil {
+               return err
+       } else {
+               if _, err := utils.Patch(url, b); err != nil {
+                       return fmt.Errorf("http patch failed, url: %s, err: 
%+v", url, err)
+               } else {
+                       return nil
+               }
+       }
+}
+
+func PatchNodes(upstream *v1.Upstream, nodes []*v1.Node) error {
+       baseUrl := conf.FindUrl(*upstream.Group)
+       url := fmt.Sprintf("%s/upstreams/%s/nodes", baseUrl, *upstream.ID)
+       nodeMap := convertNodes(nodes)
+       if b, err := json.Marshal(nodeMap); err != nil {
+               return err
+       } else {
+               if _, err := utils.Patch(url, b); err != nil {
+                       return fmt.Errorf("http patch failed, url: %s, err: 
%+v", url, err)
+               } else {
+                       return nil
+               }
+       }
+}
+
+func DeleteUpstream(upstream *v1.Upstream) error {
+       baseUrl := conf.FindUrl(*upstream.Group)
+       url := fmt.Sprintf("%s/upstreams/%s", baseUrl, *upstream.ID)
+       if _, err := utils.Delete(url); err != nil {
+               return fmt.Errorf("http delete failed, url: %s, err: %+v", url, 
err)
+       } else {
+               return nil
+       }
+}
+
+func convert2UpstreamRequest(upstream *v1.Upstream) *UpstreamRequest {
+       nodes := convertNodes(upstream.Nodes)
+       return &UpstreamRequest{
+               LBType: *upstream.Type,
+               HashOn: upstream.HashOn,
+               Key:    upstream.Key,
+               Desc:   *upstream.Name,
+               Nodes:  nodes,
+       }
+}
+
+func convertNodes(nodes []*v1.Node) map[string]int64 {
+       result := make(map[string]int64)
+       for _, u := range nodes {
+               result[*u.IP+":"+strconv.Itoa(*u.Port)] = int64(*u.Weight)
+       }
+       return result
+}
+
+// convert convert Upstream from etcd to v1.Upstream
+func (u *Upstream) convert(group string) (*v1.Upstream, error) {
+       // id
+       keys := strings.Split(*u.Key, "/")
+       id := keys[len(keys)-1]
+       // Name
+       name := u.UpstreamNodes.Desc
+       // type
+       LBType := u.UpstreamNodes.LBType
+       // key
+       key := u.Key
+       // nodes
+       nodes := make([]*v1.Node, 0)
+       for k, v := range u.UpstreamNodes.Nodes {
+               ks := strings.Split(k, ":")
+               ip := ks[0]
+               port := 80
+               if len(ks) > 1 {
+                       port, _ = strconv.Atoi(ks[1])
+               }
+               weight := int(v)
+               node := &v1.Node{IP: &ip, Port: &port, Weight: &weight}
+               nodes = append(nodes, node)
+       }
+       // fullName
+       fullName := *name
+       if group != "" {
+               fullName = group + "_" + *name
+       }
+       return &v1.Upstream{ID: &id, FullName: &fullName, Group: &group, Name: 
name, Type: LBType, Key: key, Nodes: nodes}, nil
+}
+
+type UpstreamsResponse struct {
+       Upstreams Upstreams `json:"node"`
+}
+
+type UpstreamResponse struct {
+       Action   string   `json:"action"`
+       Upstream Upstream `json:"node"`
+}
+
+type Upstreams struct {
+       Key       string      `json:"key"` // 用来定位upstreams 列表
+       Upstreams UpstreamSet `json:"nodes"`
+}
+
+type UpstreamSet []Upstream
+
+// UpstreamSet.UnmarshalJSON implements json.Unmarshaler interface.
+// lua-cjson doesn't distinguish empty array and table,
+// and by default empty array will be encoded as '{}'.
+// We have to maintain the compatibility.
+func (set *UpstreamSet) UnmarshalJSON(p []byte) error {
+       if p[0] == '{' {
+               if len(p) != 2 {
+                       return errors.New("unexpected non-empty object")
+               }
+               return nil
+       }
+       var ups []Upstream
+       if err := json.Unmarshal(p, &ups); err != nil {
+               return err
+       }
+       *set = ups
+       return nil
+}
+
+type Upstream struct {
+       Key           *string       `json:"key"` // upstream key
+       UpstreamNodes UpstreamNodes `json:"value"`
+}
+
+type UpstreamNodes struct {
+       Nodes  map[string]int64 `json:"nodes"`
+       Desc   *string          `json:"desc"` // upstream name  = k8s svc
+       LBType *string          `json:"type"` // 负载均衡类型
+}
+
+//{"type":"roundrobin","nodes":{"10.244.10.11:8080":100},"desc":"somesvc"}
+type UpstreamRequest struct {
+       LBType string           `json:"type"`
+       HashOn *string          `json:"hash_on,omitempty"`
+       Key    *string          `json:"key,omitempty"`
+       Nodes  map[string]int64 `json:"nodes"`
+       Desc   string           `json:"desc"`
+}
diff --git a/pkg/seven/apisix/upstream_test.go 
b/pkg/seven/apisix/upstream_test.go
new file mode 100644
index 0000000..1aee995
--- /dev/null
+++ b/pkg/seven/apisix/upstream_test.go
@@ -0,0 +1,84 @@
+// 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 apisix
+
+import (
+       "encoding/json"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestUpstreamsUnmarshalJSON(t *testing.T) {
+       var ups Upstreams
+       emptyData := `
+{
+       "key": "test",
+       "nodes": {}
+}
+`
+       err := json.Unmarshal([]byte(emptyData), &ups)
+       assert.Nil(t, err)
+
+       emptyData = `
+{
+       "key": "test",
+       "nodes": {"a": "b", "c": "d"}
+}
+`
+       err = json.Unmarshal([]byte(emptyData), &ups)
+       assert.Equal(t, err.Error(), "unexpected non-empty object")
+
+       emptyArray := `
+{
+       "key": "test",
+       "nodes": []
+}
+`
+       err = json.Unmarshal([]byte(emptyArray), &ups)
+       assert.Nil(t, err)
+
+       normalData := `
+{
+       "key": "test",
+       "nodes": [
+               {
+                       "key": "ups1",
+                       "value": {
+                               "desc": "test upstream 1",
+                               "type": "rr",
+                               "nodes": {
+                                       "192.168.12.12": 100
+                               }
+                       }
+               }
+       ]
+}
+`
+       err = json.Unmarshal([]byte(normalData), &ups)
+       assert.Nil(t, err)
+       assert.Equal(t, ups.Key, "test")
+       assert.Equal(t, len(ups.Upstreams), 1)
+
+       key := *ups.Upstreams[0].Key
+       assert.Equal(t, key, "ups1")
+       desc := *ups.Upstreams[0].UpstreamNodes.Desc
+       assert.Equal(t, desc, "test upstream 1")
+       lb := *ups.Upstreams[0].UpstreamNodes.LBType
+       assert.Equal(t, lb, "rr")
+
+       assert.Equal(t, len(ups.Upstreams[0].UpstreamNodes.Nodes), 1)
+       assert.Equal(t, ups.Upstreams[0].UpstreamNodes.Nodes["192.168.12.12"], 
int64(100))
+}
diff --git a/pkg/seven/conf/conf.go b/pkg/seven/conf/conf.go
new file mode 100644
index 0000000..1f016c6
--- /dev/null
+++ b/pkg/seven/conf/conf.go
@@ -0,0 +1,36 @@
+// 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 conf
+
+var BaseUrl = "http://172.16.20.90:30116/apisix/admin";
+var UrlGroup = make(map[string]string)
+
+func SetBaseUrl(url string) {
+       BaseUrl = url
+}
+
+func AddGroup(group string) {
+       if group != "" {
+               UrlGroup[group] = "http://"; + group + "/apisix/admin"
+       }
+}
+
+func FindUrl(group string) string {
+       if group != "" && UrlGroup[group] != "" {
+               return UrlGroup[group]
+       } else {
+               return BaseUrl
+       }
+}
diff --git a/pkg/seven/conf/conf_test.go b/pkg/seven/conf/conf_test.go
new file mode 100644
index 0000000..1540cca
--- /dev/null
+++ b/pkg/seven/conf/conf_test.go
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package conf
+
+import "testing"
+
+func Test_map(t *testing.T) {
+       m1 := make(map[string]string)
+       m1["a"] = "aa"
+       m1["b"] = "bb"
+       t.Log(m1["c"] == "")
+}
diff --git a/pkg/seven/db/route.go b/pkg/seven/db/route.go
new file mode 100644
index 0000000..d6c143e
--- /dev/null
+++ b/pkg/seven/db/route.go
@@ -0,0 +1,110 @@
+// 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 db
+
+import (
+       "fmt"
+
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+       "github.com/hashicorp/go-memdb"
+)
+
+const (
+       Route = "Route"
+)
+
+type RouteRequest struct {
+       Group    string
+       Name     string
+       FullName string
+}
+
+func (rr *RouteRequest) FindByName() (*v1.Route, error) {
+       txn := DB.Txn(false)
+       defer txn.Abort()
+       if raw, err := txn.First(Route, "id", rr.FullName); err != nil {
+               return nil, err
+       } else {
+               if raw != nil {
+                       currentRoute := raw.(*v1.Route)
+                       return currentRoute, nil
+               }
+               return nil, fmt.Errorf("NOT FOUND")
+       }
+}
+
+type RouteDB struct {
+       Routes []*v1.Route
+}
+
+// InsertRoute insert route to memDB
+func (db *RouteDB) Insert() error {
+       txn := DB.Txn(true)
+       defer txn.Abort()
+       for _, r := range db.Routes {
+               if err := txn.Insert(Route, r); err != nil {
+                       return err
+               }
+       }
+       txn.Commit()
+       return nil
+}
+
+func (db *RouteDB) UpdateRoute() error {
+       txn := DB.Txn(true)
+       defer txn.Abort()
+       for _, r := range db.Routes {
+               // 1. delete
+               if _, err := txn.DeleteAll(Route, "id", *(r.FullName)); err != 
nil {
+                       return err
+               }
+               // 2. insert
+               if err := txn.Insert(Route, r); err != nil {
+                       return err
+               }
+       }
+       txn.Commit()
+       return nil
+}
+
+func (db *RouteDB) DeleteRoute() error {
+       txn := DB.Txn(true)
+       defer txn.Abort()
+       for _, r := range db.Routes {
+               //if _, err := txn.DeleteAll(Route, "id", *(r.ID)); err != nil {
+               if _, err := txn.DeleteAll(Route, "id", *(r.FullName)); err != 
nil {
+                       return err
+               }
+       }
+       txn.Commit()
+       return nil
+}
+
+var routeSchema = &memdb.TableSchema{
+       Name: Route,
+       Indexes: map[string]*memdb.IndexSchema{
+               "id": {
+                       Name:    "id",
+                       Unique:  true,
+                       Indexer: &memdb.StringFieldIndex{Field: "FullName"},
+               },
+               "name": {
+                       Name:         "name",
+                       Unique:       true,
+                       Indexer:      &memdb.StringFieldIndex{Field: "Name"},
+                       AllowMissing: true,
+               },
+       },
+}
diff --git a/pkg/seven/db/service.go b/pkg/seven/db/service.go
new file mode 100644
index 0000000..d47bd29
--- /dev/null
+++ b/pkg/seven/db/service.go
@@ -0,0 +1,97 @@
+// 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 db
+
+import (
+       "fmt"
+
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+       "github.com/hashicorp/go-memdb"
+)
+
+const (
+       Service = "Service"
+)
+
+type ServiceRequest struct {
+       Group    string
+       Name     string
+       FullName string
+}
+
+func (sr *ServiceRequest) FindByName() (*v1.Service, error) {
+       txn := DB.Txn(false)
+       defer txn.Abort()
+       if raw, err := txn.First(Service, "id", sr.FullName); err != nil {
+               return nil, err
+       } else {
+               if raw != nil {
+                       currentService := raw.(*v1.Service)
+                       return currentService, nil
+               }
+               return nil, fmt.Errorf("NOT FOUND")
+       }
+}
+
+func (db *ServiceDB) Insert() error {
+       txn := DB.Txn(true)
+       defer txn.Abort()
+       for _, s := range db.Services {
+               if err := txn.Insert(Service, s); err != nil {
+                       return err
+               }
+       }
+       txn.Commit()
+       return nil
+}
+
+type ServiceDB struct {
+       Services []*v1.Service
+}
+
+func (db *ServiceDB) UpdateService() error {
+       txn := DB.Txn(true)
+       defer txn.Abort()
+       for _, s := range db.Services {
+               // 1. delete
+               if _, err := txn.DeleteAll(Service, "id", *(s.FullName)); err 
!= nil {
+                       return err
+               }
+               // 2. insert
+               if err := txn.Insert(Service, s); err != nil {
+                       return err
+               }
+       }
+
+       txn.Commit()
+       return nil
+}
+
+var serviceSchema = &memdb.TableSchema{
+       Name: Service,
+       Indexes: map[string]*memdb.IndexSchema{
+               "id": {
+                       Name:    "id",
+                       Unique:  true,
+                       Indexer: &memdb.StringFieldIndex{Field: "FullName"},
+               },
+               "name": {
+                       Name:         "name",
+                       Unique:       true,
+                       Indexer:      &memdb.StringFieldIndex{Field: "Name"},
+                       AllowMissing: true,
+               },
+       },
+}
diff --git a/pkg/seven/db/store.go b/pkg/seven/db/store.go
new file mode 100644
index 0000000..9d71355
--- /dev/null
+++ b/pkg/seven/db/store.go
@@ -0,0 +1,43 @@
+// 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 db
+
+import "github.com/hashicorp/go-memdb"
+
+var DB *memdb.MemDB
+
+func init() {
+       if db, err := NewDB(); err != nil {
+               panic(err)
+       } else {
+               DB = db
+       }
+}
+
+func NewDB() (*memdb.MemDB, error) {
+       var schema = &memdb.DBSchema{
+               Tables: map[string]*memdb.TableSchema{
+                       Service:  serviceSchema,
+                       Route:    routeSchema,
+                       Upstream: upstreamSchema,
+               },
+       }
+
+       if memDB, err := memdb.NewMemDB(schema); err != nil {
+               return nil, err
+       } else {
+               return memDB, nil
+       }
+}
diff --git a/pkg/seven/db/upstream.go b/pkg/seven/db/upstream.go
new file mode 100644
index 0000000..361565c
--- /dev/null
+++ b/pkg/seven/db/upstream.go
@@ -0,0 +1,103 @@
+// 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 db
+
+import (
+       "fmt"
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+       "github.com/hashicorp/go-memdb"
+)
+
+const (
+       Upstream = "Upstream"
+)
+
+type UpstreamDB struct {
+       Upstreams []*v1.Upstream
+}
+
+type UpstreamRequest struct {
+       Group    string
+       Name     string
+       FullName string
+}
+
+func (ur *UpstreamRequest) FindByName() (*v1.Upstream, error) {
+       txn := DB.Txn(false)
+       defer txn.Abort()
+       if raw, err := txn.First(Upstream, "id", ur.FullName); err != nil {
+               return nil, err
+       } else {
+               if raw != nil {
+                       currentUpstream := raw.(*v1.Upstream)
+                       return currentUpstream, nil
+               }
+               return nil, fmt.Errorf("NOT FOUND")
+       }
+}
+
+// insertUpstream insert upstream to memDB
+func (upstreamDB *UpstreamDB) InsertUpstreams() error {
+       txn := DB.Txn(true)
+       defer txn.Abort()
+       for _, u := range upstreamDB.Upstreams {
+               if err := txn.Insert(Upstream, u); err != nil {
+                       return err
+               }
+       }
+       txn.Commit()
+       return nil
+}
+
+func (upstreamDB *UpstreamDB) UpdateUpstreams() error {
+       txn := DB.Txn(true)
+       defer txn.Abort()
+       for _, u := range upstreamDB.Upstreams {
+               // delete
+               if _, err := txn.DeleteAll(Upstream, "id", *(u.FullName)); err 
!= nil {
+                       return err
+               }
+               // insert
+               if err := txn.Insert(Upstream, u); err != nil {
+                       return err
+               }
+       }
+       txn.Commit()
+       return nil
+}
+
+var upstreamSchema = &memdb.TableSchema{
+       Name: Upstream,
+       Indexes: map[string]*memdb.IndexSchema{
+               "id": {
+                       Name:    "id",
+                       Unique:  true,
+                       Indexer: &memdb.StringFieldIndex{Field: "FullName"},
+               },
+               "name": {
+                       Name:         "name",
+                       Unique:       true,
+                       Indexer:      &memdb.StringFieldIndex{Field: "Name"},
+                       AllowMissing: true,
+               },
+       },
+}
+
+//func indexer() *memdb.CompoundMultiIndex{
+//     var idx = make([]memdb.Indexer, 0)
+//     idx = append(idx, &memdb.StringFieldIndex{Field: "Group"})
+//     idx = append(idx, &memdb.StringFieldIndex{Field: "Name"})
+//     return &memdb.CompoundMultiIndex{Indexes: idx, AllowMissing: false}
+//}
diff --git a/pkg/seven/state/builder.go b/pkg/seven/state/builder.go
new file mode 100644
index 0000000..a300b55
--- /dev/null
+++ b/pkg/seven/state/builder.go
@@ -0,0 +1,244 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package state
+
+import (
+       "strconv"
+       "strings"
+
+       "github.com/golang/glog"
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       "github.com/api7/ingress-controller/pkg/seven/apisix"
+       "github.com/api7/ingress-controller/pkg/seven/db"
+       "github.com/api7/ingress-controller/pkg/seven/utils"
+)
+
+const (
+       ApisixUpstream = "ApisixUpstream"
+       WatchFromKind  = "watch"
+)
+
+//// InitDB insert object into memDB first time
+//func InitDB(){
+//     routes, _ := apisix.ListRoute()
+//     upstreams, _ := apisix.ListUpstream()
+//     apisix.InsertRoute(routes)
+//     apisix.InsertUpstreams(upstreams)
+//}
+//
+//// LoadTargetState load targetState from ... maybe k8s CRD
+//func LoadTargetState(routes []*v1.Route, upstreams []*v1.Upstream){
+//
+//     // 1.diff
+//     // 2.send event
+//}
+
+// paddingRoute padding route from memDB
+func paddingRoute(route *v1.Route, currentRoute *v1.Route) {
+       // padding object, just id
+       if currentRoute == nil {
+               // NOT FOUND : set Id = 0
+               id := strconv.Itoa(0)
+               route.ID = &id
+       } else {
+               route.ID = currentRoute.ID
+       }
+}
+
+// padding service from memDB
+func paddingService(service *v1.Service, currentService *v1.Service) {
+       if currentService == nil {
+               id := strconv.Itoa(0)
+               service.ID = &id
+       } else {
+               service.ID = currentService.ID
+       }
+}
+
+// paddingUpstream padding upstream from memDB
+func paddingUpstream(upstream *v1.Upstream, currentUpstream *v1.Upstream) {
+       // padding id
+       if currentUpstream == nil {
+               // NOT FOUND : set Id = 0
+               id := strconv.Itoa(0)
+               upstream.ID = &id
+       } else {
+               upstream.ID = currentUpstream.ID
+       }
+       // todo padding nodes ? or sync nodes from crd ?
+}
+
+// NewRouteWorkers make routeWrokers group by service per CRD
+// 1.make routes group by (1_2_3) it may be a map like map[1_2_3][]Route;
+// 2.route is listenning Event from the ready of 1_2_3;
+func NewRouteWorkers(routes []*v1.Route) RouteWorkerGroup {
+       rwg := make(RouteWorkerGroup)
+       for _, r := range routes {
+               quit := make(chan Quit)
+               rw := &routeWorker{Route: r, Quit: quit}
+               rw.start()
+               rwg.Add(*r.ServiceName, rw)
+       }
+       return rwg
+}
+
+// 3.route get the Event and trigger a padding for object,then diff,sync;
+func (r *routeWorker) trigger(event Event) error {
+       defer close(r.Quit)
+       // consumer Event
+       service := event.Obj.(*v1.Service)
+       r.ServiceId = service.ID
+       glog.V(2).Infof("trigger routeWorker %s from %s, %s", *r.Name, 
event.Op, *service.Name)
+
+       // padding
+       currentRoute, _ := apisix.FindCurrentRoute(r.Route)
+       paddingRoute(r.Route, currentRoute)
+       // diff
+       hasDiff, err := utils.HasDiff(r.Route, currentRoute)
+       // sync
+       if err != nil {
+               return err
+       }
+       if hasDiff {
+               r.sync()
+       }
+       // todo broadcast
+
+       return nil
+}
+
+// sync
+func (r *routeWorker) sync() {
+       if *r.Route.ID != strconv.Itoa(0) {
+               // 1. sync memDB
+               db := &db.RouteDB{Routes: []*v1.Route{r.Route}}
+               if err := db.UpdateRoute(); err != nil {
+                       glog.Errorf("update route failed, route: %#v, err: 
%+v", r.Route, err)
+                       return
+               }
+               // 2. sync apisix
+               apisix.UpdateRoute(r.Route)
+               glog.V(2).Infof("update route %s, %s", *r.Name, *r.ServiceId)
+       } else {
+               // 1. sync apisix and get id
+               if res, err := apisix.AddRoute(r.Route); err != nil {
+                       glog.Errorf("add route failed, route: %#v, err: %+v", 
r.Route, err)
+                       return
+               } else {
+                       key := res.Route.Key
+                       tmp := strings.Split(*key, "/")
+                       *r.ID = tmp[len(tmp)-1]
+               }
+               // 2. sync memDB
+               db := &db.RouteDB{Routes: []*v1.Route{r.Route}}
+               db.Insert()
+               glog.V(2).Infof("create route %s, %s", *r.Name, *r.ServiceId)
+       }
+}
+
+// service
+func NewServiceWorkers(services []*v1.Service, rwg *RouteWorkerGroup) 
ServiceWorkerGroup {
+       swg := make(ServiceWorkerGroup)
+       for _, s := range services {
+               quit := make(chan Quit)
+               rw := &serviceWorker{Service: s, Quit: quit}
+               rw.start(rwg)
+               swg.Add(*s.UpstreamName, rw)
+       }
+       return swg
+}
+
+// upstream
+func SolverUpstream(upstreams []*v1.Upstream, swg ServiceWorkerGroup) {
+       for _, u := range upstreams {
+               op := Update
+               if currentUpstream, err := apisix.FindCurrentUpstream(*u.Group, 
*u.Name, *u.FullName); err != nil {
+                       glog.Errorf("solver upstream failed, find upstream from 
etcd failed, upstream: %+v, err: %+v", u, err)
+                       return
+               } else {
+                       paddingUpstream(u, currentUpstream)
+                       // diff
+                       hasDiff, _ := utils.HasDiff(u, currentUpstream)
+                       if hasDiff {
+                               if *u.ID != strconv.Itoa(0) {
+                                       op = Update
+                                       // 0.field check
+                                       needToUpdate := true
+                                       if currentUpstream.FromKind != nil && 
*(currentUpstream.FromKind) == ApisixUpstream { // update from ApisixUpstream
+                                               if u.FromKind == nil || 
(u.FromKind != nil && *(u.FromKind) != ApisixUpstream) {
+                                                       // currentUpstream > u
+                                                       // set lb && health 
check
+                                                       needToUpdate = false
+                                               }
+                                       }
+                                       if needToUpdate {
+                                               // 1.sync memDB
+                                               upstreamDB := 
&db.UpstreamDB{Upstreams: []*v1.Upstream{u}}
+                                               if err := 
upstreamDB.UpdateUpstreams(); err != nil {
+                                                       glog.Errorf("solver 
upstream failed, update upstream to local db failed, err: %s", err.Error())
+                                                       return
+                                               }
+                                               // 2.sync apisix
+                                               if err = 
apisix.UpdateUpstream(u); err != nil {
+                                                       glog.Errorf("solver 
upstream failed, update upstream to etcd failed, err: %+v", err)
+                                                       return
+                                               }
+                                       }
+                                       // if fromKind == WatchFromKind
+                                       if u.FromKind != nil && *u.FromKind == 
WatchFromKind {
+                                               // 1.update nodes
+                                               if err = apisix.PatchNodes(u, 
u.Nodes); err != nil {
+                                                       glog.Errorf("solver 
upstream failed, patch node info to etcd failed, err: %+v", err)
+                                                       return
+                                               }
+                                               // 2. sync memDB
+                                               us := []*v1.Upstream{u}
+                                               if !needToUpdate {
+                                                       currentUpstream.Nodes = 
u.Nodes
+                                                       us = 
[]*v1.Upstream{currentUpstream}
+                                               }
+                                               upstreamDB := 
&db.UpstreamDB{Upstreams: us}
+                                               if err := 
upstreamDB.UpdateUpstreams(); err != nil {
+                                                       glog.Errorf("solver 
upstream failed, update upstream to local db failed, err: %s", err.Error())
+                                                       return
+                                               }
+                                       }
+                               } else {
+                                       op = Create
+                                       // 1.sync apisix and get response
+                                       if upstreamResponse, err := 
apisix.AddUpstream(u); err != nil {
+                                               glog.Errorf("solver upstream 
failed, update upstream to etcd failed, err: %+v", err)
+                                               return
+                                       } else {
+                                               tmp := 
strings.Split(*upstreamResponse.Upstream.Key, "/")
+                                               *u.ID = tmp[len(tmp)-1]
+                                       }
+                                       // 2.sync memDB
+                                       
//apisix.InsertUpstreams([]*v1.Upstream{u})
+                                       upstreamDB := &db.UpstreamDB{Upstreams: 
[]*v1.Upstream{u}}
+                                       upstreamDB.InsertUpstreams()
+                               }
+                       }
+               }
+               glog.V(2).Infof("solver upstream %s:%s", op, *u.Name)
+               // anyway, broadcast to service
+               serviceWorkers := swg[*u.Name]
+               for _, sw := range serviceWorkers {
+                       event := &Event{Kind: UpstreamKind, Op: op, Obj: u}
+                       sw.Event <- *event
+               }
+       }
+}
diff --git a/pkg/ingress/apisix/plugin.go b/pkg/seven/state/builder_test.go
similarity index 50%
copy from pkg/ingress/apisix/plugin.go
copy to pkg/seven/state/builder_test.go
index 4feb097..7550236 100644
--- a/pkg/ingress/apisix/plugin.go
+++ b/pkg/seven/state/builder_test.go
@@ -12,40 +12,40 @@
 // 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 apisix
+package state
 
 import (
-       "github.com/gxthrj/seven/apisix"
-       "strconv"
+       "testing"
+
+       "github.com/api7/ingress-controller/pkg/seven/utils"
 )
 
-type CorsYaml struct {
-       Enable       bool   `json:"enable,omitempty"`
-       AllowOrigin  string `json:"allow_origin,omitempty"`
-       AllowHeaders string `json:"allow_headers,omitempty"`
-       AllowMethods string `json:"allow_methods,omitempty"`
+type school struct {
+       *province
+       Name    string `json:"name"`
+       Address string `json:"address"`
 }
 
-func (c *CorsYaml) SetEnable(enable string) {
-       if b, err := strconv.ParseBool(enable); err != nil {
-               c.Enable = false
-       } else {
-               c.Enable = b
-       }
+type province struct {
+       Location string `json:"location"`
 }
 
-func (c *CorsYaml) SetOrigin(origin string) {
-       c.AllowOrigin = origin
-}
+func Test_diff(t *testing.T) {
+       //p1 := &province{Location: "jiangsu"}
+       p2 := &province{Location: "zh"}
+       s1 := &school{Name: "hello", Address: "this is a address"}
+       s2 := &school{Name: "hello", Address: "this is a address", province: p2}
+       t.Log(s1)
+       t.Log(s2)
+       if d, err := utils.Diff(s1, s2); err != nil {
+               t.Log(err.Error())
+       } else {
+               //t.Logf("s1 vs s2 hasDiff ? %v", d)
+               t.Log(d)
+               for _, delta := range d.Deltas() {
+                       t.Log(delta.Similarity())
+               }
 
-func (c *CorsYaml) SetHeaders(headers string) {
-       c.AllowHeaders = headers
-}
-func (c *CorsYaml) SetMethods(methods string) {
-       c.AllowMethods = methods
-}
+       }
 
-func (c *CorsYaml) Build() *apisix.Cors {
-       maxAge := int64(3600)
-       return apisix.BuildCors(c.Enable, &c.AllowOrigin, &c.AllowHeaders, 
&c.AllowMethods, &maxAge)
 }
diff --git a/pkg/seven/state/diff.go b/pkg/seven/state/diff.go
new file mode 100644
index 0000000..2de068d
--- /dev/null
+++ b/pkg/seven/state/diff.go
@@ -0,0 +1,15 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package state
diff --git a/pkg/ingress/apisix/plugin.go b/pkg/seven/state/event.go
similarity index 50%
copy from pkg/ingress/apisix/plugin.go
copy to pkg/seven/state/event.go
index 4feb097..026b99e 100644
--- a/pkg/ingress/apisix/plugin.go
+++ b/pkg/seven/state/event.go
@@ -12,40 +12,38 @@
 // 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 apisix
+package state
 
 import (
-       "github.com/gxthrj/seven/apisix"
-       "strconv"
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
 )
 
-type CorsYaml struct {
-       Enable       bool   `json:"enable,omitempty"`
-       AllowOrigin  string `json:"allow_origin,omitempty"`
-       AllowHeaders string `json:"allow_headers,omitempty"`
-       AllowMethods string `json:"allow_methods,omitempty"`
+type ApisixCombination struct {
+       Routes    []*v1.Route
+       Services  []*v1.Service
+       Upstreams []*v1.Upstream
 }
 
-func (c *CorsYaml) SetEnable(enable string) {
-       if b, err := strconv.ParseBool(enable); err != nil {
-               c.Enable = false
-       } else {
-               c.Enable = b
-       }
+type RouteCompare struct {
+       OldRoutes []*v1.Route
+       NewRoutes []*v1.Route
 }
 
-func (c *CorsYaml) SetOrigin(origin string) {
-       c.AllowOrigin = origin
+type Quit struct {
+       Name string
 }
 
-func (c *CorsYaml) SetHeaders(headers string) {
-       c.AllowHeaders = headers
-}
-func (c *CorsYaml) SetMethods(methods string) {
-       c.AllowMethods = methods
-}
+const (
+       RouteKind    = "route"
+       ServiceKind  = "service"
+       UpstreamKind = "upstream"
+       Create       = "create"
+       Update       = "update"
+       Delete       = "delete"
+)
 
-func (c *CorsYaml) Build() *apisix.Cors {
-       maxAge := int64(3600)
-       return apisix.BuildCors(c.Enable, &c.AllowOrigin, &c.AllowHeaders, 
&c.AllowMethods, &maxAge)
+type Event struct {
+       Kind string      // route/service/upstream
+       Op   string      // create update delete
+       Obj  interface{} // the obj of kind
 }
diff --git a/pkg/seven/state/route_worker.go b/pkg/seven/state/route_worker.go
new file mode 100644
index 0000000..1ee0c31
--- /dev/null
+++ b/pkg/seven/state/route_worker.go
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package state
+
+import "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+type routeWorker struct {
+       *v1.Route
+       Event chan Event
+       Quit  chan Quit
+}
+
+// RouteWorkerGroup for broadcast from service to route
+type RouteWorkerGroup map[string][]*routeWorker
+
+// start start watch event
+func (w *routeWorker) start() {
+       w.Event = make(chan Event)
+       go func() {
+               for {
+                       select {
+                       case event := <-w.Event:
+                               w.trigger(event)
+                       case <-w.Quit:
+                               return
+                       }
+               }
+       }()
+}
+
+func (rg *RouteWorkerGroup) Add(key string, rw *routeWorker) {
+       routes := (*rg)[key]
+       if routes == nil {
+               routes = make([]*routeWorker, 0)
+       }
+       routes = append(routes, rw)
+       (*rg)[key] = routes
+}
+
+func (rg *RouteWorkerGroup) Delete(key string, route *routeWorker) {
+       routes := (*rg)[key]
+       result := make([]*routeWorker, 0)
+       for _, r := range routes {
+               if r.Name != route.Name {
+                       result = append(result, r)
+               }
+       }
+       (*rg)[key] = result
+}
diff --git a/pkg/seven/state/service_worker.go 
b/pkg/seven/state/service_worker.go
new file mode 100644
index 0000000..20e173f
--- /dev/null
+++ b/pkg/seven/state/service_worker.go
@@ -0,0 +1,195 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package state
+
+import (
+       "strconv"
+       "strings"
+
+       "github.com/golang/glog"
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       "github.com/api7/ingress-controller/pkg/seven/apisix"
+       "github.com/api7/ingress-controller/pkg/seven/db"
+       "github.com/api7/ingress-controller/pkg/seven/utils"
+)
+
+const ApisixService = "ApisixService"
+
+type serviceWorker struct {
+       *v1.Service
+       Event chan Event
+       Quit  chan Quit
+}
+
+// ServiceWorkerGroup for broadcast from upstream to service
+type ServiceWorkerGroup map[string][]*serviceWorker
+
+// start start watch event
+func (w *serviceWorker) start(rwg *RouteWorkerGroup) {
+       w.Event = make(chan Event)
+       go func() {
+               for {
+                       select {
+                       case event := <-w.Event:
+                               w.trigger(event, rwg)
+                       case <-w.Quit:
+                               return
+                       }
+               }
+       }()
+}
+
+// trigger add to queue
+func (w *serviceWorker) trigger(event Event, rwg *RouteWorkerGroup) error {
+       glog.V(2).Infof("1.service trigger from %s, %s", event.Op, event.Kind)
+       defer close(w.Quit)
+       // consumer Event set upstreamID
+       upstream := event.Obj.(*v1.Upstream)
+       glog.V(2).Infof("2.service trigger from %s, %s", event.Op, 
*upstream.Name)
+
+       w.UpstreamId = upstream.ID
+       // add to queue
+       services := []*v1.Service{w.Service}
+       sqo := &ServiceQueueObj{Services: services, RouteWorkerGroup: *rwg}
+       sqo.AddQueue()
+
+       //op := Update
+       //// padding
+       //currentService, _ := apisix.FindCurrentService(*w.Service.Name)
+       //paddingService(w.Service, currentService)
+       //// diff
+       //hasDiff, err := utils.HasDiff(w.Service, currentService)
+       //// sync
+       //if err != nil {
+       //      return err
+       //}
+       //if hasDiff {
+       //      if *w.Service.ID == strconv.Itoa(0) {
+       //              op = Create
+       //              // 1. sync apisix and get id
+       //              if serviceResponse, err := apisix.AddService(w.Service, 
conf.BaseUrl); err != nil {
+       //                      // todo log error
+       //                      glog.Info(err.Error())
+       //              }else {
+       //                      tmp := 
strings.Split(*serviceResponse.Service.Key, "/")
+       //                      *w.Service.ID = tmp[len(tmp) - 1]
+       //              }
+       //              // 2. sync memDB
+       //              db := &db.ServiceDB{Services: []*v1.Service{w.Service}}
+       //              db.Insert()
+       //              glog.Infof("create service %s, %s", *w.Name, 
*w.UpstreamId)
+       //      }else {
+       //              op = Update
+       //              // 1. sync memDB
+       //              db := db.ServiceDB{Services: []*v1.Service{w.Service}}
+       //              if err := db.UpdateService(); err != nil {
+       //                      // todo log error
+       //              }
+       //              // 2. sync apisix
+       //              apisix.UpdateService(w.Service, conf.BaseUrl)
+       //              glog.Infof("update service %s, %s", *w.Name, 
*w.UpstreamId)
+       //      }
+       //}
+       //// broadcast to route
+       //routeWorkers := (*rwg)[*w.Service.Name]
+       //for _, rw := range routeWorkers{
+       //      event := &Event{Kind: ServiceKind, Op: op, Obj: w.Service}
+       //      glog.Infof("send event %s, %s, %s", event.Kind, event.Op, 
*w.Service.Name)
+       //      rw.Event <- *event
+       //}
+       return nil
+}
+
+func SolverService(services []*v1.Service, rwg RouteWorkerGroup) error {
+       for _, svc := range services {
+               op := Update
+               // padding
+               currentService, _ := apisix.FindCurrentService(*svc.Group, 
*svc.Name, *svc.FullName)
+               paddingService(svc, currentService)
+               // diff
+               hasDiff, err := utils.HasDiff(svc, currentService)
+               // sync
+               if err != nil {
+                       return err
+               }
+               if hasDiff {
+                       if *svc.ID == strconv.Itoa(0) {
+                               op = Create
+                               // 1. sync apisix and get id
+                               if serviceResponse, err := 
apisix.AddService(svc); err != nil {
+                                       // todo log error
+                                       glog.V(2).Info(err.Error())
+                               } else {
+                                       tmp := 
strings.Split(*serviceResponse.Service.Key, "/")
+                                       *svc.ID = tmp[len(tmp)-1]
+                               }
+                               // 2. sync memDB
+                               db := &db.ServiceDB{Services: 
[]*v1.Service{svc}}
+                               db.Insert()
+                               glog.V(2).Infof("create service %s, %s", 
*svc.Name, *svc.UpstreamId)
+                       } else {
+                               op = Update
+                               needToUpdate := true
+                               if currentService.FromKind != nil && 
*(currentService.FromKind) == ApisixService { // update from ApisixUpstream
+                                       if svc.FromKind == nil || (svc.FromKind 
!= nil && *(svc.FromKind) != ApisixService) {
+                                               // currentService > svc
+                                               // set lb && health check
+                                               needToUpdate = false
+                                       }
+                               }
+                               if needToUpdate {
+                                       // 1. sync memDB
+                                       db := db.ServiceDB{Services: 
[]*v1.Service{svc}}
+                                       if err := db.UpdateService(); err != 
nil {
+                                               // todo log error
+                                       }
+                                       // 2. sync apisix
+                                       apisix.UpdateService(svc)
+                                       glog.V(2).Infof("update service %s, 
%s", *svc.Name, *svc.UpstreamId)
+                               }
+
+                       }
+               }
+               // broadcast to route
+               routeWorkers := rwg[*svc.Name]
+               for _, rw := range routeWorkers {
+                       event := &Event{Kind: ServiceKind, Op: op, Obj: svc}
+                       glog.V(2).Infof("send event %s, %s, %s", event.Kind, 
event.Op, *svc.Name)
+                       rw.Event <- *event
+               }
+       }
+       return nil
+}
+
+func (swg *ServiceWorkerGroup) Add(key string, s *serviceWorker) {
+       sws := (*swg)[key]
+       if sws == nil {
+               sws = make([]*serviceWorker, 0)
+       }
+       sws = append(sws, s)
+       (*swg)[key] = sws
+}
+
+func (swg *ServiceWorkerGroup) Delete(key string, s *serviceWorker) {
+       sws := (*swg)[key]
+       result := make([]*serviceWorker, 0)
+       for _, r := range sws {
+               if r.Name != s.Name {
+                       result = append(result, r)
+               }
+       }
+       (*swg)[key] = result
+}
diff --git a/pkg/seven/state/solver.go b/pkg/seven/state/solver.go
new file mode 100644
index 0000000..392680a
--- /dev/null
+++ b/pkg/seven/state/solver.go
@@ -0,0 +1,129 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package state
+
+import (
+       "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+       "github.com/api7/ingress-controller/pkg/seven/apisix"
+       "github.com/api7/ingress-controller/pkg/seven/db"
+)
+
+var UpstreamQueue chan UpstreamQueueObj
+var ServiceQueue chan ServiceQueueObj
+
+func init() {
+       UpstreamQueue = make(chan UpstreamQueueObj, 500)
+       ServiceQueue = make(chan ServiceQueueObj, 500)
+       go WatchUpstream()
+       go WatchService()
+}
+
+func WatchService() {
+       for {
+               sqo := <-ServiceQueue
+               // solver service
+               SolverService(sqo.Services, sqo.RouteWorkerGroup)
+       }
+}
+
+func WatchUpstream() {
+       for {
+               uqo := <-UpstreamQueue
+               SolverUpstream(uqo.Upstreams, uqo.ServiceWorkerGroup)
+       }
+}
+
+// Solver
+func (s *ApisixCombination) Solver() (bool, error) {
+       // 1.route workers
+       rwg := NewRouteWorkers(s.Routes)
+       // 2.service workers
+       swg := NewServiceWorkers(s.Services, &rwg)
+       //sqo := &ServiceQueueObj{Services: s.Services, RouteWorkerGroup: rwg}
+       //sqo.AddQueue()
+       // 3.upstream workers
+       uqo := &UpstreamQueueObj{Upstreams: s.Upstreams, ServiceWorkerGroup: 
swg}
+       uqo.AddQueue()
+       return true, nil
+}
+
+// UpstreamQueueObj for upstream queue
+type UpstreamQueueObj struct {
+       Upstreams          []*v1.Upstream
+       ServiceWorkerGroup ServiceWorkerGroup
+}
+
+// AddQueue make upstreams in order
+// upstreams is group by CRD
+func (uqo *UpstreamQueueObj) AddQueue() {
+       UpstreamQueue <- *uqo
+}
+
+type ServiceQueueObj struct {
+       Services         []*v1.Service
+       RouteWorkerGroup RouteWorkerGroup
+}
+
+// AddQueue make upstreams in order
+// upstreams is group by CRD
+func (sqo *ServiceQueueObj) AddQueue() {
+       ServiceQueue <- *sqo
+}
+
+// Sync remove from apisix
+func (rc *RouteCompare) Sync() error {
+       for _, old := range rc.OldRoutes {
+               needToDel := true
+               for _, nr := range rc.NewRoutes {
+                       if old.Name == nr.Name {
+                               needToDel = false
+                               break
+                       }
+               }
+               if needToDel {
+                       fullName := *old.Name
+                       if *old.Group != "" {
+                               fullName = *old.Group + "_" + *old.Name
+                       }
+                       request := db.RouteRequest{Name: *old.Name, FullName: 
fullName}
+
+                       if route, err := request.FindByName(); err != nil {
+                               // log error
+                       } else {
+                               if err = apisix.DeleteRoute(route); err == nil {
+                                       db := db.RouteDB{Routes: 
[]*v1.Route{route}}
+                                       db.DeleteRoute()
+                               }
+                       }
+               }
+       }
+       return nil
+}
+
+func SyncSsl(ssl *v1.Ssl, method string) error {
+       switch method {
+       case Create:
+               _, err := apisix.AddOrUpdateSsl(ssl)
+               return err
+       case Update:
+               _, err := apisix.AddOrUpdateSsl(ssl)
+               return err
+       case Delete:
+               err := apisix.DeleteSsl(ssl)
+               return err
+       }
+       return nil
+}
diff --git a/pkg/seven/state/sync.go b/pkg/seven/state/sync.go
new file mode 100644
index 0000000..2de068d
--- /dev/null
+++ b/pkg/seven/state/sync.go
@@ -0,0 +1,15 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package state
diff --git a/pkg/seven/state/upstream_worker.go 
b/pkg/seven/state/upstream_worker.go
new file mode 100644
index 0000000..3f33ce6
--- /dev/null
+++ b/pkg/seven/state/upstream_worker.go
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package state
+
+import "github.com/gxthrj/apisix-types/pkg/apis/apisix/v1"
+
+type upstreamWorker struct {
+       *v1.Upstream
+       Event chan Event
+       Quit  chan Quit
+}
diff --git a/pkg/ingress/apisix/plugin.go b/pkg/seven/utils/diff.go
similarity index 50%
copy from pkg/ingress/apisix/plugin.go
copy to pkg/seven/utils/diff.go
index 4feb097..10a74ea 100644
--- a/pkg/ingress/apisix/plugin.go
+++ b/pkg/seven/utils/diff.go
@@ -12,40 +12,47 @@
 // 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 apisix
+package utils
 
 import (
-       "github.com/gxthrj/seven/apisix"
-       "strconv"
+       "encoding/json"
+       "github.com/golang/glog"
+       "github.com/yudai/gojsondiff"
 )
 
-type CorsYaml struct {
-       Enable       bool   `json:"enable,omitempty"`
-       AllowOrigin  string `json:"allow_origin,omitempty"`
-       AllowHeaders string `json:"allow_headers,omitempty"`
-       AllowMethods string `json:"allow_methods,omitempty"`
-}
+var (
+       differ = gojsondiff.New()
+)
 
-func (c *CorsYaml) SetEnable(enable string) {
-       if b, err := strconv.ParseBool(enable); err != nil {
-               c.Enable = false
+func HasDiff(a, b interface{}) (bool, error) {
+       aJSON, err := json.Marshal(a)
+       if err != nil {
+               return false, err
+       }
+       bJSON, err := json.Marshal(b)
+       if err != nil {
+               return false, err
+       }
+       if d, err := differ.Compare(aJSON, bJSON); err != nil {
+               return false, err
        } else {
-               c.Enable = b
+               glog.V(2).Info(d.Deltas())
+               return d.Modified(), nil
        }
 }
 
-func (c *CorsYaml) SetOrigin(origin string) {
-       c.AllowOrigin = origin
-}
-
-func (c *CorsYaml) SetHeaders(headers string) {
-       c.AllowHeaders = headers
-}
-func (c *CorsYaml) SetMethods(methods string) {
-       c.AllowMethods = methods
-}
-
-func (c *CorsYaml) Build() *apisix.Cors {
-       maxAge := int64(3600)
-       return apisix.BuildCors(c.Enable, &c.AllowOrigin, &c.AllowHeaders, 
&c.AllowMethods, &maxAge)
+func Diff(a, b interface{}) (gojsondiff.Diff, error) {
+       aJSON, err := json.Marshal(a)
+       if err != nil {
+               return nil, err
+       }
+       bJSON, err := json.Marshal(b)
+       if err != nil {
+               return nil, err
+       }
+       if d, err := differ.Compare(aJSON, bJSON); err != nil {
+               return nil, err
+       } else {
+               return d, nil
+       }
 }
diff --git a/pkg/seven/utils/http.go b/pkg/seven/utils/http.go
new file mode 100644
index 0000000..9b5a76a
--- /dev/null
+++ b/pkg/seven/utils/http.go
@@ -0,0 +1,88 @@
+// 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 utils
+
+import (
+       "fmt"
+       "net/http"
+       "time"
+
+       "gopkg.in/resty.v1"
+)
+
+const timeout = 3000
+
+func Post(url string, bytes []byte) ([]byte, error) {
+       r := resty.New().
+               SetTimeout(time.Duration(timeout)*time.Millisecond).
+               R().
+               SetHeader("content-type", "application/json")
+       r.SetBody(bytes)
+       resp, err := r.Post(url)
+       if err != nil {
+               return nil, err
+       }
+       if resp.StatusCode() != http.StatusOK && resp.StatusCode() != 
http.StatusCreated {
+               return nil, fmt.Errorf("status: %d, body: %s", 
resp.StatusCode(), resp.Body())
+       }
+       return resp.Body(), nil
+}
+
+func Put(url string, bytes []byte) ([]byte, error) {
+       r := resty.New().
+               SetTimeout(time.Duration(timeout)*time.Millisecond).
+               R().
+               SetHeader("content-type", "application/json")
+       r.SetBody(bytes)
+       resp, err := r.Put(url)
+       if err != nil {
+               return nil, err
+       }
+       if resp.StatusCode() != http.StatusOK && resp.StatusCode() != 
http.StatusCreated {
+               return nil, fmt.Errorf("status: %d, body: %s", 
resp.StatusCode(), resp.Body())
+       }
+       return resp.Body(), nil
+}
+
+func Patch(url string, bytes []byte) ([]byte, error) {
+       r := resty.New().
+               SetTimeout(time.Duration(timeout)*time.Millisecond).
+               R().
+               SetHeader("content-type", "application/json")
+       r.SetBody(bytes)
+       resp, err := r.Patch(url)
+       if err != nil {
+               return nil, err
+       }
+       if resp.StatusCode() != http.StatusOK && resp.StatusCode() != 
http.StatusCreated {
+               return nil, fmt.Errorf("status: %d, body: %s", 
resp.StatusCode(), resp.Body())
+       }
+       return resp.Body(), nil
+}
+
+func Delete(url string) ([]byte, error) {
+       r := resty.New().
+               SetTimeout(time.Duration(timeout)*time.Millisecond).
+               R().
+               SetHeader("content-type", "application/json")
+       resp, err := r.Delete(url)
+       if err != nil {
+               return nil, err
+       }
+       if resp.StatusCode() != http.StatusOK && resp.StatusCode() != 
http.StatusNotFound {
+               return nil, fmt.Errorf("status: %d, body: %s", 
resp.StatusCode(), resp.Body())
+       }
+       return resp.Body(), nil
+}
diff --git a/pkg/seven/utils/types.go b/pkg/seven/utils/types.go
new file mode 100644
index 0000000..40863de
--- /dev/null
+++ b/pkg/seven/utils/types.go
@@ -0,0 +1,15 @@
+// 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 utils

Reply via email to