You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by to...@apache.org on 2021/04/13 03:16:32 UTC

[apisix-ingress-controller] branch master updated: chore: controller tree (#361)

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

tokers 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 2dd7f14  chore: controller tree (#361)
2dd7f14 is described below

commit 2dd7f143343410a610d7dd2fcf1cdd08f0131611
Author: Alex Zhang <zc...@gmail.com>
AuthorDate: Tue Apr 13 11:16:23 2021 +0800

    chore: controller tree (#361)
---
 .github/workflows/e2e-test-ci.yml               |   2 +-
 cmd/ingress/ingress.go                          |   2 +-
 pkg/apisix/ssl.go                               |   1 +
 pkg/ingress/apisix/tls.go                       |  66 ------
 pkg/ingress/apisix/tls_test.go                  | 133 -------------
 pkg/ingress/{controller => }/apisix_route.go    |   2 +-
 pkg/ingress/apisix_tls.go                       | 240 ++++++++++++++++++++++
 pkg/ingress/{controller => }/apisix_upstream.go |   2 +-
 pkg/ingress/{controller => }/controller.go      |  82 ++++----
 pkg/ingress/controller/apisix_tls.go            | 254 ------------------------
 pkg/ingress/{controller => }/endpoint.go        |   2 +-
 pkg/ingress/{controller => }/ingress.go         |   2 +-
 pkg/ingress/{controller => }/ingress_test.go    |   2 +-
 pkg/ingress/{controller => }/manifest.go        |   2 +-
 pkg/ingress/{controller => }/manifest_test.go   |   2 +-
 pkg/ingress/{controller => }/secret.go          | 116 ++++++-----
 pkg/ingress/{controller => }/types.go           |   2 +-
 pkg/kube/translation/apisix_ssl.go              |  58 ++++++
 pkg/kube/translation/translator.go              |   3 +
 pkg/seven/apisix/event.go                       |  23 ---
 pkg/seven/conf/conf.go                          |  50 -----
 pkg/seven/conf/conf_test.go                     |  24 ---
 pkg/seven/state/event.go                        |  21 --
 pkg/seven/state/solver.go                       |  46 -----
 pkg/seven/utils/diff.go                         |  59 ------
 pkg/seven/utils/http.go                         |  88 --------
 pkg/seven/utils/types.go                        |  20 --
 test/e2e/ingress/secret.go                      |   4 +-
 28 files changed, 423 insertions(+), 885 deletions(-)

diff --git a/.github/workflows/e2e-test-ci.yml b/.github/workflows/e2e-test-ci.yml
index d6f093e..6faafea 100644
--- a/.github/workflows/e2e-test-ci.yml
+++ b/.github/workflows/e2e-test-ci.yml
@@ -28,7 +28,7 @@ jobs:
       - name: Run e2e test cases
         working-directory: ./
         run: |
-          make e2e-test E2E_CONCURRENCY=2
+          make e2e-test E2E_CONCURRENCY=1
       - name: upload coverage profile
         working-directory: ./test/e2e
         run: |
diff --git a/cmd/ingress/ingress.go b/cmd/ingress/ingress.go
index c2a4b3a..f90b50f 100644
--- a/cmd/ingress/ingress.go
+++ b/cmd/ingress/ingress.go
@@ -26,7 +26,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/apache/apisix-ingress-controller/pkg/config"
-	"github.com/apache/apisix-ingress-controller/pkg/ingress/controller"
+	controller "github.com/apache/apisix-ingress-controller/pkg/ingress"
 	"github.com/apache/apisix-ingress-controller/pkg/log"
 	"github.com/apache/apisix-ingress-controller/pkg/version"
 )
diff --git a/pkg/apisix/ssl.go b/pkg/apisix/ssl.go
index 684479a..7069c48 100644
--- a/pkg/apisix/ssl.go
+++ b/pkg/apisix/ssl.go
@@ -144,6 +144,7 @@ func (s *sslClient) Create(ctx context.Context, obj *v1.Ssl) (*v1.Ssl, error) {
 		return nil, err
 	}
 	data, err := json.Marshal(v1.Ssl{
+		ID:     obj.ID,
 		Snis:   obj.Snis,
 		Cert:   obj.Cert,
 		Key:    obj.Key,
diff --git a/pkg/ingress/apisix/tls.go b/pkg/ingress/apisix/tls.go
deleted file mode 100644
index 7ef31fa..0000000
--- a/pkg/ingress/apisix/tls.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package apisix
-
-import (
-	"context"
-
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	ingressConf "github.com/apache/apisix-ingress-controller/pkg/kube"
-	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
-	apisix "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
-)
-
-type ApisixTLSCRD configv1.ApisixTls
-
-// Convert convert to  apisix.Ssl from ingress.ApisixTls CRD
-func (as *ApisixTLSCRD) Convert(sc Secreter) (*apisix.Ssl, error) {
-	name := as.Name
-	namespace := as.Namespace
-
-	id := namespace + "_" + name
-	secretName := as.Spec.Secret.Name
-	secretNamespace := as.Spec.Secret.Namespace
-	secret, err := sc.FindByName(secretNamespace, secretName)
-	if err != nil {
-		return nil, err
-	}
-	cert := string(secret.Data["cert"])
-	key := string(secret.Data["key"])
-	status := 1
-	var snis []string
-	snis = append(snis, as.Spec.Hosts...)
-	ssl := &apisix.Ssl{
-		ID:     id,
-		Snis:   snis,
-		Cert:   cert,
-		Key:    key,
-		Status: status,
-	}
-	return ssl, nil
-}
-
-type Secreter interface {
-	FindByName(namespace, name string) (*v1.Secret, error)
-}
-
-type SecretClient struct{}
-
-func (sc *SecretClient) FindByName(namespace, name string) (*v1.Secret, error) {
-	clientSet := ingressConf.GetKubeClient()
-	return clientSet.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
-}
diff --git a/pkg/ingress/apisix/tls_test.go b/pkg/ingress/apisix/tls_test.go
deleted file mode 100644
index 4f5a3a1..0000000
--- a/pkg/ingress/apisix/tls_test.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package apisix
-
-import (
-	"encoding/json"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"gopkg.in/yaml.v2"
-	v1 "k8s.io/api/core/v1"
-
-	apisixhttp "github.com/apache/apisix-ingress-controller/pkg/apisix"
-	"github.com/apache/apisix-ingress-controller/pkg/seven/conf"
-	"github.com/apache/apisix-ingress-controller/pkg/seven/utils"
-	apisix "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
-)
-
-func TestConvert(t *testing.T) {
-	atlsStr := `
-apiVersion: apisix.apache.org/v1
-kind: ApisixTls
-metadata:
-  name: foo
-  namespace: helm
-spec:
-  hosts:
-  - api6.com
-  secret:
-    name: test-atls
-    namespace: helm
-`
-	id := "helm_foo"
-	snis := []string{"api6.com"}
-	status := 1
-	cert := "root"
-	key := "123456"
-	group := ""
-	sslExpect := &apisix.Ssl{
-		ID:     id,
-		Snis:   snis,
-		Cert:   cert,
-		Key:    key,
-		Status: status,
-		Group:  group,
-	}
-	atlsCRD := &ApisixTLSCRD{}
-	err := yaml.Unmarshal([]byte(atlsStr), atlsCRD)
-	assert.Nil(t, err, "yaml decode failed")
-	sc := &SecretClientMock{}
-	ssl, err := atlsCRD.Convert(sc)
-	assert.Nil(t, err)
-	assert.EqualValues(t, sslExpect.Key, ssl.Key, "key convert error")
-	assert.EqualValues(t, sslExpect.ID, ssl.ID, "id convert error")
-	assert.EqualValues(t, sslExpect.Cert, ssl.Cert, "cert convert error")
-	assert.EqualValues(t, sslExpect.Snis, ssl.Snis, "snis convert error")
-	assert.EqualValues(t, sslExpect.Group, ssl.Group, "group convert error")
-}
-
-func TestConvert_Error(t *testing.T) {
-	atlsStr := `
-apiVersion: apisix.apache.org/v1
-kind: ApisixTls
-metadata:
-  name: foo
-  namespace: helm
-spec:
-  secret:
-    name: test-atls
-    namespace: helm
-`
-	setDummyApisixClient(t)
-	atlsCRD := &ApisixTLSCRD{}
-	err := yaml.Unmarshal([]byte(atlsStr), atlsCRD)
-	assert.Nil(t, err, "yaml decode failed")
-	sc := &SecretClientErrorMock{}
-	ssl, err := atlsCRD.Convert(sc)
-	assert.Nil(t, ssl)
-	assert.NotNil(t, err)
-}
-
-type SecretClientMock struct{}
-
-func (sc *SecretClientMock) FindByName(namespace, name string) (*v1.Secret, error) {
-	secretStr := `
-{
-  "apiVersion": "v1",
-  "kind": "Secret",
-  "metadata": {
-    "name": "test-atls",
-    "namespace": "helm"
-  },
-  "data": {
-    "cert": "cm9vdA==",
-    "key": "MTIzNDU2"
-  }
-}
-`
-	secret := &v1.Secret{}
-	if err := json.Unmarshal([]byte(secretStr), secret); err != nil {
-		return nil, err
-	}
-	return secret, nil
-}
-
-type SecretClientErrorMock struct{}
-
-func (sc *SecretClientErrorMock) FindByName(namespace, name string) (*v1.Secret, error) {
-	return nil, utils.ErrNotFound
-}
-
-func setDummyApisixClient(t *testing.T) {
-	cli, err := apisixhttp.NewClient()
-	assert.Nil(t, err)
-	err = cli.AddCluster(&apisixhttp.ClusterOptions{
-		Name:    "",
-		BaseURL: "http://127.0.0.2:9080/apisix/admin",
-	})
-	assert.Nil(t, err)
-	conf.SetAPISIXClient(cli)
-}
diff --git a/pkg/ingress/controller/apisix_route.go b/pkg/ingress/apisix_route.go
similarity index 99%
rename from pkg/ingress/controller/apisix_route.go
rename to pkg/ingress/apisix_route.go
index 36aded8..fd4ad5a 100644
--- a/pkg/ingress/controller/apisix_route.go
+++ b/pkg/ingress/apisix_route.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"context"
diff --git a/pkg/ingress/apisix_tls.go b/pkg/ingress/apisix_tls.go
new file mode 100644
index 0000000..4e439df
--- /dev/null
+++ b/pkg/ingress/apisix_tls.go
@@ -0,0 +1,240 @@
+// 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 ingress
+
+import (
+	"context"
+	"sync"
+	"time"
+
+	"go.uber.org/zap"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/client-go/tools/cache"
+	"k8s.io/client-go/util/workqueue"
+
+	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	"github.com/apache/apisix-ingress-controller/pkg/log"
+	"github.com/apache/apisix-ingress-controller/pkg/types"
+	v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+type apisixTlsController struct {
+	controller *Controller
+	workqueue  workqueue.RateLimitingInterface
+	workers    int
+}
+
+func (c *Controller) newApisixTlsController() *apisixTlsController {
+	ctl := &apisixTlsController{
+		controller: c,
+		workqueue:  workqueue.NewNamedRateLimitingQueue(workqueue.NewItemFastSlowRateLimiter(1*time.Second, 60*time.Second, 5), "ApisixTls"),
+		workers:    1,
+	}
+
+	ctl.controller.apisixTlsInformer.AddEventHandler(
+		cache.ResourceEventHandlerFuncs{
+			AddFunc:    ctl.onAdd,
+			UpdateFunc: ctl.onUpdate,
+			DeleteFunc: ctl.onDelete,
+		},
+	)
+	return ctl
+}
+
+func (c *apisixTlsController) run(ctx context.Context) {
+	log.Info("ApisixTls controller started")
+	defer log.Info("ApisixTls controller exited")
+	if ok := cache.WaitForCacheSync(ctx.Done(), c.controller.apisixTlsInformer.HasSynced, c.controller.secretInformer.HasSynced); !ok {
+		log.Errorf("informers sync failed")
+		return
+	}
+	for i := 0; i < c.workers; i++ {
+		go c.runWorker(ctx)
+	}
+
+	<-ctx.Done()
+	c.workqueue.ShutDown()
+}
+
+func (c *apisixTlsController) runWorker(ctx context.Context) {
+	for {
+		obj, quit := c.workqueue.Get()
+		if quit {
+			return
+		}
+		err := c.sync(ctx, obj.(*types.Event))
+		c.workqueue.Done(obj)
+		c.handleSyncErr(obj, err)
+	}
+}
+
+func (c *apisixTlsController) sync(ctx context.Context, ev *types.Event) error {
+	key := ev.Object.(string)
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		log.Errorf("found ApisixTls resource with invalid meta namespace key %s: %s", key, err)
+		return err
+	}
+
+	tls, err := c.controller.apisixTlsLister.ApisixTlses(namespace).Get(name)
+	if err != nil {
+		if !k8serrors.IsNotFound(err) {
+			log.Errorf("failed to get ApisixTls %s: %s", key, err)
+			return err
+		}
+		if ev.Type != types.EventDelete {
+			log.Warnf("ApisixTls %s was deleted before it can be delivered", key)
+			// Don't need to retry.
+			return nil
+		}
+	}
+	if ev.Type == types.EventDelete {
+		if tls != nil {
+			// We still find the resource while we are processing the DELETE event,
+			// that means object with same namespace and name was created, discarding
+			// this stale DELETE event.
+			log.Warnf("discard the stale ApisixTls delete event since the %s exists", key)
+			return nil
+		}
+		tls = ev.Tombstone.(*configv1.ApisixTls)
+	}
+
+	ssl, err := c.controller.translator.TranslateSSL(tls)
+	if err != nil {
+		log.Errorw("failed to translate ApisixTls",
+			zap.Error(err),
+			zap.Any("ApisixTls", tls),
+		)
+		return err
+	}
+	log.Debug("got SSL object from ApisixTls",
+		zap.Any("ssl", ssl),
+		zap.Any("ApisixTls", tls),
+	)
+
+	secretKey := tls.Spec.Secret.Namespace + "_" + tls.Spec.Secret.Name
+	c.syncSecretSSL(secretKey, ssl, ev.Type)
+
+	if err := c.controller.syncSSL(ctx, ssl, ev.Type); err != nil {
+		log.Errorw("failed to sync SSL to APISIX",
+			zap.Error(err),
+			zap.Any("ssl", ssl),
+		)
+		return err
+	}
+	return err
+}
+
+func (c *apisixTlsController) syncSecretSSL(key string, ssl *v1.Ssl, event types.EventType) {
+	if ssls, ok := c.controller.secretSSLMap.Load(key); ok {
+		sslMap := ssls.(*sync.Map)
+		switch event {
+		case types.EventDelete:
+			sslMap.Delete(ssl.ID)
+			c.controller.secretSSLMap.Store(key, sslMap)
+		default:
+			sslMap.Store(ssl.ID, ssl)
+			c.controller.secretSSLMap.Store(key, sslMap)
+		}
+	} else if event != types.EventDelete {
+		sslMap := new(sync.Map)
+		sslMap.Store(ssl.ID, ssl)
+		c.controller.secretSSLMap.Store(key, sslMap)
+	}
+}
+
+func (c *apisixTlsController) handleSyncErr(obj interface{}, err error) {
+	if err == nil {
+		c.workqueue.Forget(obj)
+		return
+	}
+	log.Warnw("sync ApisixTls failed, will retry",
+		zap.Any("object", obj),
+		zap.Error(err),
+	)
+	c.workqueue.AddRateLimited(obj)
+}
+
+func (c *apisixTlsController) onAdd(obj interface{}) {
+	key, err := cache.MetaNamespaceKeyFunc(obj)
+	if err != nil {
+		log.Errorf("found ApisixTls object with bad namespace/name: %s, ignore it", err)
+		return
+	}
+	if !c.controller.namespaceWatching(key) {
+		return
+	}
+	log.Debugw("ApisixTls add event arrived",
+		zap.Any("object", obj),
+	)
+	c.workqueue.AddRateLimited(&types.Event{
+		Type:   types.EventAdd,
+		Object: key,
+	})
+}
+
+func (c *apisixTlsController) onUpdate(prev, curr interface{}) {
+	oldTls := prev.(*configv1.ApisixTls)
+	newTls := curr.(*configv1.ApisixTls)
+	if oldTls.GetResourceVersion() == newTls.GetResourceVersion() {
+		return
+	}
+	key, err := cache.MetaNamespaceKeyFunc(curr)
+	if err != nil {
+		log.Errorf("found ApisixTls object with bad namespace/name: %s, ignore it", err)
+		return
+	}
+	if !c.controller.namespaceWatching(key) {
+		return
+	}
+	log.Debugw("ApisixTls update event arrived",
+		zap.Any("new object", curr),
+		zap.Any("old object", prev),
+	)
+	c.workqueue.AddRateLimited(&types.Event{
+		Type:   types.EventUpdate,
+		Object: key,
+	})
+}
+
+func (c *apisixTlsController) onDelete(obj interface{}) {
+	tls, ok := obj.(*configv1.ApisixTls)
+	if !ok {
+		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
+		if !ok {
+			return
+		}
+		tls, ok = tombstone.Obj.(*configv1.ApisixTls)
+		if !ok {
+			return
+		}
+	}
+	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
+	if err != nil {
+		log.Errorf("found ApisixTls resource with bad meta namespace key: %s", err)
+		return
+	}
+	if !c.controller.namespaceWatching(key) {
+		return
+	}
+	log.Debugw("ApisixTls delete event arrived",
+		zap.Any("final state", obj),
+	)
+	c.workqueue.AddRateLimited(&types.Event{
+		Type:      types.EventDelete,
+		Object:    key,
+		Tombstone: tls,
+	})
+}
diff --git a/pkg/ingress/controller/apisix_upstream.go b/pkg/ingress/apisix_upstream.go
similarity index 99%
rename from pkg/ingress/controller/apisix_upstream.go
rename to pkg/ingress/apisix_upstream.go
index 75dd1ba..28b2c9f 100644
--- a/pkg/ingress/controller/apisix_upstream.go
+++ b/pkg/ingress/apisix_upstream.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"context"
diff --git a/pkg/ingress/controller/controller.go b/pkg/ingress/controller.go
similarity index 89%
rename from pkg/ingress/controller/controller.go
rename to pkg/ingress/controller.go
index 35d46f9..a29cbbb 100644
--- a/pkg/ingress/controller/controller.go
+++ b/pkg/ingress/controller.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"context"
@@ -24,7 +24,6 @@ import (
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/client-go/informers"
 	"k8s.io/client-go/kubernetes"
 	listerscorev1 "k8s.io/client-go/listers/core/v1"
 	"k8s.io/client-go/tools/cache"
@@ -35,23 +34,16 @@ import (
 	"github.com/apache/apisix-ingress-controller/pkg/apisix"
 	"github.com/apache/apisix-ingress-controller/pkg/config"
 	"github.com/apache/apisix-ingress-controller/pkg/kube"
-	clientset "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned"
 	crdclientset "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned"
 	"github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions"
 	listersv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v1"
 	"github.com/apache/apisix-ingress-controller/pkg/kube/translation"
 	"github.com/apache/apisix-ingress-controller/pkg/log"
 	"github.com/apache/apisix-ingress-controller/pkg/metrics"
-	"github.com/apache/apisix-ingress-controller/pkg/seven/conf"
+	"github.com/apache/apisix-ingress-controller/pkg/types"
+	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
 
-// recover any exception
-func recoverException() {
-	if err := recover(); err != nil {
-		log.Error(err)
-	}
-}
-
 // Controller is the ingress apisix controller object.
 type Controller struct {
 	name               string
@@ -66,6 +58,9 @@ type Controller struct {
 	crdClientset       crdclientset.Interface
 	metricsCollector   metrics.Collector
 	crdInformerFactory externalversions.SharedInformerFactory
+	// this map enrolls which ApisixTls objects refer to a Kubernetes
+	// Secret object.
+	secretSSLMap *sync.Map
 
 	// common informers and listers
 	epInformer             cache.SharedIndexInformer
@@ -80,6 +75,8 @@ type Controller struct {
 	apisixUpstreamLister   listersv1.ApisixUpstreamLister
 	apisixRouteLister      kube.ApisixRouteLister
 	apisixRouteInformer    cache.SharedIndexInformer
+	apisixTlsLister        listersv1.ApisixTlsLister
+	apisixTlsInformer      cache.SharedIndexInformer
 
 	// resource controllers
 	endpointsController *endpointsController
@@ -88,6 +85,7 @@ type Controller struct {
 
 	apisixUpstreamController *apisixUpstreamController
 	apisixRouteController    *apisixRouteController
+	apisixTlsController      *apisixTlsController
 }
 
 // NewController creates an ingress apisix controller object.
@@ -101,7 +99,6 @@ func NewController(cfg *config.Config) (*Controller, error) {
 	if err != nil {
 		return nil, err
 	}
-	conf.SetAPISIXClient(client)
 
 	if err := kube.InitInformer(cfg); err != nil {
 		return nil, err
@@ -160,6 +157,7 @@ func NewController(cfg *config.Config) (*Controller, error) {
 		crdClientset:       crdClientset,
 		crdInformerFactory: sharedInformerFactory,
 		watchingNamespace:  watchingNamespace,
+		secretSSLMap:       new(sync.Map),
 
 		epInformer:             kube.CoreSharedInformerFactory.Core().V1().Endpoints().Informer(),
 		epLister:               kube.CoreSharedInformerFactory.Core().V1().Endpoints().Lister(),
@@ -173,16 +171,20 @@ func NewController(cfg *config.Config) (*Controller, error) {
 		apisixRouteLister:      apisixRouteLister,
 		apisixUpstreamInformer: sharedInformerFactory.Apisix().V1().ApisixUpstreams().Informer(),
 		apisixUpstreamLister:   sharedInformerFactory.Apisix().V1().ApisixUpstreams().Lister(),
+		apisixTlsInformer:      sharedInformerFactory.Apisix().V1().ApisixTlses().Informer(),
+		apisixTlsLister:        sharedInformerFactory.Apisix().V1().ApisixTlses().Lister(),
 	}
 	c.translator = translation.NewTranslator(&translation.TranslatorOptions{
 		EndpointsLister:      c.epLister,
 		ServiceLister:        c.svcLister,
 		ApisixUpstreamLister: c.apisixUpstreamLister,
+		SecretLister:         c.secretLister,
 	})
 
 	c.endpointsController = c.newEndpointsController()
 	c.apisixUpstreamController = c.newApisixUpstreamController()
 	c.apisixRouteController = c.newApisixRouteController()
+	c.apisixTlsController = c.newApisixTlsController()
 	c.ingressController = c.newIngressController()
 	c.secretController = c.newSecretController()
 
@@ -307,6 +309,18 @@ func (c *Controller) run(ctx context.Context) {
 		c.ingressInformer.Run(ctx.Done())
 	})
 	c.goAttach(func() {
+		c.apisixRouteInformer.Run(ctx.Done())
+	})
+	c.goAttach(func() {
+		c.apisixUpstreamInformer.Run(ctx.Done())
+	})
+	c.goAttach(func() {
+		c.secretInformer.Run(ctx.Done())
+	})
+	c.goAttach(func() {
+		c.apisixTlsInformer.Run(ctx.Done())
+	})
+	c.goAttach(func() {
 		c.endpointsController.run(ctx)
 	})
 	c.goAttach(func() {
@@ -319,27 +333,12 @@ func (c *Controller) run(ctx context.Context) {
 		c.apisixRouteController.run(ctx)
 	})
 	c.goAttach(func() {
-		c.secretInformer.Run(ctx.Done())
+		c.apisixTlsController.run(ctx)
 	})
 	c.goAttach(func() {
 		c.secretController.run(ctx)
 	})
 
-	ac := &Api6Controller{
-		KubeClientSet:             c.clientset,
-		Api6ClientSet:             c.crdClientset,
-		SharedInformerFactory:     c.crdInformerFactory,
-		CoreSharedInformerFactory: kube.CoreSharedInformerFactory,
-		Stop:                      ctx.Done(),
-	}
-
-	// ApisixTLS
-	ac.ApisixTLS(c)
-
-	c.goAttach(func() {
-		ac.SharedInformerFactory.Start(ctx.Done())
-	})
-
 	<-ctx.Done()
 	c.wg.Wait()
 }
@@ -362,21 +361,16 @@ func (c *Controller) namespaceWatching(key string) (ok bool) {
 	return
 }
 
-type Api6Controller struct {
-	KubeClientSet             kubernetes.Interface
-	Api6ClientSet             clientset.Interface
-	SharedInformerFactory     externalversions.SharedInformerFactory
-	CoreSharedInformerFactory informers.SharedInformerFactory
-	Stop                      <-chan struct{}
-}
-
-func (api6 *Api6Controller) ApisixTLS(controller *Controller) {
-	atc := BuildApisixTlsController(
-		api6.KubeClientSet,
-		api6.Api6ClientSet,
-		api6.SharedInformerFactory.Apisix().V1().ApisixTlses(),
-		controller)
-	if err := atc.Run(api6.Stop); err != nil {
-		log.Errorf("failed to run ApisixTlsController: %s", err)
+func (c *Controller) syncSSL(ctx context.Context, ssl *apisixv1.Ssl, event types.EventType) error {
+	var (
+		err error
+	)
+	if event == types.EventDelete {
+		err = c.apisix.Cluster("").SSL().Delete(ctx, ssl)
+	} else if event == types.EventUpdate {
+		_, err = c.apisix.Cluster("").SSL().Update(ctx, ssl)
+	} else {
+		_, err = c.apisix.Cluster("").SSL().Create(ctx, ssl)
 	}
+	return err
 }
diff --git a/pkg/ingress/controller/apisix_tls.go b/pkg/ingress/controller/apisix_tls.go
deleted file mode 100644
index 14a0131..0000000
--- a/pkg/ingress/controller/apisix_tls.go
+++ /dev/null
@@ -1,254 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package controller
-
-import (
-	"fmt"
-	"sync"
-	"time"
-
-	"k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/util/runtime"
-	"k8s.io/apimachinery/pkg/util/wait"
-	"k8s.io/client-go/kubernetes"
-	"k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/tools/cache"
-	"k8s.io/client-go/util/workqueue"
-
-	"github.com/apache/apisix-ingress-controller/pkg/ingress/apisix"
-	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
-	clientset "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned"
-	apisixscheme "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/clientset/versioned/scheme"
-	informersv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/informers/externalversions/config/v1"
-	listersv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v1"
-	"github.com/apache/apisix-ingress-controller/pkg/log"
-	"github.com/apache/apisix-ingress-controller/pkg/seven/state"
-	v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
-)
-
-var (
-	// the struct of secretSSLMap is a map[secretKey string]map[sslKey string]bool
-	// the xxxKey is format as namespace + "/" + name
-	secretSSLMap = sync.Map{}
-)
-
-type ApisixTLSController struct {
-	controller      *Controller
-	kubeclientset   kubernetes.Interface
-	apisixClientset clientset.Interface
-	apisixTLSList   listersv1.ApisixTlsLister
-	apisixTLSSynced cache.InformerSynced
-	workqueue       workqueue.RateLimitingInterface
-}
-
-type TlsQueueObj struct {
-	Key    string              `json:"key"`
-	OldObj *configv1.ApisixTls `json:"old_obj"`
-	Ope    string              `json:"ope"` // add / update / delete
-}
-
-func BuildApisixTlsController(
-	kubeclientset kubernetes.Interface,
-	apisixTLSClientset clientset.Interface,
-	apisixTLSInformer informersv1.ApisixTlsInformer,
-	root *Controller) *ApisixTLSController {
-
-	runtime.Must(apisixscheme.AddToScheme(scheme.Scheme))
-	controller := &ApisixTLSController{
-		controller:      root,
-		kubeclientset:   kubeclientset,
-		apisixClientset: apisixTLSClientset,
-		apisixTLSList:   apisixTLSInformer.Lister(),
-		apisixTLSSynced: apisixTLSInformer.Informer().HasSynced,
-		workqueue:       workqueue.NewNamedRateLimitingQueue(workqueue.NewItemFastSlowRateLimiter(1*time.Second, 60*time.Second, 5), "ApisixTlses"),
-	}
-	apisixTLSInformer.Informer().AddEventHandler(
-		cache.ResourceEventHandlerFuncs{
-			AddFunc:    controller.addFunc,
-			UpdateFunc: controller.updateFunc,
-			DeleteFunc: controller.deleteFunc,
-		})
-	return controller
-}
-
-func (c *ApisixTLSController) Run(stop <-chan struct{}) error {
-	if ok := cache.WaitForCacheSync(stop); !ok {
-		log.Errorf("sync ApisixTLS cache failed")
-		return fmt.Errorf("failed to wait for caches to sync")
-	}
-	go wait.Until(c.runWorker, time.Second, stop)
-	return nil
-}
-
-func (c *ApisixTLSController) runWorker() {
-	for c.processNextWorkItem() {
-	}
-}
-
-func (c *ApisixTLSController) processNextWorkItem() bool {
-	defer recoverException()
-	obj, shutdown := c.workqueue.Get()
-	if shutdown {
-		return false
-	}
-	err := func(obj interface{}) error {
-		defer c.workqueue.Done(obj)
-		var key string
-		var ok bool
-
-		var tqo *TlsQueueObj
-		if tqo, ok = obj.(*TlsQueueObj); !ok {
-			c.workqueue.Forget(obj)
-			return fmt.Errorf("expected TlsQueueObj in workqueue but got %#v", obj)
-		}
-		if err := c.syncHandler(tqo); err != nil {
-			c.workqueue.AddRateLimited(tqo)
-			log.Errorf("sync tls %s failed", tqo.Key)
-			return fmt.Errorf("error syncing '%s': %s", key, err.Error())
-		}
-
-		c.workqueue.Forget(obj)
-		return nil
-	}(obj)
-	if err != nil {
-		runtime.HandleError(err)
-	}
-	return true
-}
-
-func (c *ApisixTLSController) syncHandler(tqo *TlsQueueObj) error {
-	namespace, name, err := cache.SplitMetaNamespaceKey(tqo.Key)
-	if err != nil {
-		log.Errorf("invalid resource key: %s", tqo.Key)
-		return fmt.Errorf("invalid resource key: %s", tqo.Key)
-	}
-	apisixTlsYaml := tqo.OldObj
-	if tqo.Ope == state.Delete {
-		apisixIngressTls, _ := c.apisixTLSList.ApisixTlses(namespace).Get(name)
-		if apisixIngressTls != nil && apisixIngressTls.ResourceVersion > tqo.OldObj.ResourceVersion {
-			log.Warnf("TLS %s has been covered when retry", tqo.Key)
-			return nil
-		}
-	} else {
-		apisixTlsYaml, err = c.apisixTLSList.ApisixTlses(namespace).Get(name)
-		if err != nil {
-			if errors.IsNotFound(err) {
-				log.Infof("apisixTls %s is removed", tqo.Key)
-				return nil
-			}
-			runtime.HandleError(fmt.Errorf("failed to list apisixTls %s/%s", tqo.Key, err.Error()))
-			return err
-		}
-	}
-
-	apisixTls := apisix.ApisixTLSCRD(*apisixTlsYaml)
-	sc := &apisix.SecretClient{}
-	if tls, err := apisixTls.Convert(sc); err != nil {
-		return err
-	} else {
-		// sync to apisix
-		log.Debug(tls)
-		log.Debug(tqo)
-		err = state.SyncSsl(tls, tqo.Ope)
-		// sync SyncSecretSSL
-		secretKey := fmt.Sprintf("%s_%s", apisixTls.Spec.Secret.Namespace, apisixTls.Spec.Secret.Name)
-		SyncSecretSSL(secretKey, tls, tqo.Ope)
-		return err
-	}
-}
-
-// SyncSecretSSL sync the secretSSLMap
-// the struct of secretSSLMap is a map[secretKey string]map[sslKey string]bool
-// the xxxKey is format as namespace + "_" + name
-func SyncSecretSSL(key string, ssl *v1.Ssl, operator string) {
-	ssls, ok := secretSSLMap.Load(key)
-	if ok {
-		sslMap := ssls.(*sync.Map)
-		switch operator {
-		case state.Delete:
-			sslMap.Delete(ssl.ID)
-			secretSSLMap.Store(key, sslMap)
-		default:
-			sslMap.Store(ssl.ID, ssl)
-			secretSSLMap.Store(key, sslMap)
-		}
-	} else {
-		if operator != state.Delete {
-			sslMap := &sync.Map{}
-			sslMap.Store(ssl.ID, ssl)
-			secretSSLMap.Store(key, sslMap)
-		}
-	}
-
-}
-
-func (c *ApisixTLSController) addFunc(obj interface{}) {
-	var key string
-	var err error
-	if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
-		runtime.HandleError(err)
-		return
-	}
-	if !c.controller.namespaceWatching(key) {
-		return
-	}
-	rqo := &TlsQueueObj{Key: key, OldObj: nil, Ope: state.Create}
-	c.workqueue.AddRateLimited(rqo)
-}
-
-func (c *ApisixTLSController) updateFunc(oldObj, newObj interface{}) {
-	oldTls := oldObj.(*configv1.ApisixTls)
-	newTls := newObj.(*configv1.ApisixTls)
-	if oldTls.ResourceVersion == newTls.ResourceVersion {
-		return
-	}
-	var key string
-	var err error
-	if key, err = cache.MetaNamespaceKeyFunc(newObj); err != nil {
-		runtime.HandleError(err)
-		return
-	}
-	if !c.controller.namespaceWatching(key) {
-		return
-	}
-	rqo := &TlsQueueObj{Key: key, OldObj: oldTls, Ope: state.Update}
-	c.workqueue.AddRateLimited(rqo)
-}
-
-func (c *ApisixTLSController) deleteFunc(obj interface{}) {
-	oldTls, ok := obj.(*configv1.ApisixTls)
-	if !ok {
-		oldState, ok := obj.(cache.DeletedFinalStateUnknown)
-		if !ok {
-			return
-		}
-		oldTls, ok = oldState.Obj.(*configv1.ApisixTls)
-		if !ok {
-			return
-		}
-	}
-	var key string
-	var err error
-	key, err = cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
-	if err != nil {
-		runtime.HandleError(err)
-		return
-	}
-	if !c.controller.namespaceWatching(key) {
-		return
-	}
-	rqo := &TlsQueueObj{Key: key, OldObj: oldTls, Ope: state.Delete}
-	c.workqueue.AddRateLimited(rqo)
-}
diff --git a/pkg/ingress/controller/endpoint.go b/pkg/ingress/endpoint.go
similarity index 99%
rename from pkg/ingress/controller/endpoint.go
rename to pkg/ingress/endpoint.go
index ad22a36..80c6d06 100644
--- a/pkg/ingress/controller/endpoint.go
+++ b/pkg/ingress/endpoint.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"context"
diff --git a/pkg/ingress/controller/ingress.go b/pkg/ingress/ingress.go
similarity index 99%
rename from pkg/ingress/controller/ingress.go
rename to pkg/ingress/ingress.go
index 690e819..a9034d2 100644
--- a/pkg/ingress/controller/ingress.go
+++ b/pkg/ingress/ingress.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"context"
diff --git a/pkg/ingress/controller/ingress_test.go b/pkg/ingress/ingress_test.go
similarity index 99%
rename from pkg/ingress/controller/ingress_test.go
rename to pkg/ingress/ingress_test.go
index 1e17f4c..08396dc 100644
--- a/pkg/ingress/controller/ingress_test.go
+++ b/pkg/ingress/ingress_test.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"testing"
diff --git a/pkg/ingress/controller/manifest.go b/pkg/ingress/manifest.go
similarity index 99%
rename from pkg/ingress/controller/manifest.go
rename to pkg/ingress/manifest.go
index 709f986..25901d1 100644
--- a/pkg/ingress/controller/manifest.go
+++ b/pkg/ingress/manifest.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"context"
diff --git a/pkg/ingress/controller/manifest_test.go b/pkg/ingress/manifest_test.go
similarity index 99%
rename from pkg/ingress/controller/manifest_test.go
rename to pkg/ingress/manifest_test.go
index 811aefc..9d568e6 100644
--- a/pkg/ingress/controller/manifest_test.go
+++ b/pkg/ingress/manifest_test.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 import (
 	"testing"
diff --git a/pkg/ingress/controller/secret.go b/pkg/ingress/secret.go
similarity index 74%
rename from pkg/ingress/controller/secret.go
rename to pkg/ingress/secret.go
index 5268a0b..d831d33 100644
--- a/pkg/ingress/controller/secret.go
+++ b/pkg/ingress/secret.go
@@ -13,23 +13,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package controller
+package ingress
 
 import (
 	"context"
-	"fmt"
 	"sync"
 	"time"
 
 	"go.uber.org/zap"
 	corev1 "k8s.io/api/core/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/apimachinery/pkg/util/runtime"
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/util/workqueue"
 
+	"github.com/apache/apisix-ingress-controller/pkg/kube/translation"
 	"github.com/apache/apisix-ingress-controller/pkg/log"
-	"github.com/apache/apisix-ingress-controller/pkg/seven/state"
 	"github.com/apache/apisix-ingress-controller/pkg/types"
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
@@ -67,42 +65,26 @@ func (c *secretController) run(ctx context.Context) {
 		return
 	}
 
-	handler := func() {
-		for {
-			obj, shutdown := c.workqueue.Get()
-			if shutdown {
-				return
-			}
-			err := func(obj interface{}) error {
-				defer c.workqueue.Done(obj)
-				event := obj.(*types.Event)
-				if key, ok := event.Object.(string); !ok {
-					c.workqueue.Forget(obj)
-					return fmt.Errorf("expected Secret in workqueue but got %#v", obj)
-				} else {
-					if err := c.sync(ctx, event); err != nil {
-						c.workqueue.AddRateLimited(obj)
-						log.Errorf("sync secret with ssl %s failed", key)
-						return fmt.Errorf("error syncing '%s': %s", key, err.Error())
-					}
-					c.workqueue.Forget(obj)
-					return nil
-				}
-			}(obj)
-			if err != nil {
-				runtime.HandleError(err)
-			}
-		}
-	}
-
 	for i := 0; i < c.workers; i++ {
-		go handler()
+		go c.runWorker(ctx)
 	}
 
 	<-ctx.Done()
 	c.workqueue.ShutDown()
 }
 
+func (c *secretController) runWorker(ctx context.Context) {
+	for {
+		obj, quit := c.workqueue.Get()
+		if quit {
+			return
+		}
+		err := c.sync(ctx, obj.(*types.Event))
+		c.workqueue.Done(obj)
+		c.handleSyncErr(obj, err)
+	}
+}
+
 func (c *secretController) sync(ctx context.Context, ev *types.Event) error {
 	key := ev.Object.(string)
 	namespace, name, err := cache.SplitMetaNamespaceKey(key)
@@ -143,21 +125,55 @@ func (c *secretController) sync(ctx context.Context, ev *types.Event) error {
 	}
 	// sync SSL in APISIX which is store in secretSSLMap
 	// FixMe Need to update the status of CRD ApisixTls
-	ssls, ok := secretSSLMap.Load(secretMapkey)
-	if ok {
-		sslMap := ssls.(*sync.Map)
-		sslMap.Range(func(_, v interface{}) bool {
-			ssl := v.(*apisixv1.Ssl)
-			// sync ssl
-			ssl.Cert = string(sec.Data["cert"])
-			ssl.Key = string(sec.Data["key"])
-			ssl.FullName = ssl.ID
-			return state.SyncSsl(ssl, ev.Type.String()) == nil
-		})
+	ssls, ok := c.controller.secretSSLMap.Load(secretMapkey)
+	if !ok {
+		// This secret is not concerned.
+		return nil
+	}
+	cert, ok := sec.Data["cert"]
+	if !ok {
+		return translation.ErrEmptyCert
 	}
+	pkey, ok := sec.Data["key"]
+	if !ok {
+		return translation.ErrEmptyPrivKey
+	}
+	sslMap := ssls.(*sync.Map)
+	sslMap.Range(func(_, v interface{}) bool {
+		ssl := v.(*apisixv1.Ssl)
+		// sync ssl
+		ssl.Cert = string(cert)
+		ssl.Key = string(pkey)
+
+		// Use another goroutine to send requests, to avoid
+		// long time lock occupying.
+		go func(ssl *apisixv1.Ssl) {
+			err := c.controller.syncSSL(ctx, ssl, ev.Type)
+			if err != nil {
+				log.Errorw("failed to sync ssl to APISIX",
+					zap.Error(err),
+					zap.Any("ssl", ssl),
+					zap.Any("secret", sec),
+				)
+			}
+		}(ssl)
+		return true
+	})
 	return err
 }
 
+func (c *secretController) handleSyncErr(obj interface{}, err error) {
+	if err == nil {
+		c.workqueue.Forget(obj)
+		return
+	}
+	log.Warnw("sync ApisixTls failed, will retry",
+		zap.Any("object", obj),
+		zap.Error(err),
+	)
+	c.workqueue.AddRateLimited(obj)
+}
+
 func (c *secretController) onAdd(obj interface{}) {
 	key, err := cache.MetaNamespaceKeyFunc(obj)
 	if err != nil {
@@ -168,6 +184,9 @@ func (c *secretController) onAdd(obj interface{}) {
 		return
 	}
 
+	log.Debugw("secret add event arrived",
+		zap.Any("object", obj),
+	)
 	c.workqueue.AddRateLimited(&types.Event{
 		Type:   types.EventAdd,
 		Object: key,
@@ -189,6 +208,10 @@ func (c *secretController) onUpdate(prev, curr interface{}) {
 	if !c.controller.namespaceWatching(key) {
 		return
 	}
+	log.Debugw("secret update event arrived",
+		zap.Any("new object", curr),
+		zap.Any("old object", prev),
+	)
 	c.workqueue.AddRateLimited(&types.Event{
 		Type:   types.EventUpdate,
 		Object: key,
@@ -208,7 +231,7 @@ func (c *secretController) onDelete(obj interface{}) {
 
 	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
 	if err != nil {
-		log.Errorf("found secret resource with bad meta namesapce key: %s", err)
+		log.Errorf("found secret resource with bad meta namespace key: %s", err)
 		return
 	}
 	// FIXME Refactor Controller.namespaceWatching to just use
@@ -217,6 +240,9 @@ func (c *secretController) onDelete(obj interface{}) {
 	if !c.controller.namespaceWatching(key) {
 		return
 	}
+	log.Debugw("secret delete event arrived",
+		zap.Any("final state", sec),
+	)
 	c.workqueue.AddRateLimited(&types.Event{
 		Type:      types.EventDelete,
 		Object:    key,
diff --git a/pkg/ingress/controller/types.go b/pkg/ingress/types.go
similarity index 97%
rename from pkg/ingress/controller/types.go
rename to pkg/ingress/types.go
index d857ef6..640ba00 100644
--- a/pkg/ingress/controller/types.go
+++ b/pkg/ingress/types.go
@@ -12,7 +12,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package controller
+package ingress
 
 const (
 	ADD           = "ADD"
diff --git a/pkg/kube/translation/apisix_ssl.go b/pkg/kube/translation/apisix_ssl.go
new file mode 100644
index 0000000..9572b3b
--- /dev/null
+++ b/pkg/kube/translation/apisix_ssl.go
@@ -0,0 +1,58 @@
+// 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 translation
+
+import (
+	"errors"
+
+	"github.com/apache/apisix-ingress-controller/pkg/id"
+	configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
+	apisix "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+var (
+	// ErrEmptyCert means the cert field in Kubernetes Secret is not found.
+	ErrEmptyCert = errors.New("missing cert field")
+	// ErrEmptyPrivKey means the key field in Kubernetes Secret is not found.
+	ErrEmptyPrivKey = errors.New("missing key field")
+)
+
+func (t *translator) TranslateSSL(tls *configv1.ApisixTls) (*apisixv1.Ssl, error) {
+	s, err := t.SecretLister.Secrets(tls.Spec.Secret.Namespace).Get(tls.Spec.Secret.Name)
+	if err != nil {
+		return nil, err
+	}
+	cert, ok := s.Data["cert"]
+	if !ok {
+		return nil, ErrEmptyCert
+	}
+	key, ok := s.Data["key"]
+	if !ok {
+		return nil, ErrEmptyPrivKey
+	}
+	var snis []string
+	snis = append(snis, tls.Spec.Hosts...)
+	fullname := tls.Namespace + "_" + tls.Name
+	ssl := &apisix.Ssl{
+		ID:       id.GenID(fullname),
+		FullName: fullname,
+		Snis:     snis,
+		Cert:     string(cert),
+		Key:      string(key),
+		Status:   1,
+	}
+	return ssl, nil
+}
diff --git a/pkg/kube/translation/translator.go b/pkg/kube/translation/translator.go
index f7843b6..705bf35 100644
--- a/pkg/kube/translation/translator.go
+++ b/pkg/kube/translation/translator.go
@@ -64,6 +64,8 @@ type Translator interface {
 	// TranslateRouteV2alpha1 translates the configv2alph1.ApisixRoute object into several Route
 	// and Upstream resources.
 	TranslateRouteV2alpha1(*configv2alpha1.ApisixRoute) ([]*apisixv1.Route, []*apisixv1.Upstream, error)
+	// TranslateSSL translates the configv2alpha1.ApisixTls object into the APISIX SSL resource.
+	TranslateSSL(*configv1.ApisixTls) (*apisixv1.Ssl, error)
 }
 
 // TranslatorOptions contains options to help Translator
@@ -72,6 +74,7 @@ type TranslatorOptions struct {
 	EndpointsLister      listerscorev1.EndpointsLister
 	ServiceLister        listerscorev1.ServiceLister
 	ApisixUpstreamLister listersv1.ApisixUpstreamLister
+	SecretLister         listerscorev1.SecretLister
 }
 
 type translator struct {
diff --git a/pkg/seven/apisix/event.go b/pkg/seven/apisix/event.go
deleted file mode 100644
index 64bd3d9..0000000
--- a/pkg/seven/apisix/event.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package 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/conf/conf.go b/pkg/seven/conf/conf.go
deleted file mode 100644
index 6be1b8a..0000000
--- a/pkg/seven/conf/conf.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package conf
-
-import (
-	"github.com/apache/apisix-ingress-controller/pkg/apisix"
-	"github.com/apache/apisix-ingress-controller/pkg/log"
-)
-
-var (
-	BaseUrl  = "http://172.16.20.90:30116/apisix/admin"
-	UrlGroup = make(map[string]string)
-	Client   apisix.APISIX
-)
-
-func SetBaseUrl(url string) {
-	BaseUrl = url
-}
-
-func AddGroup(group string) {
-	if group != "" {
-		err := Client.AddCluster(&apisix.ClusterOptions{
-			Name:    group,
-			BaseURL: "http://" + group + "/apisix/admin",
-		})
-		if err != nil {
-			if err == apisix.ErrDuplicatedCluster {
-				log.Errorf("failed to create cluster %s: %s", group, err)
-			} else {
-				log.Infof("cluster %s already exists", group)
-			}
-		}
-	}
-}
-
-func SetAPISIXClient(c apisix.APISIX) {
-	Client = c
-}
diff --git a/pkg/seven/conf/conf_test.go b/pkg/seven/conf/conf_test.go
deleted file mode 100644
index 1540cca..0000000
--- a/pkg/seven/conf/conf_test.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package 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/state/event.go b/pkg/seven/state/event.go
deleted file mode 100644
index 8c354e0..0000000
--- a/pkg/seven/state/event.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package state
-
-const (
-	Create = "create"
-	Update = "update"
-	Delete = "delete"
-)
diff --git a/pkg/seven/state/solver.go b/pkg/seven/state/solver.go
deleted file mode 100644
index d17cdeb..0000000
--- a/pkg/seven/state/solver.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package state
-
-import (
-	"context"
-
-	"github.com/apache/apisix-ingress-controller/pkg/seven/conf"
-	v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
-)
-
-type CRDStatus struct {
-	Id     string `json:"id"`
-	Status string `json:"status"`
-	Err    error  `json:"err"`
-}
-
-func SyncSsl(ssl *v1.Ssl, method string) error {
-	var cluster string
-	if ssl.Group != "" {
-		cluster = ssl.Group
-	}
-	switch method {
-	case Create:
-		_, err := conf.Client.Cluster(cluster).SSL().Create(context.TODO(), ssl)
-		return err
-	case Update:
-		_, err := conf.Client.Cluster(cluster).SSL().Update(context.TODO(), ssl)
-		return err
-	case Delete:
-		return conf.Client.Cluster(cluster).SSL().Delete(context.TODO(), ssl)
-	}
-	return nil
-}
diff --git a/pkg/seven/utils/diff.go b/pkg/seven/utils/diff.go
deleted file mode 100644
index 5a48256..0000000
--- a/pkg/seven/utils/diff.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package utils
-
-import (
-	"encoding/json"
-
-	"github.com/golang/glog"
-	"github.com/yudai/gojsondiff"
-)
-
-var (
-	differ = gojsondiff.New()
-)
-
-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 {
-		glog.V(2).Info(d.Deltas())
-		return d.Modified(), nil
-	}
-}
-
-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
deleted file mode 100644
index 9b5a76a..0000000
--- a/pkg/seven/utils/http.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package 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
deleted file mode 100644
index 6709dc9..0000000
--- a/pkg/seven/utils/types.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package utils
-
-import "errors"
-
-// ErrNotFound unify Not Found error.
-var ErrNotFound = errors.New("NOT FOUND")
diff --git a/test/e2e/ingress/secret.go b/test/e2e/ingress/secret.go
index 9729cfb..a28e16b 100644
--- a/test/e2e/ingress/secret.go
+++ b/test/e2e/ingress/secret.go
@@ -143,7 +143,7 @@ do+oXlr8db++r87a8QQUkizzc6wXD9JffBNo9AO9Ed4HVOukpEA0gqVGBu85N3xW
 jW4KB95bGOTa7r7DM1Up0MbAIwWoeLBGhOIXk7inurZGg+FNjZMA5Lzm6qo=
 -----END RSA PRIVATE KEY-----`
 		// key compare
-		key_compare := "HrMHUvE9Esvn7GnZ+vAynaIg/8wlB3r0zm0htmnwofaOw61M98WSdvoWLaQa8YKSdemgQUz2W4MYk2rRZcVSzHfJOLRG7g4ieZau6peDYOmPmp/0ZZFpOzBKoWHN3QP/8i/7SF+JX+EDLD2JO2+GM6iR3f2Zj7v0vx+CcoQ1rjxaXNETSSHo8yvW6pdFZOLgJk4rOHKGypnqzygxxamM8Hq7WSPrWhNe47y1QAfz42kBQXRUJpNNd7W749cTsMWCqBlR+8klTlnSFHkjyijBZjg5ihqZsi/8JzHGrmAixZ54ugPgbufD0/ZJdo3w7opJc4WTnUI2GhiBL+ENCA0X1s/6H8JG8zsC50PvxOBpRgK455TTvejm1JHyt0GTh7c4WFEeQSrbEFzS89BpVrPtre2enO38pkILI8ty8r6tIbZzuOJhM6ZpxQQcAe8OUvFuIIlx21yBvlljbu3eH5Hg7X+wtJ [...]
+		keyCompare := "HrMHUvE9Esvn7GnZ+vAynaIg/8wlB3r0zm0htmnwofaOw61M98WSdvoWLaQa8YKSdemgQUz2W4MYk2rRZcVSzHfJOLRG7g4ieZau6peDYOmPmp/0ZZFpOzBKoWHN3QP/8i/7SF+JX+EDLD2JO2+GM6iR3f2Zj7v0vx+CcoQ1rjxaXNETSSHo8yvW6pdFZOLgJk4rOHKGypnqzygxxamM8Hq7WSPrWhNe47y1QAfz42kBQXRUJpNNd7W749cTsMWCqBlR+8klTlnSFHkjyijBZjg5ihqZsi/8JzHGrmAixZ54ugPgbufD0/ZJdo3w7opJc4WTnUI2GhiBL+ENCA0X1s/6H8JG8zsC50PvxOBpRgK455TTvejm1JHyt0GTh7c4WFEeQSrbEFzS89BpVrPtre2enO38pkILI8ty8r6tIbZzuOJhM6ZpxQQcAe8OUvFuIIlx21yBvlljbu3eH5Hg7X+wtJR [...]
 		// create secret
 		err := s.NewSecret(secretName, cert, key)
 		assert.Nil(ginkgo.GinkgoT(), err, "create secret error")
@@ -158,7 +158,7 @@ jW4KB95bGOTa7r7DM1Up0MbAIwWoeLBGhOIXk7inurZGg+FNjZMA5Lzm6qo=
 		assert.Nil(ginkgo.GinkgoT(), err, "list tls error")
 		assert.Len(ginkgo.GinkgoT(), tls, 1, "tls number not expect")
 		assert.Equal(ginkgo.GinkgoT(), cert, tls[0].Cert, "tls cert not expect")
-		assert.Equal(ginkgo.GinkgoT(), key_compare, tls[0].Key, "tls key not expect")
+		assert.Equal(ginkgo.GinkgoT(), keyCompare, tls[0].Key, "tls key not expect")
 
 		// check DP
 		s.NewAPISIXHttpsClient(host).GET("/ip").WithHeader("Host", host).Expect().Status(http.StatusOK).Body().Raw()